{"_id":"@elgin-white/sail-desktop-agent","name":"@elgin-white/sail-desktop-agent","dist-tags":{"beta":"0.0.1-beta.0","latest":"0.0.1-beta.0"},"versions":{"0.0.1-beta.0":{"name":"@elgin-white/sail-desktop-agent","version":"0.0.1-beta.0","type":"module","description":"FDC3 Desktop Agent implementation for Sail","main":"./dist/index.mjs","module":"./dist/index.mjs","types":"./dist/index.d.mts","exports":{".":{"import":"./dist/index.mjs","types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./browser":{"import":"./dist/browser/index.mjs","types":"./dist/browser/index.d.mts","default":"./dist/browser/index.mjs"},"./transports":{"import":"./dist/transports/index.mjs","types":"./dist/transports/index.d.mts","default":"./dist/transports/index.mjs"}},"sideEffects":false,"scripts":{"build":"tsdown","dev":"tsdown --watch","typecheck":"tsc","lint":"eslint .","lint:fix":"eslint . --fix","format":"prettier --check .","format:fix":"prettier --write .","test":"vitest run && cucumber-js","test:cucumber":"cucumber-js","test:cucumber:report":"FORCE_COLOR=false cucumber-js --publish","test:cucumber:watch":"cucumber-js --watch","validate":"npm run typecheck && npm run lint && npm run format"},"devDependencies":{"@cucumber/cucumber":"^12.5.0","@types/lodash":"^4.17.24","expect":"^30.2.0","lodash":"^4.17.23","tsx":"^4.21.0"},"dependencies":{"@finos/fdc3":"^2.2.0","@finos/fdc3-schema":"^2.2.1-beta.3","immer":"^10.1.1"},"author":"","license":"Apache-2.0","homepage":"https://github.com/finos/FDC3-Sail","repository":{"type":"git","url":"git+https://github.com/finos/FDC3-Sail.git"},"bugs":{"url":"https://github.com/finos/FDC3-Sail/issues"},"publishConfig":{"access":"public"},"_id":"@elgin-white/sail-desktop-agent@0.0.1-beta.0","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-J2czJO7NW0rJqwxJDaPtBnruFQa6mkhwBgAA0F8n3ire9ADxWJJ20NI6mBsU4iKfNHTI84IY3NFyOqBt+cNY8g==","shasum":"4e5c8c2fadabc6118ce716f1633932e99a8dfa84","tarball":"https://registry.npmjs.org/@elgin-white/sail-desktop-agent/-/sail-desktop-agent-0.0.1-beta.0.tgz","fileCount":20,"unpackedSize":797932,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEfccFpQF23ioitFPuJLiRJctrCrqPkSz4U0+As7+dv9AiEAop6RIRy3qFRGfgvcwpiJY/g+hH9ePYmIvpkL6Rshhc4="}]},"_npmUser":{"name":"cwatson1988","email":"cwatson1988@gmail.com"},"directories":{},"maintainers":[{"name":"cwatson1988","email":"cwatson1988@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sail-desktop-agent_0.0.1-beta.0_1778068319221_0.42697494289414806"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-06T11:51:59.090Z","0.0.1-beta.0":"2026-05-06T11:51:59.430Z","modified":"2026-05-06T11:51:59.698Z"},"maintainers":[{"name":"cwatson1988","email":"cwatson1988@gmail.com"}],"description":"FDC3 Desktop Agent implementation for Sail","homepage":"https://github.com/finos/FDC3-Sail","repository":{"type":"git","url":"git+https://github.com/finos/FDC3-Sail.git"},"bugs":{"url":"https://github.com/finos/FDC3-Sail/issues"},"license":"Apache-2.0","readme":"# FDC3 Sail Desktop Agent\r\n\r\nA pure, transport-agnostic FDC3 Desktop Agent implementation that supports the complete Desktop Agent Communication Protocol (DACP) specification.\r\n\r\n## Overview\r\n\r\nThis package provides a production-ready FDC3 Desktop Agent that manages application instances, channels, intents, and private channels according to the [FDC3 2.2 specification](https://fdc3.finos.org/docs/api/spec).\r\n\r\n**Key Features:**\r\n\r\n- ✅ **Full FDC3 2.2 Compliance**: All mandatory Desktop Agent APIs implemented\r\n- ✅ **Transport Agnostic**: Core has zero transport dependencies - works with any message transport\r\n- ✅ **Environment Agnostic**: Runs in browser, Node.js, Web Worker, or any JavaScript runtime\r\n- ✅ **WCP Support**: Full Web Connection Protocol (WCP1-6) implementation for browser apps\r\n- ✅ **Flexible Deployment**: Same code runs locally, on server, or in worker\r\n- ✅ **Type Safety**: Built with TypeScript and Zod validation\r\n\r\n## Architecture\r\n\r\nThe package follows a clean three-layer architecture:\r\n\r\n```\r\n┌─────────────────────────────────────────────────────────────────────────┐\r\n│  Browser Apps (iframes)                                                 │\r\n│  Using @finos/fdc3-get-agent                                           │\r\n│  fdc3.raiseIntent(), fdc3.broadcast(), etc.                            │\r\n└────────────────────────────────┬────────────────────────────────────────┘\r\n                                 │ MessagePort (WCP)\r\n                                 ▼\r\n┌─────────────────────────────────────────────────────────────────────────┐\r\n│  WCPConnector (Browser only)                                            │\r\n│  - Handles WCP1-3 handshake with iframe apps                           │\r\n│  - Manages MessagePorts per app                                         │\r\n│  - Bridges to Transport                                                 │\r\n└────────────────────────────────┬────────────────────────────────────────┘\r\n                                 │ Transport (swappable)\r\n                                 ▼\r\n┌─────────────────────────────────────────────────────────────────────────┐\r\n│  DesktopAgent (runs anywhere)                                           │\r\n│  - Pure FDC3 logic, zero environment dependencies                       │\r\n│  - DACP message handlers                                                │\r\n│  - State registries (apps, channels, intents)                          │\r\n└─────────────────────────────────────────────────────────────────────────┘\r\n```\r\n\r\n### Directory Structure\r\n\r\n```\r\npackages/sail-desktop-agent/\r\n├── src/\r\n│   ├── core/                      # Pure FDC3 Desktop Agent (environment-agnostic)\r\n│   │   ├── sail-desktop-agent.ts       # Main DesktopAgent class\r\n│   │   ├── handlers/              # DACP message handlers\r\n│   │   │   └── dacp/              # All FDC3 operation handlers\r\n│   │   ├── state/                 # State registries\r\n│   │   │   ├── app-instance-registry.ts\r\n│   │   │   ├── intent-registry.ts\r\n│   │   │   ├── channel-context-registry.ts\r\n│   │   │   └── ...\r\n│   │   ├── interfaces/            # Transport & AppLauncher interfaces\r\n│   │   └── app-directory/         # FDC3 App Directory management\r\n│   ├── browser/                   # Browser-specific code\r\n│   │   ├── browser-sail-desktop-agent.ts  # Factory functions\r\n│   │   └── wcp/                   # WCP implementation\r\n│   │       ├── wcp-connector.ts   # WCP1-6 protocol handler\r\n│   │       └── message-port-transport.ts\r\n│   └── transports/                # Transport implementations\r\n│       └── in-memory-transport.ts # For same-process communication\r\n└── test/                          # Cucumber BDD tests\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install @finos/fdc3-sail-desktop-agent\r\n```\r\n\r\n## Quick Start\r\n\r\n### Browser Mode (Desktop Agent in same window)\r\n\r\nUse when Desktop Agent runs in the browser alongside your UI:\r\n\r\n```typescript\r\nimport { createBrowserDesktopAgent } from \"@finos/fdc3-sail-desktop-agent/browser\"\r\n\r\nconst { desktopAgent, wcpConnector, start, stop } = createBrowserDesktopAgent({\r\n  wcpOptions: {\r\n    // Return false for Sail-controlled UI (recommended)\r\n    getIntentResolverUrl: () => false,\r\n    getChannelSelectorUrl: () => false,\r\n  },\r\n  appDirectories: [\"https://example.com/apps.json\"],\r\n})\r\n\r\n// Start Desktop Agent and WCP Connector\r\nstart()\r\n\r\n// Apps in iframes can now connect via fdc3.getAgent()\r\n```\r\n\r\n### Server Mode (Desktop Agent on server)\r\n\r\nUse when Desktop Agent runs on a Node.js server:\r\n\r\n```typescript\r\n// Browser client\r\nimport { createWCPClient } from \"@finos/fdc3-sail-desktop-agent/browser\"\r\nimport { SocketIOClientTransport } from \"@finos/sail-platform-api\"\r\n\r\nconst transport = new SocketIOClientTransport({\r\n  url: \"wss://your-server.com\",\r\n  auth: { userId: \"user123\" },\r\n})\r\n\r\nconst { wcpConnector, start } = createWCPClient({\r\n  transport,\r\n  wcpOptions: {\r\n    getIntentResolverUrl: () => false,\r\n    getChannelSelectorUrl: () => false,\r\n  },\r\n})\r\n\r\nstart()\r\n// Apps connect via WCP, messages flow to server\r\n```\r\n\r\n```typescript\r\n// Server\r\nimport { DesktopAgent } from \"@finos/fdc3-sail-desktop-agent\"\r\nimport { SocketIOServerTransport } from \"@finos/sail-platform-api\"\r\n\r\nconst transport = new SocketIOServerTransport(io, userId)\r\nconst agent = new DesktopAgent({ transport })\r\nagent.start()\r\n```\r\n\r\n### Worker Mode (Desktop Agent in Web Worker)\r\n\r\nUse when Desktop Agent runs in a Web Worker for isolation:\r\n\r\n```typescript\r\n// Main thread\r\nimport { createWCPClient } from \"@finos/fdc3-sail-desktop-agent/browser\"\r\nimport { WebWorkerTransport } from \"@finos/sail-platform-api\"\r\n\r\nconst worker = new Worker(\"sail-desktop-agent-worker.js\")\r\nconst transport = new WebWorkerTransport(worker)\r\n\r\nconst { wcpConnector, start } = createWCPClient({ transport })\r\nstart()\r\n```\r\n\r\n### Manual Composition (Advanced)\r\n\r\nFor full control over component setup:\r\n\r\n```typescript\r\nimport { DesktopAgent } from \"@finos/fdc3-sail-desktop-agent\"\r\nimport { WCPConnector } from \"@finos/fdc3-sail-desktop-agent/browser\"\r\nimport { createInMemoryTransportPair } from \"@finos/fdc3-sail-desktop-agent/transports\"\r\n\r\n// Create linked transport pair\r\nconst [daTransport, wcpTransport] = createInMemoryTransportPair()\r\n\r\n// Create Desktop Agent\r\nconst desktopAgent = new DesktopAgent({\r\n  transport: daTransport,\r\n  appLauncher: myAppLauncher,\r\n  requestIntentResolution: myIntentResolver,\r\n})\r\n\r\n// Create WCP Connector\r\nconst wcpConnector = new WCPConnector(wcpTransport, {\r\n  getIntentResolverUrl: () => false,\r\n  getChannelSelectorUrl: () => false,\r\n})\r\n\r\n// Start both\r\ndesktopAgent.start()\r\nwcpConnector.start()\r\n```\r\n\r\n## FDC3 API Coverage\r\n\r\n### Context Management\r\n\r\n- `broadcast()` - Broadcast context to channel\r\n- `addContextListener()` - Listen for context on channels\r\n- `getCurrentContext()` - Get current context for channel\r\n\r\n### Channel Management\r\n\r\n- `getCurrentChannel()` - Get current user channel\r\n- `joinUserChannel()` - Join user channel\r\n- `leaveCurrentChannel()` - Leave current channel\r\n- `getUserChannels()` - Get available user channels\r\n- `getOrCreateChannel()` - Get or create app channel\r\n\r\n### Intent Management\r\n\r\n- `raiseIntent()` - Raise intent with optional target\r\n- `raiseIntentForContext()` - Raise intent by context type\r\n- `addIntentListener()` - Listen for specific intents\r\n- `findIntent()` - Find handlers for intent\r\n- `findIntentsByContext()` - Find intents for context type\r\n\r\n### App Management\r\n\r\n- `getInfo()` - Get desktop agent metadata\r\n- `open()` - Launch applications\r\n- `findInstances()` - Find running app instances\r\n- `getAppMetadata()` - Get app metadata from directory\r\n\r\n### Private Channels\r\n\r\n- `createPrivateChannel()` - Create private channels\r\n- Private channel context listeners\r\n- Private channel disconnect handling\r\n\r\n## Protocol Support\r\n\r\n### DACP (Desktop Agent Communication Protocol)\r\n\r\nAll FDC3 operations use DACP messages. The Desktop Agent handles:\r\n\r\n**Request Messages:** `broadcastRequest`, `raiseIntentRequest`, `addContextListenerRequest`, `joinUserChannelRequest`, `openRequest`, `findIntentRequest`, etc.\r\n\r\n**Response Messages:** Automatic response generation with proper error handling.\r\n\r\n**Event Messages:** `contextEvent`, `intentEvent`, `listenerEvent` for async notifications.\r\n\r\n### WCP (Web Connection Protocol)\r\n\r\nBrowser app connection handshake:\r\n\r\n- **WCP1Hello** - App initiates connection\r\n- **WCP3Handshake** - Desktop Agent responds with MessagePort\r\n- **WCP4ValidateAppIdentity** - App validates identity\r\n- **WCP5ValidateAppIdentityResponse** - Desktop Agent confirms\r\n- **WCP6Goodbye** - App disconnects gracefully\r\n\r\n## Transport Interface\r\n\r\nThe Desktop Agent works with any transport implementing this interface:\r\n\r\n```typescript\r\ninterface Transport {\r\n  send(message: unknown): void\r\n  onMessage(handler: (message: unknown) => void): void\r\n  onDisconnect(handler: () => void): void\r\n  isConnected(): boolean\r\n  getInstanceId(): string | null\r\n  disconnect(): void\r\n}\r\n```\r\n\r\n**Built-in Transports:**\r\n\r\n- `InMemoryTransport` - Same-process communication\r\n- `MessagePortTransport` - Browser MessagePort API\r\n\r\n**Platform SDK Transports:**\r\n\r\n- `SocketIOClientTransport` - Browser to server\r\n- `SocketIOServerTransport` - Server-side Socket.IO\r\n\r\n## Testing\r\n\r\n### Running Tests\r\n\r\n```bash\r\n# Run Cucumber BDD tests\r\nnpm run test:cucumber --workspace=@finos/fdc3-sail-desktop-agent\r\n\r\n# Run unit tests\r\nnpm run test --workspace=@finos/fdc3-sail-desktop-agent\r\n\r\n# Type checking\r\nnpm run typecheck --workspace=@finos/fdc3-sail-desktop-agent\r\n```\r\n\r\n### Test Architecture\r\n\r\nTests use a `MockTransport` that simulates DACP message flow:\r\n\r\n```typescript\r\n// Tests send DACP messages directly to the transport\r\nconst message: RaiseIntentRequest = {\r\n  type: 'raiseIntentRequest',\r\n  meta: { source: { instanceId: 'app-1' }, ... },\r\n  payload: { intent: 'ViewChart', context: {...} }\r\n}\r\n\r\nawait mockTransport.receiveMessage(message)\r\n\r\n// Verify responses\r\nconst responses = mockTransport.getMessagesByType('raiseIntentResponse')\r\nexpect(responses[0].payload.resolution).toBeDefined()\r\n```\r\n\r\n## Development\r\n\r\n### Building\r\n\r\n```bash\r\nnpm run build --workspace=@finos/fdc3-sail-desktop-agent\r\n```\r\n\r\n### Key Design Principles\r\n\r\n1. **Pure Core**: `DesktopAgent` has zero browser/Node.js dependencies\r\n2. **Transport Abstraction**: All communication via `Transport` interface\r\n3. **WCP in Browser Only**: `WCPConnector` handles browser-specific concerns\r\n4. **Message-Driven Cleanup**: App disconnects flow through transport (WCP6Goodbye)\r\n\r\n### Adding New DACP Handlers\r\n\r\n1. Create handler in `src/core/handlers/dacp/`:\r\n\r\n```typescript\r\nexport function handleNewFeatureRequest(message: unknown, context: DACPHandlerContext): void {\r\n  // Validate, process, send response\r\n}\r\n```\r\n\r\n2. Register in `src/core/handlers/dacp/index.ts`:\r\n\r\n```typescript\r\nconst handlerMap = {\r\n  // ...existing handlers\r\n  newFeatureRequest: newHandlers.handleNewFeatureRequest,\r\n}\r\n```\r\n\r\n3. Add Cucumber tests in `test/features/` and `test/step-definitions/`.\r\n\r\n## Dependencies\r\n\r\n### Runtime\r\n\r\n- `@finos/fdc3` - Official FDC3 types\r\n- `@finos/fdc3-schema` - FDC3 JSON schemas and type guards\r\n\r\n### Peer Dependencies\r\n\r\n- `zod` - Runtime validation (optional, for schema validation)\r\n\r\n## License\r\n\r\nApache-2.0\r\n\r\n## Related\r\n\r\n- [FDC3 Specification](https://fdc3.finos.org/docs/api/spec)\r\n- [@finos/fdc3-get-agent](https://www.npmjs.com/package/@finos/fdc3) - Browser-side FDC3 API\r\n- [Sail Platform API](../sail-platform-api/) - Middleware, app launcher, and Sail integrations\r\n","readmeFilename":"README.md","_rev":"1-60a0700e495a8cce421ac7893f61ecfc"}