@stina/extension-api 0.20.0 → 0.22.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.
- package/dist/{chunk-WIDGIYRV.js → chunk-U3PEHSBG.js} +1 -1
- package/dist/chunk-U3PEHSBG.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -6
- package/dist/index.d.ts +67 -6
- package/dist/index.js +1 -1
- package/dist/runtime.cjs +242 -0
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.js +243 -1
- package/dist/runtime.js.map +1 -1
- package/dist/{types.tools-DgCozsOW.d.cts → types.tools-BXGZf8zc.d.cts} +208 -1
- package/dist/{types.tools-DgCozsOW.d.ts → types.tools-BXGZf8zc.d.ts} +208 -1
- package/package.json +1 -1
- package/src/background.test.ts +525 -0
- package/src/background.ts +261 -0
- package/src/index.ts +15 -0
- package/src/messages.ts +71 -0
- package/src/runtime.ts +113 -0
- package/src/types.components.ts +21 -0
- package/src/types.context.ts +217 -0
- package/src/types.permissions.ts +1 -0
- package/src/types.ts +7 -0
- package/dist/chunk-WIDGIYRV.js.map +0 -1
package/src/types.context.ts
CHANGED
|
@@ -104,6 +104,9 @@ export interface ExtensionContext {
|
|
|
104
104
|
/** Local storage (if permitted) */
|
|
105
105
|
readonly storage?: StorageAPI
|
|
106
106
|
|
|
107
|
+
/** Background workers (if permitted) */
|
|
108
|
+
readonly backgroundWorkers?: BackgroundWorkersAPI
|
|
109
|
+
|
|
107
110
|
/** Logging (always available) */
|
|
108
111
|
readonly log: LogAPI
|
|
109
112
|
}
|
|
@@ -397,3 +400,217 @@ export interface ExtensionModule {
|
|
|
397
400
|
*/
|
|
398
401
|
deactivate?(): void | Promise<void>
|
|
399
402
|
}
|
|
403
|
+
|
|
404
|
+
// ============================================================================
|
|
405
|
+
// Background Workers API
|
|
406
|
+
// ============================================================================
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Restart policy for background tasks.
|
|
410
|
+
* Controls how tasks are restarted after failures.
|
|
411
|
+
*/
|
|
412
|
+
export interface BackgroundRestartPolicy {
|
|
413
|
+
/**
|
|
414
|
+
* When to restart the task:
|
|
415
|
+
* - 'always': Always restart, regardless of exit reason
|
|
416
|
+
* - 'on-failure': Only restart if the task threw an error
|
|
417
|
+
* - 'never': Never restart automatically
|
|
418
|
+
*/
|
|
419
|
+
type: 'always' | 'on-failure' | 'never'
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Maximum number of restarts before giving up.
|
|
423
|
+
* 0 means unlimited restarts.
|
|
424
|
+
* @default 0
|
|
425
|
+
*/
|
|
426
|
+
maxRestarts?: number
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Initial delay in milliseconds before first restart.
|
|
430
|
+
* @default 1000
|
|
431
|
+
*/
|
|
432
|
+
initialDelayMs?: number
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Maximum delay in milliseconds between restarts.
|
|
436
|
+
* @default 60000
|
|
437
|
+
*/
|
|
438
|
+
maxDelayMs?: number
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Multiplier for exponential backoff.
|
|
442
|
+
* @default 2
|
|
443
|
+
*/
|
|
444
|
+
backoffMultiplier?: number
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Configuration for a background task.
|
|
449
|
+
*/
|
|
450
|
+
export interface BackgroundTaskConfig {
|
|
451
|
+
/**
|
|
452
|
+
* Unique identifier for the task within the extension.
|
|
453
|
+
* Used to reference the task for stopping or checking status.
|
|
454
|
+
*/
|
|
455
|
+
id: string
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Human-readable name for observability and logging.
|
|
459
|
+
*/
|
|
460
|
+
name: string
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* User ID that owns this task.
|
|
464
|
+
* Background tasks are always user-scoped.
|
|
465
|
+
*/
|
|
466
|
+
userId: string
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Policy for restarting the task after failures.
|
|
470
|
+
*/
|
|
471
|
+
restartPolicy: BackgroundRestartPolicy
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Optional payload data passed to the task callback.
|
|
475
|
+
*/
|
|
476
|
+
payload?: Record<string, unknown>
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Context provided to background task callbacks.
|
|
481
|
+
* Extends ExecutionContext with task-specific functionality.
|
|
482
|
+
*/
|
|
483
|
+
export interface BackgroundTaskContext extends ExecutionContext {
|
|
484
|
+
/**
|
|
485
|
+
* AbortSignal that is triggered when the task should stop.
|
|
486
|
+
* Check this signal regularly and exit gracefully when aborted.
|
|
487
|
+
*/
|
|
488
|
+
readonly signal: AbortSignal
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Report the current health status of the task.
|
|
492
|
+
* Use this to provide observability into what the task is doing.
|
|
493
|
+
*
|
|
494
|
+
* @param status Human-readable status message
|
|
495
|
+
*/
|
|
496
|
+
reportHealth(status: string): void
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Task-specific logging API.
|
|
500
|
+
* Messages are tagged with the task ID for easier debugging.
|
|
501
|
+
*/
|
|
502
|
+
readonly log: LogAPI
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Callback function for background tasks.
|
|
507
|
+
* The function should run until the signal is aborted, then clean up and return.
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```typescript
|
|
511
|
+
* const callback: BackgroundTaskCallback = async (ctx) => {
|
|
512
|
+
* const connection = await createConnection()
|
|
513
|
+
* try {
|
|
514
|
+
* while (!ctx.signal.aborted) {
|
|
515
|
+
* ctx.reportHealth('Waiting for messages...')
|
|
516
|
+
* const message = await connection.receive({ signal: ctx.signal })
|
|
517
|
+
* await processMessage(message)
|
|
518
|
+
* }
|
|
519
|
+
* } finally {
|
|
520
|
+
* await connection.close()
|
|
521
|
+
* }
|
|
522
|
+
* }
|
|
523
|
+
* ```
|
|
524
|
+
*/
|
|
525
|
+
export type BackgroundTaskCallback = (context: BackgroundTaskContext) => Promise<void>
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Health status of a background task.
|
|
529
|
+
*/
|
|
530
|
+
export interface BackgroundTaskHealth {
|
|
531
|
+
/**
|
|
532
|
+
* Unique task identifier within the extension.
|
|
533
|
+
*/
|
|
534
|
+
taskId: string
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Human-readable task name.
|
|
538
|
+
*/
|
|
539
|
+
name: string
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* User ID that owns this task.
|
|
543
|
+
*/
|
|
544
|
+
userId: string
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Current task status.
|
|
548
|
+
*/
|
|
549
|
+
status: 'pending' | 'running' | 'stopped' | 'failed' | 'restarting'
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Number of times the task has been restarted.
|
|
553
|
+
*/
|
|
554
|
+
restartCount: number
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Last health status message reported by the task.
|
|
558
|
+
*/
|
|
559
|
+
lastHealthStatus?: string
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Timestamp of the last health report.
|
|
563
|
+
*/
|
|
564
|
+
lastHealthTime?: string
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Error message if the task failed.
|
|
568
|
+
*/
|
|
569
|
+
error?: string
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* API for managing background workers.
|
|
574
|
+
* Background workers are long-running tasks that can be automatically restarted.
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```typescript
|
|
578
|
+
* const task = await context.backgroundWorkers.start({
|
|
579
|
+
* id: 'my-task',
|
|
580
|
+
* name: 'My Background Task',
|
|
581
|
+
* userId: 'user-123',
|
|
582
|
+
* restartPolicy: { type: 'always', maxRestarts: 0 }
|
|
583
|
+
* }, async (ctx) => {
|
|
584
|
+
* while (!ctx.signal.aborted) {
|
|
585
|
+
* // Do work...
|
|
586
|
+
* }
|
|
587
|
+
* })
|
|
588
|
+
*
|
|
589
|
+
* // Later, to stop the task:
|
|
590
|
+
* task.dispose()
|
|
591
|
+
* ```
|
|
592
|
+
*/
|
|
593
|
+
export interface BackgroundWorkersAPI {
|
|
594
|
+
/**
|
|
595
|
+
* Start a new background task.
|
|
596
|
+
*
|
|
597
|
+
* @param config Task configuration
|
|
598
|
+
* @param callback Function to execute as the background task
|
|
599
|
+
* @returns Disposable that stops the task when disposed
|
|
600
|
+
*/
|
|
601
|
+
start(config: BackgroundTaskConfig, callback: BackgroundTaskCallback): Promise<Disposable>
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Stop a running background task.
|
|
605
|
+
*
|
|
606
|
+
* @param taskId The task ID to stop
|
|
607
|
+
*/
|
|
608
|
+
stop(taskId: string): Promise<void>
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Get the health status of all background tasks for this extension.
|
|
612
|
+
*
|
|
613
|
+
* @returns Array of task health statuses
|
|
614
|
+
*/
|
|
615
|
+
getStatus(): Promise<BackgroundTaskHealth[]>
|
|
616
|
+
}
|
package/src/types.permissions.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -95,4 +95,11 @@ export type {
|
|
|
95
95
|
StorageAPI,
|
|
96
96
|
LogAPI,
|
|
97
97
|
ExtensionModule,
|
|
98
|
+
// Background workers
|
|
99
|
+
BackgroundRestartPolicy,
|
|
100
|
+
BackgroundTaskConfig,
|
|
101
|
+
BackgroundTaskContext,
|
|
102
|
+
BackgroundTaskCallback,
|
|
103
|
+
BackgroundTaskHealth,
|
|
104
|
+
BackgroundWorkersAPI,
|
|
98
105
|
} from './types.context.js'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/messages.ts"],"sourcesContent":["/**\n * Message protocol between Extension Host and Extension Workers\n */\n\nimport type {\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolResult,\n ActionResult,\n ModelInfo,\n SchedulerFirePayload,\n} from './types.js'\n\n// ============================================================================\n// Host → Worker Messages\n// ============================================================================\n\nexport type HostToWorkerMessage =\n | ActivateMessage\n | DeactivateMessage\n | SettingsChangedMessage\n | SchedulerFireMessage\n | ProviderChatRequestMessage\n | ProviderModelsRequestMessage\n | ToolExecuteRequestMessage\n | ActionExecuteRequestMessage\n | ResponseMessage\n | StreamingFetchChunkMessage\n\nexport interface ActivateMessage {\n type: 'activate'\n id: string\n payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n }\n}\n\nexport interface DeactivateMessage {\n type: 'deactivate'\n id: string\n}\n\nexport interface SettingsChangedMessage {\n type: 'settings-changed'\n id: string\n payload: {\n key: string\n value: unknown\n }\n}\n\nexport interface SchedulerFireMessage {\n type: 'scheduler-fire'\n id: string\n payload: SchedulerFirePayload\n}\n\nexport interface ProviderChatRequestMessage {\n type: 'provider-chat-request'\n id: string\n payload: {\n providerId: string\n messages: ChatMessage[]\n options: ChatOptions\n }\n}\n\nexport interface ProviderModelsRequestMessage {\n type: 'provider-models-request'\n id: string\n payload: {\n providerId: string\n options?: GetModelsOptions\n }\n}\n\nexport interface ToolExecuteRequestMessage {\n type: 'tool-execute-request'\n id: string\n payload: {\n toolId: string\n params: Record<string, unknown>\n /** User ID if the tool is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ActionExecuteRequestMessage {\n type: 'action-execute-request'\n id: string\n payload: {\n actionId: string\n params: Record<string, unknown>\n /** User ID if the action is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ResponseMessage {\n type: 'response'\n id: string\n payload: {\n requestId: string\n success: boolean\n data?: unknown\n error?: string\n }\n}\n\n/**\n * Message sent from host to worker with streaming fetch data chunks.\n * Used for streaming network responses (e.g., NDJSON streams from Ollama).\n */\nexport interface StreamingFetchChunkMessage {\n type: 'streaming-fetch-chunk'\n id: string\n payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n }\n}\n\n// ============================================================================\n// Worker → Host Messages\n// ============================================================================\n\nexport type WorkerToHostMessage =\n | ReadyMessage\n | RequestMessage\n | ProviderRegisteredMessage\n | ToolRegisteredMessage\n | ActionRegisteredMessage\n | StreamEventMessage\n | LogMessage\n | ProviderModelsResponseMessage\n | ToolExecuteResponseMessage\n | ActionExecuteResponseMessage\n | StreamingFetchAckMessage\n\nexport interface ReadyMessage {\n type: 'ready'\n}\n\n/**\n * Message sent from worker to host to acknowledge receipt of a streaming fetch chunk.\n * This enables backpressure control to prevent unbounded memory growth.\n */\nexport interface StreamingFetchAckMessage {\n type: 'streaming-fetch-ack'\n payload: {\n requestId: string\n }\n}\n\nexport interface RequestMessage {\n type: 'request'\n id: string\n method: RequestMethod\n payload: unknown\n}\n\nexport type RequestMethod =\n | 'network.fetch'\n | 'network.fetch-stream'\n | 'settings.getAll'\n | 'settings.get'\n | 'settings.set'\n | 'user.getProfile'\n | 'events.emit'\n | 'scheduler.schedule'\n | 'scheduler.cancel'\n | 'chat.appendInstruction'\n | 'database.execute'\n | 'storage.get'\n | 'storage.set'\n | 'storage.delete'\n | 'storage.keys'\n | 'storage.getForUser'\n | 'storage.setForUser'\n | 'storage.deleteForUser'\n | 'storage.keysForUser'\n\nexport interface ProviderRegisteredMessage {\n type: 'provider-registered'\n payload: {\n id: string\n name: string\n }\n}\n\nexport interface ToolRegisteredMessage {\n type: 'tool-registered'\n payload: {\n id: string\n name: string\n description: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface ActionRegisteredMessage {\n type: 'action-registered'\n payload: {\n id: string\n }\n}\n\nexport interface StreamEventMessage {\n type: 'stream-event'\n payload: {\n requestId: string\n event: StreamEvent\n }\n}\n\nexport interface ProviderModelsResponseMessage {\n type: 'provider-models-response'\n payload: {\n requestId: string\n models: ModelInfo[]\n error?: string\n }\n}\n\nexport interface ToolExecuteResponseMessage {\n type: 'tool-execute-response'\n payload: {\n requestId: string\n result: ToolResult\n error?: string\n }\n}\n\nexport interface ActionExecuteResponseMessage {\n type: 'action-execute-response'\n payload: {\n requestId: string\n result: ActionResult\n error?: string\n }\n}\n\nexport interface LogMessage {\n type: 'log'\n payload: {\n level: 'debug' | 'info' | 'warn' | 'error'\n message: string\n data?: Record<string, unknown>\n }\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\nexport interface PendingRequest<T = unknown> {\n resolve: (value: T) => void\n reject: (error: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n}\n"],"mappings":";;;;;;;;AAgRO,SAAS,oBAA4B;AAC1C,SAAO,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACjE;","names":[]}
|