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,20 @@
1
+ export default class SystemAppearance{
2
+ constructor(api=globalThis.Arcane?.appearance||null){ this.api=api; }
3
+
4
+ available(){ return Boolean(this.api&&typeof this.api.apply==='function'); }
5
+
6
+ async current(){
7
+ if(!this.api||typeof this.api.current!=='function') return {supported:false,platform:'browser'};
8
+ return this.api.current();
9
+ }
10
+
11
+ async apply(input={}){
12
+ if(!this.available()) return {supported:false,platform:'browser'};
13
+ const scheme=['system','light','dark'].includes(input.scheme)?input.scheme:'system';
14
+ return this.api.apply({
15
+ scheme,
16
+ captionColor:scheme==='system'?null:input.captionColor||null,
17
+ textColor:scheme==='system'?null:input.textColor||null
18
+ });
19
+ }
20
+ }
@@ -0,0 +1,51 @@
1
+ (function installSystemPlatformPresentation(globalObject) {
2
+ 'use strict';
3
+
4
+ const KERNEL_TYPES = Object.freeze(['nt', 'linux']);
5
+
6
+ function normalizedValue(value) {
7
+ return typeof value === 'string' ? value.trim().toLowerCase() : '';
8
+ }
9
+
10
+ function kernelType(status = {}) {
11
+ const candidates = [
12
+ status.kernelType,
13
+ status.platform,
14
+ status.rawPlatform,
15
+ status.execution?.effectivePlatform,
16
+ status.execution?.hostPlatform,
17
+ ].map(normalizedValue);
18
+
19
+ for (const value of candidates) {
20
+ if (value === 'windows' || value === 'win32' || value === 'nt') return 'nt';
21
+ if (value === 'linux') return 'linux';
22
+ }
23
+ return null;
24
+ }
25
+
26
+ function displayName(status = {}) {
27
+ const kernel = kernelType(status);
28
+ if (kernel === 'nt') return 'Microsoft NT';
29
+ if (kernel === 'linux') return 'Linux';
30
+ if (typeof status.displayName === 'string' && status.displayName.trim()) return status.displayName.trim();
31
+ if (typeof status.platform === 'string' && status.platform.trim()) return status.platform.trim();
32
+ return 'Unknown operating system';
33
+ }
34
+
35
+ function apply(status = {}, root = globalObject.document?.documentElement) {
36
+ const kernel = kernelType(status);
37
+ if (root?.classList) {
38
+ root.classList.remove('arcane-kernel', ...KERNEL_TYPES.map((type) => `arcane-kernel-${type}`));
39
+ if (kernel) root.classList.add('arcane-kernel', `arcane-kernel-${kernel}`);
40
+ }
41
+ if (root?.dataset) {
42
+ if (kernel) root.dataset.arcaneKernel = kernel;
43
+ else delete root.dataset.arcaneKernel;
44
+ }
45
+
46
+ // This marker is presentation metadata only. Native permission evidence remains authoritative.
47
+ return Object.freeze({ kernelType: kernel, displayName: displayName(status) });
48
+ }
49
+
50
+ globalObject.ArcaneSystemPlatformPresentation = Object.freeze({ kernelType, displayName, apply });
51
+ })(globalThis);
@@ -0,0 +1,28 @@
1
+ const TOOL_PATTERN=/^[a-z][a-z0-9-]{0,63}$/;
2
+
3
+ function quoteArgument(value){
4
+ const text=String(value??'');
5
+ if(!text||/[\s"';&|<>$`]/.test(text)) return `"${text.replace(/"/g,'\\"')}"`;
6
+ return text;
7
+ }
8
+
9
+ export default class SystemToolRegistry{
10
+ constructor(definitions=[]){this.tools=new Map();for(const definition of definitions)this.register(definition);}
11
+ register(definition={}){
12
+ const id=String(definition.id||'').trim().toLowerCase();
13
+ if(!TOOL_PATTERN.test(id)||this.tools.has(id))throw new TypeError(`Duplicate or invalid system tool: ${id}`);
14
+ if(typeof definition.command!=='function'&&typeof definition.command!=='string')throw new TypeError(`System tool ${id} requires a command builder.`);
15
+ const record=Object.freeze({id,label:String(definition.label||id),description:String(definition.description||''),usage:String(definition.usage||id),command:definition.command});
16
+ this.tools.set(id,record);return record;
17
+ }
18
+ list(){return [...this.tools.values()].sort((left,right)=>left.id.localeCompare(right.id));}
19
+ get(id){return this.tools.get(String(id||'').toLowerCase())||null;}
20
+ build(id,args=[]){
21
+ const tool=this.get(id);if(!tool)throw new RangeError(`Unknown system tool: ${id}`);
22
+ const command=typeof tool.command==='function'?tool.command(Array.from(args)):tool.command;
23
+ if(typeof command!=='string'||!command.trim())throw new TypeError(`System tool ${id} produced an empty command.`);
24
+ return command.trim();
25
+ }
26
+ }
27
+
28
+ export {quoteArgument};
@@ -0,0 +1,52 @@
1
+ import TerminalSession from '../entities/TerminalSession.js';
2
+
3
+ export default class TerminalClient extends EventTarget{
4
+ constructor(api=globalThis.Arcane?.terminal){
5
+ super();
6
+ this.api=api||null;
7
+ this.sessions=new Map();
8
+ this.unsubscribe=[];
9
+ const events=globalThis.Arcane?.events;
10
+ if(events?.on){
11
+ for(const type of ['terminal.output','terminal.exit','terminal.error']){
12
+ this.unsubscribe.push(events.on(type,data=>this.receive(type,data)));
13
+ }
14
+ }
15
+ }
16
+
17
+ get available(){ return Boolean(this.api?.start&&this.api?.write&&this.api?.close); }
18
+
19
+ async start(options={}){
20
+ if(!this.available) throw new Error('The Arcane native terminal capability is unavailable. Open Terminal from the installed Arcane shell.');
21
+ const result=await this.api.start(options);
22
+ const session=new TerminalSession({...result,state:'running'});
23
+ this.sessions.set(session.id,session);
24
+ this.emit('session',{session});
25
+ return session;
26
+ }
27
+
28
+ async write(id,data){ TerminalSession.id(id);return this.api.write(id,String(data??'')); }
29
+ async resize(id,columns,rows){ TerminalSession.id(id);return this.api.resize(id,Number(columns),Number(rows)); }
30
+ async signal(id,signal='interrupt'){ TerminalSession.id(id);return this.api.signal(id,String(signal)); }
31
+ async close(id){
32
+ TerminalSession.id(id);
33
+ const result=await this.api.close(id);
34
+ const session=this.sessions.get(id);
35
+ if(session) this.sessions.set(id,session.with({state:'closed'}));
36
+ return result;
37
+ }
38
+
39
+ receive(type,data={}){
40
+ const id=String(data.sessionId||'');
41
+ if(!id||!this.sessions.has(id)) return;
42
+ if(type==='terminal.exit'){
43
+ const session=this.sessions.get(id).with({state:'exited'});
44
+ this.sessions.set(id,session);
45
+ this.emit('exit',{...data,session});
46
+ }else if(type==='terminal.error') this.emit('error',data);
47
+ else this.emit('output',data);
48
+ }
49
+
50
+ emit(type,detail){ this.dispatchEvent(new CustomEvent(`terminal-${type}`,{detail})); }
51
+ destroy(){ for(const off of this.unsubscribe) if(typeof off==='function') off();this.unsubscribe=[]; }
52
+ }
@@ -0,0 +1,50 @@
1
+ const COMMAND_PATTERN=/^(?:[a-z][a-z0-9:_-]{0,63}|\?)$/;
2
+
3
+ export function splitCommandLine(input=''){
4
+ const source=String(input||'').trim();
5
+ const values=[];
6
+ let token='';
7
+ let quote='';
8
+ let escaped=false;
9
+ for(const character of source){
10
+ if(escaped){token+=character;escaped=false;continue;}
11
+ if(character==='\\'&&quote!=='\''){escaped=true;continue;}
12
+ if(quote){if(character===quote) quote='';else token+=character;continue;}
13
+ if(character==='"'||character==='\''){quote=character;continue;}
14
+ if(/\s/.test(character)){if(token){values.push(token);token='';}continue;}
15
+ token+=character;
16
+ }
17
+ if(escaped) token+='\\';
18
+ if(token) values.push(token);
19
+ return values;
20
+ }
21
+
22
+ export default class TerminalCommandRegistry{
23
+ constructor(commands=[]){
24
+ this.commands=new Map();
25
+ for(const command of commands) this.register(command);
26
+ }
27
+
28
+ register({name,aliases=[],description='',usage='',run}={}){
29
+ const normalized=String(name||'').trim().toLowerCase();
30
+ if(!COMMAND_PATTERN.test(normalized)) throw new TypeError('Terminal command names must use lowercase command syntax.');
31
+ if(typeof run!=='function') throw new TypeError(`Terminal command ${normalized} requires a run function.`);
32
+ const record=Object.freeze({name:normalized,aliases:Object.freeze(aliases.map(alias=>String(alias).toLowerCase())),description:String(description),usage:String(usage||normalized),run});
33
+ for(const key of [record.name,...record.aliases]){
34
+ if(!COMMAND_PATTERN.test(key)||this.commands.has(key)) throw new TypeError(`Duplicate or invalid terminal command: ${key}`);
35
+ this.commands.set(key,record);
36
+ }
37
+ return record;
38
+ }
39
+
40
+ resolve(name){ return this.commands.get(String(name||'').toLowerCase())||null; }
41
+ definitions(){ return [...new Set(this.commands.values())].sort((left,right)=>left.name.localeCompare(right.name)); }
42
+ completions(prefix=''){ const value=String(prefix).toLowerCase();return this.definitions().map(item=>item.name).filter(name=>name.startsWith(value)); }
43
+
44
+ async execute(line,context={}){
45
+ const [name,...args]=splitCommandLine(line);
46
+ const command=this.resolve(name);
47
+ if(!command) return {handled:false};
48
+ return {handled:true,value:await command.run({args,line:String(line),command,context})};
49
+ }
50
+ }
@@ -0,0 +1,26 @@
1
+ import {loadAndApplyTheme} from './ThemeManager.js';
2
+
3
+ const sharedKey='arcaneThemeReady';
4
+ const listenerKey='arcaneThemeAppearanceListener';
5
+
6
+ export function bootstrapArcaneTheme(options={}){
7
+ const useSharedPromise=Object.keys(options).length===0;
8
+ if(useSharedPromise&&globalThis[sharedKey]) return globalThis[sharedKey];
9
+
10
+ const ready=loadAndApplyTheme(options).catch(error=>{
11
+ console.warn('[Arcane theme] Unable to load the saved appearance; using the system theme.',error);
12
+ return {manager:null,state:null,error};
13
+ });
14
+
15
+ if(useSharedPromise) globalThis[sharedKey]=ready;
16
+ if(useSharedPromise&&globalThis.Arcane?.events?.on&&!globalThis[listenerKey]){
17
+ globalThis[listenerKey]=globalThis.Arcane.events.on('appearance.changed',async()=>{
18
+ const result=await ready;
19
+ if(result?.manager) await result.manager.load();
20
+ });
21
+ }
22
+ return ready;
23
+ }
24
+
25
+ export const arcaneThemeReady=bootstrapArcaneTheme();
26
+ export default arcaneThemeReady;
@@ -0,0 +1,131 @@
1
+ import Theme,{arcaneDarkThemeTokens,arcaneLightThemeTokens} from '../entities/Theme.js';
2
+ import PreferenceStore from './PreferenceStore.js';
3
+ import {applyAppearancePreferences,createAppearancePreferenceStore} from './AppearancePreferences.js';
4
+ import SystemAppearance from './SystemAppearance.js';
5
+
6
+ const skinSchema=Object.freeze([
7
+ {key:'appearance.activeSkin',type:'text',defaultValue:''},
8
+ {key:'appearance.customSkin',type:'text',defaultValue:''}
9
+ ]);
10
+
11
+ export default class ThemeManager{
12
+ constructor({appearanceStore=null,skinStore=null,systemAppearance=null,root=null}={}){
13
+ this.appearanceStore=appearanceStore||createAppearancePreferenceStore();
14
+ this.skinStore=skinStore||new PreferenceStore({namespace:'arcane',schema:skinSchema});
15
+ this.root=root||globalThis.document?.documentElement||null;
16
+ this.systemAppearance=systemAppearance||new SystemAppearance();
17
+ this.appearance=this.appearanceStore.defaults();
18
+ this.skinState=this.skinStore.defaults();
19
+ this.customTheme=null;
20
+ }
21
+
22
+ async load(){
23
+ const [appearance,skinState]=await Promise.all([this.appearanceStore.load(),this.skinStore.load()]);
24
+ this.appearance=appearance;
25
+ this.skinState=skinState;
26
+ this.customTheme=parseTheme(skinState['appearance.customSkin']);
27
+ this.apply();
28
+ return this.current();
29
+ }
30
+
31
+ apply(){
32
+ if(!this.root) return this.current();
33
+ Theme.clear(this.root);
34
+ applyAppearancePreferences(this.appearance,this.root);
35
+ if(this.skinState['appearance.activeSkin']==='custom'&&this.customTheme) this.customTheme.apply(this.root);
36
+ return this.current();
37
+ }
38
+
39
+ current(){
40
+ const custom=this.skinState['appearance.activeSkin']==='custom'&&this.customTheme;
41
+ return {
42
+ mode:custom?'custom':this.appearance['appearance.colorScheme']||'system',
43
+ appearance:{...this.appearance},
44
+ theme:this.customTheme?.toJSON()||null
45
+ };
46
+ }
47
+
48
+ async setScheme(scheme='system'){
49
+ const normalized=['system','light','dark'].includes(scheme)?scheme:'system';
50
+ await Promise.all([
51
+ this.appearanceStore.set('appearance.colorScheme',normalized),
52
+ this.skinStore.set('appearance.activeSkin','')
53
+ ]);
54
+ this.appearance={...this.appearance,'appearance.colorScheme':normalized};
55
+ this.skinState={...this.skinState,'appearance.activeSkin':''};
56
+ this.apply();await this.syncSystemAppearance();this.emit();
57
+ return this.current();
58
+ }
59
+
60
+ async saveCustom(input={}){
61
+ const theme=Theme.fromJSON(input);
62
+ await Promise.all([
63
+ this.skinStore.set('appearance.customSkin',JSON.stringify(theme)),
64
+ this.skinStore.set('appearance.activeSkin','custom'),
65
+ this.appearanceStore.set('appearance.colorScheme',theme.scheme)
66
+ ]);
67
+ this.customTheme=theme;
68
+ this.skinState={...this.skinState,'appearance.customSkin':JSON.stringify(theme),'appearance.activeSkin':'custom'};
69
+ this.appearance={...this.appearance,'appearance.colorScheme':theme.scheme};
70
+ this.apply();await this.syncSystemAppearance();this.emit();
71
+ return this.current();
72
+ }
73
+
74
+ async activateCustom(){
75
+ if(!this.customTheme) await this.load();
76
+ if(!this.customTheme) return this.current();
77
+ await Promise.all([
78
+ this.skinStore.set('appearance.activeSkin','custom'),
79
+ this.appearanceStore.set('appearance.colorScheme',this.customTheme.scheme)
80
+ ]);
81
+ this.skinState={...this.skinState,'appearance.activeSkin':'custom'};
82
+ this.appearance={...this.appearance,'appearance.colorScheme':this.customTheme.scheme};
83
+ this.apply();await this.syncSystemAppearance();this.emit();
84
+ return this.current();
85
+ }
86
+
87
+ preview(input={}){
88
+ const theme=Theme.fromJSON(input);
89
+ if(this.root) theme.apply(this.root);
90
+ return theme;
91
+ }
92
+
93
+ async resetCustom(){
94
+ await Promise.all([
95
+ this.skinStore.set('appearance.customSkin',''),
96
+ this.skinStore.set('appearance.activeSkin','')
97
+ ]);
98
+ this.customTheme=null;
99
+ this.skinState={...this.skinState,'appearance.customSkin':'','appearance.activeSkin':''};
100
+ this.apply();await this.syncSystemAppearance();this.emit();
101
+ return this.current();
102
+ }
103
+
104
+ async syncSystemAppearance(){
105
+ const state=this.current();
106
+ const theme=state.mode==='custom'?this.customTheme:null;
107
+ const tokens=theme?.tokens||(state.mode==='dark'?arcaneDarkThemeTokens:state.mode==='light'?arcaneLightThemeTokens:null);
108
+ return this.systemAppearance.apply({
109
+ scheme:theme?.scheme||state.mode,
110
+ captionColor:tokens?.surface||null,
111
+ textColor:tokens?.text||null
112
+ });
113
+ }
114
+
115
+ emit(){
116
+ if(typeof globalThis.CustomEvent==='function'&&typeof globalThis.dispatchEvent==='function'){
117
+ globalThis.dispatchEvent(new CustomEvent('arcane-theme-change',{detail:this.current()}));
118
+ }
119
+ }
120
+ }
121
+
122
+ function parseTheme(value){
123
+ if(!value) return null;
124
+ try{return Theme.fromJSON(value);}catch{return null;}
125
+ }
126
+
127
+ export async function loadAndApplyTheme(options={}){
128
+ const manager=options.manager||new ThemeManager(options);
129
+ const state=await manager.load();
130
+ return {manager,state};
131
+ }
@@ -0,0 +1,149 @@
1
+ import Is from '../../node_modules/strong-type/index.js';
2
+ import './DBOPFS.js';
3
+ import UserEntity from '../entities/User.js';
4
+
5
+ const is=new Is(false);
6
+
7
+ class TimeGuard {
8
+ // Default to 17 day grace period from last check
9
+ #gracePeriod = 1000 * 60 * 60 * 24 * 17;
10
+ // ms s m h d
11
+
12
+ // Allow a six (6) hour grace period to handle time changes
13
+ #gracePeriodClock = 1000 * 60 * 60 * 6;
14
+ // ms s m h
15
+
16
+ // Note, all times are in milliseconds
17
+ #storedTime = 0;
18
+ #lastSuccessfulTime = 0;
19
+
20
+ constructor() {
21
+ if (window.timeguard) {
22
+ return window.timeguard;
23
+ }
24
+
25
+ this.#loadTime();
26
+ }
27
+
28
+ #loadTime() {
29
+ this.#storedTime = window.user?.current_time || 0;
30
+ this.#lastSuccessfulTime = window.user?.last_successful_time || 0;
31
+
32
+ return true;
33
+ }
34
+
35
+ // set gracePeriod(v) {
36
+ // if(!is.number(v)) {
37
+ // throw new Error('gracePeriod must be number')
38
+ // }
39
+
40
+ // this.#gracePeriod = v;
41
+
42
+ // return true;
43
+ // }
44
+
45
+ // get gracePeriod() {
46
+ // return this.#gracePeriod;
47
+ // }
48
+
49
+ #checkTime(time=0) {
50
+ if(!is.number(time)) {
51
+ throw new Error('time must be number')
52
+ }
53
+
54
+ if (!time) {
55
+ throw new Error('time must be supplied');
56
+ }
57
+ }
58
+
59
+ setStoredTime(time=0) {
60
+ this.#checkTime(time);
61
+
62
+ if (time < this.#storedTime) {
63
+ //throw new Error('current time must be greater than last time check')
64
+ }
65
+
66
+ this.#storedTime = time;
67
+ window.user.current_time = time;
68
+
69
+ return true;
70
+ }
71
+
72
+ setLastSuccessfulCheckTime(time=0) {
73
+ this.#checkTime(time);
74
+
75
+ if (time < this.#lastSuccessfulTime) {
76
+ //console.log('Clock rollback detected');
77
+ throw new Error('current time must be greater than last time check');
78
+ }
79
+
80
+ this.#lastSuccessfulTime = time;
81
+ window.user.last_successful_time = time;
82
+
83
+ return true;
84
+ }
85
+
86
+ checkClockRollback() {
87
+ // Get current time and add time change grace period.
88
+ // This is to allow for time changes up to six hours
89
+ const currentTimeWithTimeChangeGracePeriod = Date.now() + this.#gracePeriodClock;
90
+
91
+ if (currentTimeWithTimeChangeGracePeriod < this.#storedTime) {
92
+ //console.log('Clock rollback detected');
93
+ throw new Error('Clock has been rolled back greater than six hours from last check');
94
+ }
95
+
96
+ return true;
97
+ }
98
+
99
+ checkGracePeriod() {
100
+ this.checkClockRollback();
101
+
102
+ const currentTime = Date.now();
103
+
104
+ if (currentTime < this.#lastSuccessfulTime) {
105
+ //console.log('Clock rollback detected');
106
+ throw new Error('current time must be greater than last time check')
107
+ }
108
+
109
+ const timeDifferenceBetweenChecks = currentTime - this.#lastSuccessfulTime;
110
+
111
+ console.log(timeDifferenceBetweenChecks);
112
+
113
+ // Note, if the last known time check has not been set, this error will always be emitted
114
+ if (timeDifferenceBetweenChecks > this.#gracePeriod) {
115
+ throw new Error('Grace period time has been exceeded or key is invalid');
116
+ }
117
+
118
+ return true;
119
+ }
120
+ }
121
+
122
+
123
+ window.addEventListener(
124
+ 'user-entity-loaded',
125
+ instantiateTimeGuard
126
+ );
127
+
128
+ if(window.user?.ready){
129
+ instantiateTimeGuard();
130
+ }
131
+
132
+ function instantiateTimeGuard() {
133
+ if(!window.timeguard){
134
+ window.timeguard = new TimeGuard();
135
+
136
+ window.timeguard.ready = true;
137
+
138
+ const timeguardReady = new CustomEvent(
139
+ 'time-guard-ready', {
140
+ detail: { db: window.timeguard }
141
+ }
142
+ );
143
+
144
+ window.dispatchEvent(timeguardReady);
145
+
146
+ }
147
+ }
148
+
149
+ export default TimeGuard;
@@ -0,0 +1,83 @@
1
+ function parseArguments(value,name=''){
2
+ if(value&&typeof value==='object'){
3
+ return value;
4
+ }
5
+
6
+ if(typeof value!=='string'){
7
+ throw new Error(`Tool ${name} did not provide valid arguments.`);
8
+ }
9
+
10
+ try{
11
+ return JSON.parse(value);
12
+ }catch(error){
13
+ console.warn(error);
14
+ throw new Error(`Tool ${name} returned invalid JSON arguments.`);
15
+ }
16
+ }
17
+
18
+ function getResponseCalls(response={}){
19
+ const calls=response?.choices?.[0]?.message?.tool_calls;
20
+
21
+ if(!Array.isArray(calls)||!calls.length){
22
+ throw new Error('AI response did not contain a tool call.');
23
+ }
24
+
25
+ return calls;
26
+ }
27
+
28
+ function dispatch(name='',argumentsValue='',handlers={}){
29
+ if(typeof handlers[name]!=='function'){
30
+ throw new Error(`No handler is registered for tool ${name}.`);
31
+ }
32
+
33
+ return handlers[name](
34
+ parseArguments(argumentsValue,name)
35
+ );
36
+ }
37
+
38
+ async function handleResponse(response={},handlers={}){
39
+ const calls=getResponseCalls(response);
40
+ const results=[];
41
+
42
+ for(let i=0;i<calls.length;i++){
43
+ const toolCall=calls[i]?.function;
44
+
45
+ if(!toolCall?.name){
46
+ throw new Error('AI response contained an invalid tool call.');
47
+ }
48
+
49
+ results.push(
50
+ await dispatch(
51
+ toolCall.name,
52
+ toolCall.arguments,
53
+ handlers
54
+ )
55
+ );
56
+ }
57
+
58
+ return results.length===1
59
+ ?results[0]
60
+ :results;
61
+ }
62
+
63
+ async function handleStreamedCalls(calls={},handlers={}){
64
+ if(!calls||typeof calls!=='object'||Array.isArray(calls)){
65
+ throw new Error('Streamed tool calls must be an object.');
66
+ }
67
+
68
+ const names=Object.keys(calls);
69
+
70
+ return Promise.allSettled(
71
+ names.map(
72
+ name=>Promise.resolve().then(
73
+ ()=>dispatch(name,calls[name],handlers)
74
+ )
75
+ )
76
+ );
77
+ }
78
+
79
+ export {
80
+ handleResponse,
81
+ handleStreamedCalls,
82
+ parseArguments
83
+ };