arcane-os 0.1.0-dev.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/COMMERCIAL-LICENSE.md +13 -0
  3. package/LICENSE +661 -0
  4. package/NOTICE +70 -0
  5. package/README.md +448 -0
  6. package/bin/arcane-test.mjs +741 -0
  7. package/bin/arcane.mjs +5 -0
  8. package/docs/architecture.md +234 -0
  9. package/docs/compatibility.md +36 -0
  10. package/docs/event-manager.md +166 -0
  11. package/docs/platform-targets.md +108 -0
  12. package/docs/publishing.md +203 -0
  13. package/docs/reference/README.md +117 -0
  14. package/docs/reference/arcane-ollama.md +288 -0
  15. package/docs/reference/availability-and-normalization.md +123 -0
  16. package/docs/reference/behavioral-testing.md +86 -0
  17. package/docs/reference/cli.md +569 -0
  18. package/docs/reference/core/README.md +62 -0
  19. package/docs/reference/core/arcane-ai-contracts.md +873 -0
  20. package/docs/reference/core/arcane-api.md +601 -0
  21. package/docs/reference/core/arcane-entities.md +65 -0
  22. package/docs/reference/core/arcane-events.md +134 -0
  23. package/docs/reference/core/ollama-module.md +181 -0
  24. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
  25. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
  26. package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
  27. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
  28. package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
  29. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
  30. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
  31. package/docs/reference/event-manager.md +957 -0
  32. package/docs/reference/inventory/package-api.json +2632 -0
  33. package/docs/reference/inventory/runtime-components.json +934 -0
  34. package/docs/reference/inventory/runtime-entities.json +26 -0
  35. package/docs/reference/inventory/runtime-modules.json +1249 -0
  36. package/docs/reference/protocols.md +242 -0
  37. package/docs/reference/runtime-components.md +1098 -0
  38. package/docs/reference/runtime-entities.md +303 -0
  39. package/docs/reference/runtime-modules.md +2010 -0
  40. package/docs/reference/sdk-api.md +4901 -0
  41. package/docs/roadmap.md +79 -0
  42. package/docs/work-amplification.md +124 -0
  43. package/node_modules/event-pubsub/CHANGELOG.md +55 -0
  44. package/node_modules/event-pubsub/MIGRATION.md +70 -0
  45. package/node_modules/event-pubsub/README.md +363 -0
  46. package/node_modules/event-pubsub/SECURITY.md +37 -0
  47. package/node_modules/event-pubsub/index.js +141 -0
  48. package/node_modules/event-pubsub/licence +21 -0
  49. package/node_modules/event-pubsub/package.json +59 -0
  50. package/node_modules/strong-type/README.md +408 -0
  51. package/node_modules/strong-type/assets/strong-type-header.png +0 -0
  52. package/node_modules/strong-type/index.js +1151 -0
  53. package/node_modules/strong-type/licence +21 -0
  54. package/node_modules/strong-type/node.js +125 -0
  55. package/node_modules/strong-type/package.json +61 -0
  56. package/package.json +95 -0
  57. package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
  58. package/runtime/arcane/components/app-bar.html +468 -0
  59. package/runtime/arcane/components/assistant-panel.html +715 -0
  60. package/runtime/arcane/components/calculator.html +8 -0
  61. package/runtime/arcane/components/chart.html +655 -0
  62. package/runtime/arcane/components/chat.html +1225 -0
  63. package/runtime/arcane/components/conversation-view.html +13 -0
  64. package/runtime/arcane/components/dashboard-config.html +341 -0
  65. package/runtime/arcane/components/data-maintenance.html +112 -0
  66. package/runtime/arcane/components/data-view.html +92 -0
  67. package/runtime/arcane/components/directory-picker.html +197 -0
  68. package/runtime/arcane/components/document-inspector.html +252 -0
  69. package/runtime/arcane/components/file-drop.html +264 -0
  70. package/runtime/arcane/components/file-inspector.html +293 -0
  71. package/runtime/arcane/components/file-manager.html +1715 -0
  72. package/runtime/arcane/components/header.html +142 -0
  73. package/runtime/arcane/components/integration-settings.html +14 -0
  74. package/runtime/arcane/components/local-ai-status.html +360 -0
  75. package/runtime/arcane/components/markdown-document.html +1048 -0
  76. package/runtime/arcane/components/markdown-editor.html +360 -0
  77. package/runtime/arcane/components/media-embed.html +8 -0
  78. package/runtime/arcane/components/modal.html +402 -0
  79. package/runtime/arcane/components/output-panel.html +259 -0
  80. package/runtime/arcane/components/preferences-form.html +135 -0
  81. package/runtime/arcane/components/record-timeline.html +105 -0
  82. package/runtime/arcane/components/relationship-board.html +116 -0
  83. package/runtime/arcane/components/screen-capture.html +8 -0
  84. package/runtime/arcane/components/source-code-viewer.html +441 -0
  85. package/runtime/arcane/components/source-explanation.html +124 -0
  86. package/runtime/arcane/components/speech.html +365 -0
  87. package/runtime/arcane/components/summary-strip.html +177 -0
  88. package/runtime/arcane/components/table.html +77 -0
  89. package/runtime/arcane/components/task-progress.html +282 -0
  90. package/runtime/arcane/components/terminal-workspace.html +65 -0
  91. package/runtime/arcane/components/theme-editor.html +41 -0
  92. package/runtime/arcane/components/theme-switcher.html +46 -0
  93. package/runtime/arcane/components/unified-inbox.html +20 -0
  94. package/runtime/arcane/components/voice-transcription.html +476 -0
  95. package/runtime/arcane/components/weather-widget.html +8 -0
  96. package/runtime/arcane/components/web-navigator.html +239 -0
  97. package/runtime/arcane/css/communications.css +1 -0
  98. package/runtime/arcane/css/dashboard-config.css +45 -0
  99. package/runtime/arcane/css/document-site.css +981 -0
  100. package/runtime/arcane/css/layout.css +438 -0
  101. package/runtime/arcane/css/primitives.css +321 -0
  102. package/runtime/arcane/css/theme.css +112 -0
  103. package/runtime/arcane/css/utility-workspace.css +1 -0
  104. package/runtime/arcane/entities/ApiModelRecord.js +20 -0
  105. package/runtime/arcane/entities/Calculation.js +13 -0
  106. package/runtime/arcane/entities/Chat.js +581 -0
  107. package/runtime/arcane/entities/CommunicationMessage.js +29 -0
  108. package/runtime/arcane/entities/CommunicationThread.js +21 -0
  109. package/runtime/arcane/entities/Document.js +10 -0
  110. package/runtime/arcane/entities/File.js +143 -0
  111. package/runtime/arcane/entities/Image.js +135 -0
  112. package/runtime/arcane/entities/IntentEnvelope.js +834 -0
  113. package/runtime/arcane/entities/Preference.js +83 -0
  114. package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
  115. package/runtime/arcane/entities/TerminalSession.js +46 -0
  116. package/runtime/arcane/entities/Theme.js +107 -0
  117. package/runtime/arcane/entities/User.js +1046 -0
  118. package/runtime/arcane/entities/Weather.js +23 -0
  119. package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
  120. package/runtime/arcane/img/arrow-left.png +0 -0
  121. package/runtime/arcane/img/arrow-right.png +0 -0
  122. package/runtime/arcane/img/doc.svg +5 -0
  123. package/runtime/arcane/img/folder.svg +4 -0
  124. package/runtime/arcane/img/image.svg +5 -0
  125. package/runtime/arcane/img/refresh.png +0 -0
  126. package/runtime/arcane/img/send.svg +9 -0
  127. package/runtime/arcane/img/trash.svg +5 -0
  128. package/runtime/arcane/img/upload.svg +5 -0
  129. package/runtime/arcane/modules/AI.js +2048 -0
  130. package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
  131. package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
  132. package/runtime/arcane/modules/AIResponseLength.js +42 -0
  133. package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
  134. package/runtime/arcane/modules/AnsiText.js +53 -0
  135. package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
  136. package/runtime/arcane/modules/AppDataScope.js +245 -0
  137. package/runtime/arcane/modules/AppearancePreferences.js +28 -0
  138. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
  139. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
  140. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
  141. package/runtime/arcane/modules/AsyncBoundary.js +161 -0
  142. package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
  143. package/runtime/arcane/modules/CalculatorEngine.js +20 -0
  144. package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
  145. package/runtime/arcane/modules/ChartLibrary.js +35 -0
  146. package/runtime/arcane/modules/ChatRecords.js +13 -0
  147. package/runtime/arcane/modules/CommunicationAppController.js +43 -0
  148. package/runtime/arcane/modules/CommunicationHub.js +16 -0
  149. package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
  150. package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
  151. package/runtime/arcane/modules/ComponentContracts.js +586 -0
  152. package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
  153. package/runtime/arcane/modules/ConversationActionItems.js +488 -0
  154. package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
  155. package/runtime/arcane/modules/ConversationTimebox.js +527 -0
  156. package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
  157. package/runtime/arcane/modules/DBLS.js +171 -0
  158. package/runtime/arcane/modules/DBOPFS.js +1154 -0
  159. package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
  160. package/runtime/arcane/modules/DataMaintenance.js +91 -0
  161. package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
  162. package/runtime/arcane/modules/DirectoryPicker.js +109 -0
  163. package/runtime/arcane/modules/DocumentNavigation.js +223 -0
  164. package/runtime/arcane/modules/Errors.js +1025 -0
  165. package/runtime/arcane/modules/GifEncoder.js +29 -0
  166. package/runtime/arcane/modules/HTMLImport.js +114 -0
  167. package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
  168. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
  169. package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
  170. package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
  171. package/runtime/arcane/modules/MD.js +111 -0
  172. package/runtime/arcane/modules/Mail.js +352 -0
  173. package/runtime/arcane/modules/MailTransport.mjs +180 -0
  174. package/runtime/arcane/modules/Marked.min.js +71 -0
  175. package/runtime/arcane/modules/MemoryRecords.js +44 -0
  176. package/runtime/arcane/modules/MessageAdvisory.js +38 -0
  177. package/runtime/arcane/modules/ModelDefinition.js +189 -0
  178. package/runtime/arcane/modules/Ollama.js +74 -0
  179. package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
  180. package/runtime/arcane/modules/OllamaSettings.js +24 -0
  181. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
  182. package/runtime/arcane/modules/PreferenceStore.js +109 -0
  183. package/runtime/arcane/modules/QRCode.min.js +1 -0
  184. package/runtime/arcane/modules/Questionnaire.js +61 -0
  185. package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
  186. package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
  187. package/runtime/arcane/modules/RecordReviewStore.js +94 -0
  188. package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
  189. package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
  190. package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
  191. package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
  192. package/runtime/arcane/modules/ScreenCapture.js +20 -0
  193. package/runtime/arcane/modules/SpeechPlayback.js +581 -0
  194. package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
  195. package/runtime/arcane/modules/SystemAppearance.js +20 -0
  196. package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
  197. package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
  198. package/runtime/arcane/modules/TerminalClient.js +52 -0
  199. package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
  200. package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
  201. package/runtime/arcane/modules/ThemeManager.js +131 -0
  202. package/runtime/arcane/modules/TimeGuard.js +149 -0
  203. package/runtime/arcane/modules/ToolCallRouter.js +83 -0
  204. package/runtime/arcane/modules/WaitForComponent.js +102 -0
  205. package/runtime/arcane/modules/YouTubeMedia.js +16 -0
  206. package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
  207. package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
  208. package/runtime/arcane/modules/uPlot.min.css +1 -0
  209. package/runtime/arcane/security/arcane-network-policy.json +6 -0
  210. package/runtime/strong-type/index.js +352 -0
  211. package/runtime/strong-type/licence +21 -0
  212. package/runtime/strong-type/package.json +45 -0
  213. package/schemas/arcane-app-bundle.schema.json +125 -0
  214. package/schemas/arcane-app.schema.json +329 -0
  215. package/schemas/arcane-lock.schema.json +86 -0
  216. package/schemas/arcane-package.schema.json +224 -0
  217. package/schemas/cli-event.schema.json +122 -0
  218. package/schemas/event-stack.schema.json +152 -0
  219. package/schemas/native-build-plan.schema.json +176 -0
  220. package/schemas/target-adapter.schema.json +117 -0
  221. package/src/app-descriptor.mjs +500 -0
  222. package/src/cli/main.mjs +561 -0
  223. package/src/constants.mjs +31 -0
  224. package/src/dev-server.mjs +718 -0
  225. package/src/doctor.mjs +315 -0
  226. package/src/dom-event-instrumentation.mjs +594 -0
  227. package/src/errors.mjs +75 -0
  228. package/src/event-manager.mjs +1342 -0
  229. package/src/event-queue.mjs +138 -0
  230. package/src/events.mjs +219 -0
  231. package/src/index.mjs +177 -0
  232. package/src/integrated-provider-loader.mjs +432 -0
  233. package/src/native-plan.mjs +698 -0
  234. package/src/native-provider-loader.mjs +1126 -0
  235. package/src/packager/core.mjs +2691 -0
  236. package/src/process.mjs +353 -0
  237. package/src/release-bundle.mjs +2523 -0
  238. package/src/repository.mjs +90 -0
  239. package/src/runtime.mjs +452 -0
  240. package/src/scaffold.mjs +380 -0
  241. package/src/targets/index.mjs +436 -0
  242. package/src/templates/assets/app-icon.png +0 -0
  243. package/src/templates/workspace-template.mjs +388 -0
  244. package/src/testing-loader.mjs +9 -0
  245. package/src/testing.mjs +427 -0
  246. package/src/toolchain.mjs +1335 -0
  247. package/src/update-check.mjs +307 -0
  248. package/src/workspace.mjs +449 -0
@@ -0,0 +1,245 @@
1
+ const APPLICATION_ID_PATTERN=/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
2
+ const APPLICATION_ID_MAX_LENGTH=64;
3
+ const APP_DATA_DIRECTORY='apps';
4
+ const APP_LOCAL_STORAGE_PREFIX='arcane.apps.';
5
+
6
+ function scopeError(code,message){
7
+ const error=new Error(message);
8
+ error.code=code;
9
+ return error;
10
+ }
11
+
12
+ /**
13
+ * Validates the canonical application identifier used by Arcane packages,
14
+ * the native host, and application-owned data folders.
15
+ *
16
+ * @param {*} value
17
+ * @param {string} label
18
+ * @returns {string}
19
+ */
20
+ export function canonicalApplicationId(value,label='applicationId'){
21
+ if(typeof value!=='string'
22
+ ||value.length<1
23
+ ||value.length>APPLICATION_ID_MAX_LENGTH
24
+ ||!APPLICATION_ID_PATTERN.test(value)){
25
+ throw scopeError(
26
+ 'APP_DATA_SCOPE_INVALID',
27
+ `${label} must be a canonical Arcane application identifier.`
28
+ );
29
+ }
30
+
31
+ return value;
32
+ }
33
+
34
+ /**
35
+ * Reads the immutable application declaration from a document. Application
36
+ * pages declare this before storage modules run so standalone browser builds
37
+ * do not depend on their mount path for identity.
38
+ *
39
+ * @param {Document|Object|null} documentObject
40
+ * @returns {string|null}
41
+ */
42
+ export function declaredApplicationId(documentObject=globalThis.document){
43
+ const metaValue=documentObject
44
+ ?.querySelector?.('meta[name="arcane-app-id"]')
45
+ ?.getAttribute?.('content');
46
+ const rootValue=documentObject?.documentElement?.dataset?.arcaneAppId;
47
+ const declarations=[metaValue,rootValue]
48
+ .filter(value=>value!==null&&value!==undefined&&value!=='')
49
+ .map((value,index)=>canonicalApplicationId(
50
+ value,
51
+ index===0?'arcane-app-id metadata':'document application id'
52
+ ));
53
+
54
+ if(new Set(declarations).size>1){
55
+ throw scopeError(
56
+ 'APP_DATA_SCOPE_MISMATCH',
57
+ 'The document contains conflicting Arcane application identities.'
58
+ );
59
+ }
60
+
61
+ return declarations[0]||null;
62
+ }
63
+
64
+ /**
65
+ * Resolves the synchronous browser declaration used by storage mechanisms
66
+ * such as localStorage. Native-capable asynchronous callers must use
67
+ * resolveApplicationId so the host-bound identity remains authoritative.
68
+ *
69
+ * @param {Object} options
70
+ * @param {string|null} options.applicationId
71
+ * @param {Document|Object|null} options.documentObject
72
+ * @returns {string}
73
+ */
74
+ export function resolveBrowserApplicationId({
75
+ applicationId=null,
76
+ documentObject=globalThis.document
77
+ }={}){
78
+ const explicit=applicationId===null||applicationId===undefined||applicationId===''
79
+ ?null
80
+ :canonicalApplicationId(applicationId);
81
+ const declared=declaredApplicationId(documentObject);
82
+
83
+ if(explicit&&declared&&explicit!==declared){
84
+ throw scopeError(
85
+ 'APP_DATA_SCOPE_MISMATCH',
86
+ 'The configured and declared Arcane application identities do not match.'
87
+ );
88
+ }
89
+
90
+ if(explicit||declared){
91
+ return explicit||declared;
92
+ }
93
+
94
+ throw scopeError(
95
+ 'APP_DATA_SCOPE_REQUIRED',
96
+ 'Arcane application data cannot open without a declared application identity.'
97
+ );
98
+ }
99
+
100
+ /**
101
+ * Resolves a logical local-storage key beneath the current browser app scope.
102
+ * Callers retain their domain namespace while the shared mechanism supplies
103
+ * the canonical `arcane.apps.<id>:` ownership prefix.
104
+ *
105
+ * @param {*} logicalKey
106
+ * @param {Object} options
107
+ * @returns {string}
108
+ */
109
+ export function resolveApplicationLocalStorageKey(logicalKey='',options={}){
110
+ const applicationId=resolveBrowserApplicationId(options);
111
+ return `${APP_LOCAL_STORAGE_PREFIX}${applicationId}:${String(logicalKey)}`;
112
+ }
113
+
114
+ async function nativeApplicationId(arcane=globalThis.Arcane){
115
+ if(typeof arcane?.app?.current!=='function'){
116
+ return null;
117
+ }
118
+
119
+ const descriptor=await arcane.app.current();
120
+
121
+ if(!descriptor||typeof descriptor!=='object'){
122
+ throw scopeError(
123
+ 'APP_DATA_SCOPE_INVALID',
124
+ 'Arcane returned an invalid bound application descriptor.'
125
+ );
126
+ }
127
+
128
+ return canonicalApplicationId(
129
+ descriptor.id,
130
+ 'bound application id'
131
+ );
132
+ }
133
+
134
+ /**
135
+ * Resolves one application identity for persistent data. The native host is
136
+ * authoritative when present. Browser-only applications must declare their
137
+ * package id in `meta[name="arcane-app-id"]` (or the equivalent root data
138
+ * attribute). A mismatch fails closed before a data directory is opened.
139
+ *
140
+ * @param {Object} options
141
+ * @param {string|null} options.applicationId Explicit identity for adapters
142
+ * and synthetic tests. Application pages should use document metadata.
143
+ * @param {Document|Object|null} options.documentObject
144
+ * @param {Object|null} options.arcane
145
+ * @returns {Promise<string>}
146
+ */
147
+ export async function resolveApplicationId({
148
+ applicationId=null,
149
+ documentObject=globalThis.document,
150
+ arcane=globalThis.Arcane
151
+ }={}){
152
+ let browserIdentity=null;
153
+ try{
154
+ browserIdentity=resolveBrowserApplicationId({
155
+ applicationId,
156
+ documentObject
157
+ });
158
+ }catch(error){
159
+ if(error?.code!=='APP_DATA_SCOPE_REQUIRED'){
160
+ throw error;
161
+ }
162
+ }
163
+ const nativeIdentity=await nativeApplicationId(arcane);
164
+
165
+ if(nativeIdentity&&browserIdentity&&nativeIdentity!==browserIdentity){
166
+ throw scopeError(
167
+ 'APP_DATA_SCOPE_MISMATCH',
168
+ 'The document identity does not match the application bound by Arcane.'
169
+ );
170
+ }
171
+
172
+ if(nativeIdentity||browserIdentity){
173
+ return nativeIdentity||browserIdentity;
174
+ }
175
+
176
+ throw scopeError(
177
+ 'APP_DATA_SCOPE_REQUIRED',
178
+ 'Arcane application data cannot open without a bound application identity.'
179
+ );
180
+ }
181
+
182
+ /**
183
+ * Opens `<OPFS root>/apps/<application-id>` and returns only that app-owned
184
+ * directory. This is an organizational boundary for browser-only same-origin
185
+ * code; the native host additionally supplies a separate WebView profile and
186
+ * authoritative application identity.
187
+ *
188
+ * @param {Object} options
189
+ * @param {StorageManager|Object} options.storage
190
+ * @param {string|null} options.applicationId
191
+ * @param {Document|Object|null} options.documentObject
192
+ * @param {Object|null} options.arcane
193
+ * @param {boolean} options.create
194
+ * @returns {Promise<{applicationId:string,directory:FileSystemDirectoryHandle|Object,path:string}>}
195
+ */
196
+ export async function openApplicationDataDirectory({
197
+ storage=globalThis.navigator?.storage,
198
+ applicationId=null,
199
+ documentObject=globalThis.document,
200
+ arcane=globalThis.Arcane,
201
+ create=true
202
+ }={}){
203
+ if(!storage||typeof storage.getDirectory!=='function'){
204
+ throw scopeError(
205
+ 'APP_DATA_STORAGE_UNAVAILABLE',
206
+ 'Origin Private File System storage is unavailable in this browser.'
207
+ );
208
+ }
209
+
210
+ const id=await resolveApplicationId({
211
+ applicationId,
212
+ documentObject,
213
+ arcane
214
+ });
215
+ const root=await storage.getDirectory();
216
+
217
+ if(!root||typeof root.getDirectoryHandle!=='function'){
218
+ throw scopeError(
219
+ 'APP_DATA_STORAGE_UNAVAILABLE',
220
+ 'Arcane could not open the Origin Private File System root.'
221
+ );
222
+ }
223
+
224
+ const applications=await root.getDirectoryHandle(
225
+ APP_DATA_DIRECTORY,
226
+ {create:Boolean(create)}
227
+ );
228
+ const directory=await applications.getDirectoryHandle(
229
+ id,
230
+ {create:Boolean(create)}
231
+ );
232
+
233
+ return {
234
+ applicationId:id,
235
+ directory,
236
+ path:`${APP_DATA_DIRECTORY}/${id}`
237
+ };
238
+ }
239
+
240
+ export {
241
+ APPLICATION_ID_MAX_LENGTH,
242
+ APPLICATION_ID_PATTERN,
243
+ APP_DATA_DIRECTORY,
244
+ APP_LOCAL_STORAGE_PREFIX
245
+ };
@@ -0,0 +1,28 @@
1
+ import PreferenceStore from './PreferenceStore.js';
2
+
3
+ export const appearancePreferenceSchema=Object.freeze([
4
+ {key:'appearance.colorScheme',type:'select',label:'Color scheme',description:'Use the device preference or choose a consistent light or dark appearance.',defaultValue:'system',options:[{label:'Use device setting',value:'system'},{label:'Light',value:'light'},{label:'Dark',value:'dark'}]},
5
+ {key:'appearance.density',type:'select',label:'Layout density',description:'Choose comfortable spacing or fit more information on screen.',defaultValue:'comfortable',options:[{label:'Comfortable',value:'comfortable'},{label:'Compact',value:'compact'}]},
6
+ {key:'accessibility.reduceMotion',type:'boolean',label:'Reduce motion',description:'Limit non-essential animation and transitions.',defaultValue:false},
7
+ {key:'accessibility.largeText',type:'boolean',label:'Larger text',description:'Increase the base text size in compatible Arcane applications.',defaultValue:false}
8
+ ]);
9
+
10
+ export function createAppearancePreferenceStore(options={}){
11
+ return new PreferenceStore({namespace:'arcane',schema:appearancePreferenceSchema,...options});
12
+ }
13
+
14
+ export function applyAppearancePreferences(values={},root=document.documentElement){
15
+ const scheme=values['appearance.colorScheme'];
16
+ if(scheme==='light'||scheme==='dark') root.dataset.colorScheme=scheme; else root.removeAttribute('data-color-scheme');
17
+ root.dataset.density=values['appearance.density']||'comfortable';
18
+ root.dataset.reduceMotion=String(Boolean(values['accessibility.reduceMotion']));
19
+ root.style.fontSize=values['accessibility.largeText']?'18px':'';
20
+ return values;
21
+ }
22
+
23
+ export async function loadAndApplyAppearancePreferences(options={}){
24
+ const store=options.store||createAppearancePreferenceStore(options);
25
+ const values=await store.load();
26
+ applyAppearancePreferences(values,options.root||document.documentElement);
27
+ return {store,values};
28
+ }
@@ -0,0 +1,22 @@
1
+ import CommunicationMessage from '../entities/CommunicationMessage.js';
2
+ import CommunicationThread from '../entities/CommunicationThread.js';
3
+
4
+ function cleanEndpoint(value){const url=new URL(String(value||'http://127.0.0.1:8020'));if(!['http:','https:'].includes(url.protocol)) throw new TypeError('Bridge URL must use HTTP or HTTPS.');return url.href.replace(/\/$/,'');}
5
+
6
+ export default class ArcaneCommunicationBridge{
7
+ constructor({id,label,channels,endpoint='http://127.0.0.1:8020',fetchImpl=globalThis.fetch}={}){
8
+ this.id=String(id||'arcane-bridge');this.label=String(label||'Arcane communications bridge');this.channels=Array.from(channels||['other']);this.endpoint=cleanEndpoint(endpoint);this.fetchImpl=fetchImpl;
9
+ }
10
+ async request(path,options={}){
11
+ if(typeof this.fetchImpl!=='function') throw new Error('Network access is unavailable.');
12
+ const response=await this.fetchImpl(`${this.endpoint}${path}`,{...options,headers:{Accept:'application/json',...(options.body?{'Content-Type':'application/json'}:{}),...options.headers}});
13
+ const text=await response.text();let body={};if(text){try{body=JSON.parse(text);}catch{throw new Error('The communications bridge returned invalid JSON.');}}
14
+ if(!response.ok) throw new Error(body.error||`Communications bridge request failed (${response.status}).`);
15
+ return body;
16
+ }
17
+ async listThreads(){const body=await this.request(`/v1/providers/${encodeURIComponent(this.id)}/threads`);return Array.from(body.threads||[],value=>new CommunicationThread({...value,providerId:this.id}));}
18
+ async getMessages(threadId){const body=await this.request(`/v1/providers/${encodeURIComponent(this.id)}/threads/${encodeURIComponent(threadId)}/messages`);return Array.from(body.messages||[],value=>new CommunicationMessage({...value,threadId,providerId:this.id}));}
19
+ async send(input={}){const body=await this.request(`/v1/providers/${encodeURIComponent(this.id)}/messages`,{method:'POST',body:JSON.stringify(input)});return new CommunicationMessage({...body.message,providerId:this.id,threadId:body.message?.threadId||input.threadId,direction:'outbound'});}
20
+ async connect(){return this.request(`/v1/providers/${encodeURIComponent(this.id)}/connect`,{method:'POST'});}
21
+ async disconnect(){return this.request(`/v1/providers/${encodeURIComponent(this.id)}/disconnect`,{method:'POST'});}
22
+ }
@@ -0,0 +1,135 @@
1
+ import * as ArcaneNetworkPolicy from './ArcaneNetworkPolicy.js?v=3';
2
+
3
+ function boundedText(value,fallback,maximum=500){
4
+ const text=typeof value==='string'?value.trim():'';
5
+ if(!text)return fallback;
6
+ return text.replace(/[\u0000-\u001f\u007f]/g,' ').slice(0,maximum);
7
+ }
8
+
9
+ function decisionId(){
10
+ if(typeof globalThis.crypto?.randomUUID==='function')return globalThis.crypto.randomUUID();
11
+ return `navigation-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`;
12
+ }
13
+
14
+ function destinationContext(value){
15
+ const url=new URL(String(value));
16
+ if(!['http:','https:'].includes(url.protocol))throw new TypeError('Arcane navigation policy accepts only HTTP or HTTPS destinations.');
17
+ const defaultPort=url.protocol==='https:'?443:80;
18
+ const hostname=url.hostname;
19
+ const ipLiteral=isIpLiteralHostname(hostname);
20
+ return Object.freeze({
21
+ url:url.href,
22
+ hostname,
23
+ canonicalHostname:ipLiteral?hostname.replace(/^\[|\]$/g,''):ArcaneNetworkPolicy.canonicalNetworkHostname(hostname),
24
+ ipLiteral,
25
+ protocol:'tcp',
26
+ remotePort:url.port?Number(url.port):defaultPort
27
+ });
28
+ }
29
+
30
+ function isIpLiteralHostname(hostname){
31
+ const value=String(hostname||'').replace(/^\[|\]$/g,'');
32
+ if(value.includes(':'))return /^[0-9a-f:.]+$/i.test(value);
33
+ const parts=value.split('.');
34
+ return parts.length===4&&parts.every(function validIpv4Part(part){
35
+ return /^(?:0|[1-9]\d{0,2})$/.test(part)&&Number(part)<=255;
36
+ });
37
+ }
38
+
39
+ function blockedDecision(rule,target,policyGeneration,ruleType){
40
+ return Object.freeze({
41
+ blocked:true,
42
+ decisionId:decisionId(),
43
+ decidedAt:new Date().toISOString(),
44
+ policyGeneration,
45
+ ruleId:boundedText(rule?.id,'unknown-rule',80),
46
+ ruleType,
47
+ target:target.url,
48
+ matchedValue:ruleType==='domain'?boundedText(rule?.domain,target.hostname,253):boundedText(rule?.cidr,target.hostname,160),
49
+ reason:Object.freeze({
50
+ code:boundedText(rule?.reason?.code,'global-deny',80),
51
+ title:boundedText(rule?.reason?.title,'Blocked by the global deny policy',120),
52
+ description:boundedText(rule?.reason?.description,'Arcane stopped this navigation before sending a request.')
53
+ }),
54
+ source:Object.freeze({
55
+ id:boundedText(rule?.source?.id,'arcane-policy',80),
56
+ label:boundedText(rule?.source?.label,'Arcane global policy',120),
57
+ reference:boundedText(rule?.source?.reference,'',500)||null
58
+ })
59
+ });
60
+ }
61
+
62
+ function unavailableDecision(target,error){
63
+ return Object.freeze({
64
+ blocked:true,
65
+ decisionId:decisionId(),
66
+ decidedAt:new Date().toISOString(),
67
+ policyGeneration:null,
68
+ ruleId:'policy-unavailable',
69
+ ruleType:'policy',
70
+ target:target.url,
71
+ matchedValue:target.hostname,
72
+ reason:Object.freeze({
73
+ code:'policy-unavailable',
74
+ title:'Navigation paused because policy is unavailable',
75
+ description:'Arcane could not verify the current global deny policy, so it did not send this request.'
76
+ }),
77
+ source:Object.freeze({
78
+ id:'arcane-runtime',
79
+ label:'Arcane policy safety boundary',
80
+ reference:boundedText(error?.code||'','',120)||null
81
+ })
82
+ });
83
+ }
84
+
85
+ function requireNetworkMatcher(networkMatcher){
86
+ if(typeof networkMatcher==='function')return networkMatcher;
87
+ const error=new Error('Arcane cannot evaluate literal-IP navigation because the canonical network matcher is unavailable.');
88
+ error.code='ARCANE_NETWORK_POLICY_MATCHER_UNAVAILABLE';
89
+ throw error;
90
+ }
91
+
92
+ export function createArcaneNavigationGuard({
93
+ loadPolicy=ArcaneNetworkPolicy.loadArcaneNetworkPolicy,
94
+ onDecision=null,
95
+ networkMatcher=ArcaneNetworkPolicy.findDeniedNetworkRule
96
+ }={}){
97
+ return async function guardArcaneNavigation(value,context={}){
98
+ const target=destinationContext(value);
99
+ let decision;
100
+ try{
101
+ if(!target.ipLiteral&&(!target.canonicalHostname||target.canonicalHostname!==target.hostname)){
102
+ const error=new TypeError('Arcane navigation policy requires a canonical hostname without a trailing root dot.');
103
+ error.code='ARCANE_NETWORK_POLICY_HOSTNAME_NONCANONICAL';
104
+ throw error;
105
+ }
106
+ const policy=await loadPolicy();
107
+ const domainRule=ArcaneNetworkPolicy.findDeniedDomainRule(policy,target.canonicalHostname);
108
+ let networkRule=null;
109
+ if(!domainRule&&target.ipLiteral){
110
+ const findDeniedNetworkRule=requireNetworkMatcher(networkMatcher);
111
+ networkRule=findDeniedNetworkRule(policy,target.canonicalHostname,{
112
+ protocol:target.protocol,
113
+ remotePort:target.remotePort
114
+ });
115
+ }
116
+ decision=domainRule
117
+ ?blockedDecision(domainRule,target,policy.generation,'domain')
118
+ :networkRule
119
+ ?blockedDecision(networkRule,target,policy.generation,'network')
120
+ :Object.freeze({
121
+ blocked:false,
122
+ decisionId:decisionId(),
123
+ decidedAt:new Date().toISOString(),
124
+ policyGeneration:policy.generation,
125
+ target:target.url
126
+ });
127
+ }catch(error){
128
+ decision=unavailableDecision(target,error);
129
+ }
130
+ if(typeof onDecision==='function'){
131
+ await onDecision(decision,Object.freeze({intent:boundedText(context.intent,'embedded',32)}));
132
+ }
133
+ return decision;
134
+ };
135
+ }