arcane-os 0.2.2 → 0.3.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.
Files changed (117) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +8 -8
  3. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +29 -22
  4. package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +203 -0
  5. package/browser-runtime/ai/browser-kokoro-worker.mjs +11 -2
  6. package/browser-runtime/ai/browser-speech-artifacts.mjs +3230 -397
  7. package/browser-runtime/ai/browser-speech-providers.mjs +1141 -157
  8. package/browser-runtime/ai/browser-speech.mjs +2 -0
  9. package/browser-runtime/ai/browser-whisper-worker.mjs +11 -2
  10. package/browser-runtime/ai/model-controller.mjs +285 -95
  11. package/browser-runtime/ai/speech-worker-client.mjs +247 -32
  12. package/browser-runtime/ai/speech-worker-runtime.mjs +2310 -167
  13. package/browser-runtime/event-manager.mjs +1097 -1
  14. package/docs/architecture.md +2 -2
  15. package/docs/event-manager.md +155 -27
  16. package/docs/reference/README.md +27 -27
  17. package/docs/reference/ai/browser-speech-package-authority.json +835 -0
  18. package/docs/reference/ai/browser-speech.md +1162 -246
  19. package/docs/reference/ai/browser-wasm.md +18 -7
  20. package/docs/reference/availability-and-normalization.md +6 -3
  21. package/docs/reference/behavioral-testing.md +29 -6
  22. package/docs/reference/cli.md +117 -9
  23. package/docs/reference/core/arcane-ai-contracts.md +1 -1
  24. package/docs/reference/event-manager.md +577 -32
  25. package/docs/reference/inventory/package-api.json +478 -2
  26. package/docs/reference/inventory/runtime-components.json +108 -44
  27. package/docs/reference/inventory/runtime-modules.json +131 -53
  28. package/docs/reference/mail.md +316 -0
  29. package/docs/reference/protocols.md +157 -43
  30. package/docs/reference/runtime-components.md +258 -83
  31. package/docs/reference/runtime-modules.md +613 -77
  32. package/docs/reference/sdk-api.md +1014 -25
  33. package/package.json +5 -4
  34. package/runtime/ARCANE_RUNTIME_RELEASE.json +145 -140
  35. package/runtime/arcane/components/app-bar.html +34 -13
  36. package/runtime/arcane/components/assistant-panel.html +110 -57
  37. package/runtime/arcane/components/calculator.html +7 -4
  38. package/runtime/arcane/components/chart.html +58 -17
  39. package/runtime/arcane/components/chat.html +606 -136
  40. package/runtime/arcane/components/conversation-view.html +13 -6
  41. package/runtime/arcane/components/dashboard-config.html +96 -59
  42. package/runtime/arcane/components/data-maintenance.html +69 -14
  43. package/runtime/arcane/components/data-view.html +53 -7
  44. package/runtime/arcane/components/directory-picker.html +118 -32
  45. package/runtime/arcane/components/document-inspector.html +47 -10
  46. package/runtime/arcane/components/file-drop.html +81 -35
  47. package/runtime/arcane/components/file-inspector.html +72 -22
  48. package/runtime/arcane/components/file-manager.html +374 -79
  49. package/runtime/arcane/components/integration-settings.html +12 -5
  50. package/runtime/arcane/components/local-ai-status.html +48 -19
  51. package/runtime/arcane/components/markdown-document.html +161 -68
  52. package/runtime/arcane/components/markdown-editor.html +110 -33
  53. package/runtime/arcane/components/media-embed.html +8 -5
  54. package/runtime/arcane/components/modal.html +15 -5
  55. package/runtime/arcane/components/output-panel.html +28 -23
  56. package/runtime/arcane/components/preferences-form.html +22 -4
  57. package/runtime/arcane/components/record-timeline.html +18 -2
  58. package/runtime/arcane/components/relationship-board.html +23 -3
  59. package/runtime/arcane/components/screen-capture.html +10 -4
  60. package/runtime/arcane/components/source-code-viewer.html +76 -9
  61. package/runtime/arcane/components/source-explanation.html +23 -3
  62. package/runtime/arcane/components/speech.html +462 -384
  63. package/runtime/arcane/components/summary-strip.html +22 -11
  64. package/runtime/arcane/components/table.html +39 -21
  65. package/runtime/arcane/components/task-progress.html +79 -21
  66. package/runtime/arcane/components/terminal-workspace.html +7 -4
  67. package/runtime/arcane/components/theme-editor.html +7 -3
  68. package/runtime/arcane/components/unified-inbox.html +9 -4
  69. package/runtime/arcane/components/voice-transcription.html +639 -98
  70. package/runtime/arcane/components/weather-widget.html +5 -3
  71. package/runtime/arcane/components/web-navigator.html +48 -8
  72. package/runtime/arcane/entities/Chat.js +1 -1
  73. package/runtime/arcane/entities/User.js +110 -23
  74. package/runtime/arcane/modules/AI.js +2109 -130
  75. package/runtime/arcane/modules/AIProviderRuntime.js +720 -13
  76. package/runtime/arcane/modules/AIRuntimeState.js +109 -52
  77. package/runtime/arcane/modules/ApiModelDatabase.js +390 -17
  78. package/runtime/arcane/modules/BrowserTestSuite.js +205 -28
  79. package/runtime/arcane/modules/CalculatorEngine.js +63 -3
  80. package/runtime/arcane/modules/CommunicationAppController.js +588 -28
  81. package/runtime/arcane/modules/CommunicationHub.js +590 -10
  82. package/runtime/arcane/modules/ComponentContracts.js +470 -0
  83. package/runtime/arcane/modules/ConversationTimebox.js +152 -33
  84. package/runtime/arcane/modules/DBLS.js +40 -7
  85. package/runtime/arcane/modules/DBOPFS.js +35 -11
  86. package/runtime/arcane/modules/DataMaintenance.js +12 -2
  87. package/runtime/arcane/modules/Errors.js +65 -7
  88. package/runtime/arcane/modules/HTMLImport.js +198 -14
  89. package/runtime/arcane/modules/LocalAIReadinessController.js +208 -29
  90. package/runtime/arcane/modules/Mail.js +738 -115
  91. package/runtime/arcane/modules/MailOutbox.mjs +1395 -0
  92. package/runtime/arcane/modules/MailTransport.mjs +197 -39
  93. package/runtime/arcane/modules/Ollama.js +36 -1
  94. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +583 -7
  95. package/runtime/arcane/modules/PreferenceStore.js +367 -33
  96. package/runtime/arcane/modules/RecordReviewStore.js +322 -23
  97. package/runtime/arcane/modules/ScreenCapture.js +1397 -15
  98. package/runtime/arcane/modules/SpeechPlayback.js +438 -41
  99. package/runtime/arcane/modules/TerminalClient.js +277 -12
  100. package/runtime/arcane/modules/ThemeBootstrap.js +80 -6
  101. package/runtime/arcane/modules/ThemeManager.js +39 -7
  102. package/runtime/arcane/modules/TimeGuard.js +131 -20
  103. package/runtime/arcane/modules/WaitForComponent.js +386 -33
  104. package/schemas/arcane-lock.schema.json +2 -2
  105. package/src/cli/main.mjs +435 -9
  106. package/src/event-manager.mjs +1097 -1
  107. package/src/import-map.mjs +21 -3
  108. package/src/index.mjs +13 -0
  109. package/src/installed-sdk-runtime.mjs +112 -0
  110. package/src/mail-api.mjs +22 -0
  111. package/src/mail-credentials.mjs +667 -0
  112. package/src/mail-server.mjs +1769 -0
  113. package/src/mail.mjs +261 -0
  114. package/src/sdk-browser-runtime.mjs +85 -41
  115. package/src/testing-loader.mjs +7 -0
  116. package/src/toolchain.mjs +3 -0
  117. package/src/workspace.mjs +1 -1
@@ -1,6 +1,33 @@
1
1
  export const DEFAULT_MAIL_REQUEST_TIMEOUT_MS=590_000;
2
2
  export const MAX_MAIL_RESPONSE_BYTES=65_536;
3
3
 
4
+ const REPORT_KEY_PATTERN=/^[a-zA-Z0-9._:-]{8,128}$/;
5
+ const REQUEST_ID_PATTERN=/^[a-zA-Z0-9-]{8,128}$/;
6
+ const PROVIDER_ID_PATTERN=/^[a-zA-Z0-9._:-]{1,256}$/;
7
+ const ERROR_CODE_PATTERN=/^[a-zA-Z0-9._:-]{1,80}$/;
8
+ const RETRYABLE_STATUS_CODES=new Set([408,425,429,500,502,503,504]);
9
+ const NON_RETRYABLE_RATE_CODES=new Set(['daily_quota_exceeded','monthly_quota_exceeded']);
10
+ const RESPONSE_CONTRACT=Object.freeze({
11
+ accepted:202,
12
+ delivery_uncertain:207,
13
+ partially_accepted:207,
14
+ });
15
+
16
+ export class MailTransportError extends Error {
17
+ constructor(message,{cause,code='MAIL_TRANSPORT_ERROR',retryable=false,
18
+ retryAfterMs=0,statusCode=0,uncertain=false}={}){
19
+ super(message,{cause});
20
+ this.name='MailTransportError';
21
+ this.code=code;
22
+ this.retryable=Boolean(retryable);
23
+ this.retryAfterMs=Number.isSafeInteger(retryAfterMs)&&retryAfterMs>0
24
+ ? retryAfterMs
25
+ : 0;
26
+ this.statusCode=Number.isSafeInteger(statusCode)?statusCode:0;
27
+ this.uncertain=Boolean(uncertain);
28
+ }
29
+ }
30
+
4
31
  export function normalizeMailEndpoint(endpoint,base=globalThis.location?.href){
5
32
  if(typeof endpoint!=='string'||!endpoint.trim()){
6
33
  throw new Error('Mail endpoint is required');
@@ -21,15 +48,7 @@ export function normalizeMailEndpoint(endpoint,base=globalThis.location?.href){
21
48
  return url.href;
22
49
  }
23
50
 
24
- const REPORT_KEY_PATTERN=/^[a-zA-Z0-9._:-]{8,128}$/;
25
- const REQUEST_ID_PATTERN=/^[a-zA-Z0-9-]{8,128}$/;
26
- const RESPONSE_CONTRACT=Object.freeze({
27
- accepted:202,
28
- delivery_uncertain:207,
29
- partially_accepted:207,
30
- });
31
-
32
- function serializeReport(report){
51
+ export function serializeMailReport(report){
33
52
  if(!report||typeof report!=='object'||Array.isArray(report)){
34
53
  throw new Error('Mail report must be a JSON object');
35
54
  }
@@ -44,25 +63,69 @@ function serializeReport(report){
44
63
  }
45
64
  }
46
65
 
47
- function parseDeliveryResponse(response,responseText){
48
- let body;
66
+ function validateSerializedReport(serializedReport){
67
+ if(typeof serializedReport!=='string'||!serializedReport){
68
+ throw new Error('Serialized mail report is required');
69
+ }
70
+ let parsed;
49
71
  try{
50
- body=JSON.parse(responseText);
72
+ parsed=JSON.parse(serializedReport);
51
73
  }catch{
52
- throw new Error('Mail server returned an invalid success response');
74
+ throw new Error('Serialized mail report must contain valid JSON');
75
+ }
76
+ if(!parsed||typeof parsed!=='object'||Array.isArray(parsed)){
77
+ throw new Error('Serialized mail report must contain a JSON object');
78
+ }
79
+ return serializedReport;
80
+ }
81
+
82
+ function parseJsonObject(value){
83
+ try{
84
+ const parsed=JSON.parse(value);
85
+ return parsed&&typeof parsed==='object'&&!Array.isArray(parsed)?parsed:null;
86
+ }catch{
87
+ return null;
88
+ }
89
+ }
90
+
91
+ function parseRetryAfter(value,now=Date.now()){
92
+ if(typeof value!=='string'||!value.trim()){
93
+ return 0;
53
94
  }
54
- if(!body||typeof body!=='object'||Array.isArray(body)
95
+ const trimmed=value.trim();
96
+ if(/^\d+(?:\.\d+)?$/u.test(trimmed)){
97
+ return Math.min(86_400_000,Math.max(0,Math.ceil(Number(trimmed)*1000)));
98
+ }
99
+ const timestamp=Date.parse(trimmed);
100
+ return Number.isFinite(timestamp)
101
+ ? Math.min(86_400_000,Math.max(0,timestamp-now))
102
+ : 0;
103
+ }
104
+
105
+ function invalidSuccessResponse(response){
106
+ return new MailTransportError('Mail server returned an invalid success response',{
107
+ code:'MAIL_INVALID_RESPONSE',statusCode:response.status,uncertain:true,
108
+ });
109
+ }
110
+
111
+ function parseDeliveryResponse(response,responseText){
112
+ const body=parseJsonObject(responseText);
113
+ if(!body
55
114
  || typeof body.requestId!=='string'||!REQUEST_ID_PATTERN.test(body.requestId)
56
115
  || !Object.hasOwn(RESPONSE_CONTRACT,body.status)
57
116
  || RESPONSE_CONTRACT[body.status]!==response.status) {
58
- throw new Error('Mail server returned an invalid success response');
117
+ throw invalidSuccessResponse(response);
59
118
  }
60
119
  for(const field of ['accepted','rejected']){
61
120
  if(body[field]!==undefined
62
121
  && (!Number.isSafeInteger(body[field])||body[field]<0)) {
63
- throw new Error('Mail server returned an invalid success response');
122
+ throw invalidSuccessResponse(response);
64
123
  }
65
124
  }
125
+ if(body.providerId!==undefined
126
+ && (typeof body.providerId!=='string'||!PROVIDER_ID_PATTERN.test(body.providerId))){
127
+ throw invalidSuccessResponse(response);
128
+ }
66
129
 
67
130
  return {
68
131
  requestId:body.requestId,
@@ -71,20 +134,69 @@ function parseDeliveryResponse(response,responseText){
71
134
  uncertain:body.status==='delivery_uncertain',
72
135
  status:body.status,
73
136
  statusCode:response.status,
137
+ ...(body.providerId?{providerId:body.providerId}:{}),
138
+ retryAfterMs:Number.isSafeInteger(body.retryAfterMs)&&body.retryAfterMs>0
139
+ ? body.retryAfterMs
140
+ : parseRetryAfter(response.headers?.get?.('retry-after')),
74
141
  };
75
142
  }
76
143
 
144
+ function parseRejection(response,responseText){
145
+ const body=parseJsonObject(responseText);
146
+ const source=body?.error&&typeof body.error==='object'&&!Array.isArray(body.error)
147
+ ? body.error
148
+ : body;
149
+ const rawCode=source?.code;
150
+ const code=typeof rawCode==='string'&&ERROR_CODE_PATTERN.test(rawCode)
151
+ ? rawCode
152
+ : `MAIL_HTTP_${String(response.status)}`;
153
+ let retryable=typeof source?.retryable==='boolean'
154
+ ? source.retryable
155
+ : RETRYABLE_STATUS_CODES.has(response.status);
156
+ if(code==='invalid_idempotent_request'||NON_RETRYABLE_RATE_CODES.has(code)){
157
+ retryable=false;
158
+ }else if(code==='concurrent_idempotent_requests'){
159
+ retryable=true;
160
+ }
161
+ const uncertain=typeof source?.uncertain==='boolean'?source.uncertain:false;
162
+ const bodyRetryAfter=Number.isSafeInteger(source?.retryAfterMs)&&source.retryAfterMs>0
163
+ ? source.retryAfterMs
164
+ : 0;
165
+ return new MailTransportError(`Mail server rejected the request (${response.status})`,{
166
+ code,
167
+ retryable,
168
+ retryAfterMs:bodyRetryAfter||parseRetryAfter(response.headers?.get?.('retry-after')),
169
+ statusCode:response.status,
170
+ uncertain,
171
+ });
172
+ }
173
+
77
174
  async function readBoundedResponseText(response){
78
175
  const declaredLength=response.headers?.get?.('content-length');
79
176
  if(declaredLength!==null&&declaredLength!==undefined&&declaredLength!==''){
80
177
  const parsedLength=Number(declaredLength);
81
178
  if(!Number.isSafeInteger(parsedLength)||parsedLength<0||parsedLength>MAX_MAIL_RESPONSE_BYTES){
82
- throw new Error(`Mail server response cannot exceed ${MAX_MAIL_RESPONSE_BYTES.toLocaleString('en-US')} bytes`);
179
+ throw new MailTransportError(
180
+ `Mail server response cannot exceed ${MAX_MAIL_RESPONSE_BYTES.toLocaleString('en-US')} bytes`,
181
+ {code:'MAIL_RESPONSE_TOO_LARGE',statusCode:response.status,uncertain:true}
182
+ );
83
183
  }
84
184
  }
85
185
 
86
186
  if(!response.body||typeof response.body.getReader!=='function'){
87
- throw new Error('Mail server returned an unreadable success response');
187
+ if(typeof response.text!=='function'){
188
+ throw new MailTransportError('Mail server returned an unreadable response',{
189
+ code:'MAIL_UNREADABLE_RESPONSE',statusCode:response.status,uncertain:true,
190
+ });
191
+ }
192
+ const text=await response.text();
193
+ if(new TextEncoder().encode(text).byteLength>MAX_MAIL_RESPONSE_BYTES){
194
+ throw new MailTransportError(
195
+ `Mail server response cannot exceed ${MAX_MAIL_RESPONSE_BYTES.toLocaleString('en-US')} bytes`,
196
+ {code:'MAIL_RESPONSE_TOO_LARGE',statusCode:response.status,uncertain:true}
197
+ );
198
+ }
199
+ return text;
88
200
  }
89
201
 
90
202
  const reader=response.body.getReader();
@@ -96,12 +208,17 @@ async function readBoundedResponseText(response){
96
208
  const { done,value }=await reader.read();
97
209
  if(done) break;
98
210
  if(!(value instanceof Uint8Array)){
99
- throw new Error('Mail server returned an unreadable success response');
211
+ throw new MailTransportError('Mail server returned an unreadable response',{
212
+ code:'MAIL_UNREADABLE_RESPONSE',statusCode:response.status,uncertain:true,
213
+ });
100
214
  }
101
215
  byteLength+=value.byteLength;
102
216
  if(byteLength>MAX_MAIL_RESPONSE_BYTES){
103
- await reader.cancel().catch(() => {});
104
- throw new Error(`Mail server response cannot exceed ${MAX_MAIL_RESPONSE_BYTES.toLocaleString('en-US')} bytes`);
217
+ await reader.cancel().catch(function ignoreReaderCancellation(){});
218
+ throw new MailTransportError(
219
+ `Mail server response cannot exceed ${MAX_MAIL_RESPONSE_BYTES.toLocaleString('en-US')} bytes`,
220
+ {code:'MAIL_RESPONSE_TOO_LARGE',statusCode:response.status,uncertain:true}
221
+ );
105
222
  }
106
223
  text+=decoder.decode(value,{stream:true});
107
224
  }
@@ -112,6 +229,17 @@ async function readBoundedResponseText(response){
112
229
  }
113
230
  }
114
231
 
232
+ function requestBodyFrom({report,serializedReport}){
233
+ if(serializedReport===undefined){
234
+ return serializeMailReport(report);
235
+ }
236
+ const validated=validateSerializedReport(serializedReport);
237
+ if(report!==undefined&&serializeMailReport(report)!==validated){
238
+ throw new Error('Mail report does not match its immutable serialized request body');
239
+ }
240
+ return validated;
241
+ }
242
+
115
243
  export async function sendMailReport({
116
244
  appKey,
117
245
  appName,
@@ -120,9 +248,11 @@ export async function sendMailReport({
120
248
  report,
121
249
  reportKey,
122
250
  requestTimeout=DEFAULT_MAIL_REQUEST_TIMEOUT_MS,
251
+ serializedReport,
252
+ signal,
123
253
  }){
124
254
  if(typeof fetchImpl!=='function'){
125
- throw new Error('Mail transport is unavailable');
255
+ throw new MailTransportError('Mail transport is unavailable',{code:'MAIL_UNAVAILABLE'});
126
256
  }
127
257
  const resolvedEndpoint=normalizeMailEndpoint(endpoint);
128
258
  if(typeof appName!=='string'||!/^[a-z0-9](?:[a-z0-9-]{0,62})$/.test(appName)){
@@ -137,7 +267,10 @@ export async function sendMailReport({
137
267
  if(appKey!==undefined&&appKey!==null&&typeof appKey!=='string'){
138
268
  throw new Error('Mail application key must be a string');
139
269
  }
140
- const requestBody=serializeReport(report);
270
+ if(signal!==undefined&&!(signal instanceof AbortSignal)){
271
+ throw new TypeError('Mail signal must be an AbortSignal');
272
+ }
273
+ const requestBody=requestBodyFrom({report,serializedReport});
141
274
 
142
275
  const headers={
143
276
  'Content-Type':'application/json',
@@ -149,32 +282,57 @@ export async function sendMailReport({
149
282
  }
150
283
 
151
284
  const controller=new AbortController();
285
+ let timedOut=false;
286
+ const forwardAbort=function forwardMailAbort(){
287
+ controller.abort(signal.reason??new Error('Mail request was cancelled'));
288
+ };
289
+ signal?.addEventListener('abort',forwardAbort,{once:true});
290
+ if(signal?.aborted){
291
+ forwardAbort();
292
+ }
152
293
  const timeout=setTimeout(
153
- () => controller.abort(new Error('Mail request timed out')),
294
+ function abortTimedOutMail(){
295
+ timedOut=true;
296
+ controller.abort(new Error('Mail request timed out'));
297
+ },
154
298
  requestTimeout
155
299
  );
156
300
 
157
301
  try{
158
- const response=await fetchImpl(
159
- resolvedEndpoint,
160
- {
161
- method:'POST',
162
- headers,
163
- body:requestBody,
164
- credentials:'same-origin',
165
- redirect:'error',
166
- referrerPolicy:'no-referrer',
167
- signal:controller.signal,
168
- }
169
- );
170
-
171
- if(!response.ok){
172
- throw new Error(`Mail server rejected the request (${response.status})`);
302
+ let response;
303
+ try{
304
+ response=await fetchImpl(
305
+ resolvedEndpoint,
306
+ {
307
+ method:'POST',
308
+ headers,
309
+ body:requestBody,
310
+ credentials:'same-origin',
311
+ redirect:'error',
312
+ referrerPolicy:'no-referrer',
313
+ signal:controller.signal,
314
+ }
315
+ );
316
+ }catch(error){
317
+ const cancelled=signal?.aborted&&!timedOut;
318
+ throw new MailTransportError(
319
+ cancelled?'Mail request was cancelled':timedOut?'Mail request timed out':'Mail server could not be reached',
320
+ {
321
+ cause:error,
322
+ code:cancelled?'MAIL_CANCELLED':timedOut?'MAIL_TIMEOUT':'MAIL_NETWORK_ERROR',
323
+ retryable:true,
324
+ uncertain:true,
325
+ }
326
+ );
173
327
  }
174
328
 
175
329
  const responseText=await readBoundedResponseText(response);
330
+ if(!response.ok){
331
+ throw parseRejection(response,responseText);
332
+ }
176
333
  return parseDeliveryResponse(response,responseText);
177
334
  }finally{
178
335
  clearTimeout(timeout);
336
+ signal?.removeEventListener('abort',forwardAbort);
179
337
  }
180
338
  }
@@ -2,6 +2,20 @@
2
2
  * First-class browser module for Arcane's capability-gated Ollama service.
3
3
  * Apps should import this module instead of connecting to localhost:11434.
4
4
  */
5
+ import {
6
+ createArcaneEventSource,
7
+ projectArcaneDOMEvent
8
+ } from 'arcane-os/event-manager';
9
+
10
+ const PUBLISH_OLLAMA_READY=Symbol('publish-ollama-ready');
11
+
12
+ export const OLLAMA_EVENT_TYPES=Object.freeze({
13
+ ready:'arcane-ollama-ready'
14
+ });
15
+ export const OLLAMA_REASONS=Object.freeze({
16
+ ready:'ollama-module-ready'
17
+ });
18
+
5
19
  function api(){
6
20
  const client=globalThis.Arcane?.ollama
7
21
  if(!client){
@@ -13,6 +27,11 @@ function api(){
13
27
  }
14
28
 
15
29
  export class Ollama{
30
+ #events=createArcaneEventSource(this,{
31
+ source:'ollama',
32
+ eventTypes:Object.freeze(Object.values(OLLAMA_EVENT_TYPES))
33
+ })
34
+
16
35
  version(){ return api().version() }
17
36
  models(){ return api().models() }
18
37
  list(){ return api().models() }
@@ -63,6 +82,22 @@ export class Ollama{
63
82
  unload(model){
64
83
  return this.generate({ model,prompt:'',keep_alive:0 })
65
84
  }
85
+
86
+ [PUBLISH_OLLAMA_READY](){
87
+ const reason=OLLAMA_REASONS.ready;
88
+ const {occurrence}=this.#events.dispatch(
89
+ OLLAMA_EVENT_TYPES.ready,
90
+ Object.freeze({ollama:this,reason}),
91
+ {
92
+ operationId:`${this.#events.instanceId}:ready:1`,
93
+ publicDetail:Object.freeze({ready:true,reason})
94
+ }
95
+ )
96
+ if(typeof globalThis.CustomEvent==='function'
97
+ &&typeof globalThis.dispatchEvent==='function'){
98
+ projectArcaneDOMEvent(globalThis,occurrence)
99
+ }
100
+ }
66
101
  }
67
102
 
68
103
  export const ollama=Object.freeze(new Ollama())
@@ -71,4 +106,4 @@ export default ollama
71
106
  if(!Object.prototype.hasOwnProperty.call(globalThis,'arcaneOllama')){
72
107
  Object.defineProperty(globalThis,'arcaneOllama',{ value:ollama,enumerable:true,configurable:false,writable:false })
73
108
  }
74
- globalThis.dispatchEvent?.(new CustomEvent('arcane-ollama-ready',{ detail:{ ollama } }))
109
+ ollama[PUBLISH_OLLAMA_READY]()