From 8ceec521eb539c3d9de5349eb87a96b5da82a94f Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Tue, 5 May 2026 12:03:46 +0000 Subject: [PATCH] Add interfaces package --- .gitignore | 13 ++++++++ package.json | 41 +++++++++++++++++++++++ readme.md | 3 ++ test/test.node.ts | 9 +++++ ts/data/agent.ts | 34 +++++++++++++++++++ ts/data/audit.ts | 14 ++++++++ ts/data/automation.ts | 28 ++++++++++++++++ ts/data/dashboard.ts | 27 +++++++++++++++ ts/data/device.ts | 64 ++++++++++++++++++++++++++++++++++++ ts/data/home.ts | 34 +++++++++++++++++++ ts/data/index.ts | 7 ++++ ts/data/tool.ts | 74 ++++++++++++++++++++++++++++++++++++++++++ ts/index.ts | 4 +++ ts/plugins.ts | 9 +++++ ts/request/agent.ts | 15 +++++++++ ts/request/approval.ts | 32 ++++++++++++++++++ ts/request/device.ts | 17 ++++++++++ ts/request/home.ts | 14 ++++++++ ts/request/index.ts | 5 +++ ts/request/tool.ts | 28 ++++++++++++++++ tsconfig.json | 13 ++++++++ 21 files changed, 485 insertions(+) create mode 100644 .gitignore create mode 100644 package.json create mode 100644 readme.md create mode 100644 test/test.node.ts create mode 100644 ts/data/agent.ts create mode 100644 ts/data/audit.ts create mode 100644 ts/data/automation.ts create mode 100644 ts/data/dashboard.ts create mode 100644 ts/data/device.ts create mode 100644 ts/data/home.ts create mode 100644 ts/data/index.ts create mode 100644 ts/data/tool.ts create mode 100644 ts/index.ts create mode 100644 ts/plugins.ts create mode 100644 ts/request/agent.ts create mode 100644 ts/request/approval.ts create mode 100644 ts/request/device.ts create mode 100644 ts/request/home.ts create mode 100644 ts/request/index.ts create mode 100644 ts/request/tool.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4336e0e --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +node_modules/ +dist/ +dist_*/ +dist_ts/ +coverage/ +.nyc_output/ +.nogit/ +.playwright-mcp/ +*.log +.DS_Store +.env +.env.* +!.env.example diff --git a/package.json b/package.json new file mode 100644 index 0000000..19ca37c --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "@smarthome.exchange/interfaces", + "version": "0.1.0", + "private": false, + "description": "Shared TypeScript interfaces and TypedRequest contracts for smarthome.exchange.", + "exports": { + ".": "./dist_ts/index.js" + }, + "type": "module", + "author": "Task Venture Capital GmbH", + "license": "MIT", + "scripts": { + "test": "tstest test/ --verbose --logfile --timeout 60", + "build": "tsbuild tsfolders --allowimplicitany", + "buildDocs": "tsdoc" + }, + "dependencies": { + "@api.global/typedrequest-interfaces": "^3.0.19", + "@tsclass/tsclass": "^9.5.1" + }, + "devDependencies": { + "@git.zone/tsbuild": "^4.4.0", + "@git.zone/tsdoc": "^2.0.3", + "@git.zone/tsrun": "^2.0.3", + "@git.zone/tstest": "^3.6.3", + "@types/node": "^25.6.0" + }, + "files": [ + "ts/**/*", + "dist/**/*", + "dist_*/**/*", + "dist_ts/**/*", + "readme.md", + "changelog.md", + "license" + ], + "browserslist": [ + "last 1 chrome versions" + ], + "packageManager": "pnpm@10.28.2" +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..59be203 --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +# @smarthome.exchange/interfaces + +Shared data interfaces and TypedRequest contracts for smarthome.exchange packages. diff --git a/test/test.node.ts b/test/test.node.ts new file mode 100644 index 0000000..dfb7d67 --- /dev/null +++ b/test/test.node.ts @@ -0,0 +1,9 @@ +import { expect, tap } from '@git.zone/tstest/tapbundle'; +import * as shxInterfaces from '../ts/index.js'; + +tap.test('exports data and request namespaces', async () => { + expect(shxInterfaces.data).toBeTruthy(); + expect(shxInterfaces.request).toBeTruthy(); +}); + +export default tap.start(); diff --git a/ts/data/agent.ts b/ts/data/agent.ts new file mode 100644 index 0000000..6742d89 --- /dev/null +++ b/ts/data/agent.ts @@ -0,0 +1,34 @@ +import type { TToolScope } from './tool.js'; + +export type TAgentMode = 'auto' | 'ask' | 'suggest'; + +export type TAgentGlyph = 'comfort' | 'sentinel' | 'watt' | 'dawn' | 'echo' | 'steward' | 'custom'; + +export interface IAgentDefinition { + id: string; + name: string; + role: string; + glyph: TAgentGlyph; + color: string; + mode: TAgentMode; + model: string; + scopes: TToolScope[]; + systemPrompt: string; + enabled: boolean; +} + +export interface IAgentStatus { + agentId: string; + actionsToday: number; + latest?: string; + savings?: string; + runningToolIds: string[]; +} + +export interface IAgentReasoningEvent { + id: string; + agentId: string; + kind: 'observe' | 'think' | 'plan' | 'tool' | 'speak'; + text: string; + createdAt: string; +} diff --git a/ts/data/audit.ts b/ts/data/audit.ts new file mode 100644 index 0000000..1143790 --- /dev/null +++ b/ts/data/audit.ts @@ -0,0 +1,14 @@ +export type TAuditReceiptKind = 'tool' | 'approval' | 'automation' | 'agent' | 'system'; + +export interface IAuditReceipt { + id: string; + kind: TAuditReceiptKind; + callerId: string; + toolId?: string; + approvalId?: string; + scope?: string; + inputSummary: string; + outputSummary: string; + reversible: boolean; + createdAt: string; +} diff --git a/ts/data/automation.ts b/ts/data/automation.ts new file mode 100644 index 0000000..099a539 --- /dev/null +++ b/ts/data/automation.ts @@ -0,0 +1,28 @@ +import type { TToolScope } from './tool.js'; + +export type TAutomationTriggerKind = 'time' | 'device' | 'presence' | 'calendar' | 'manual' | 'system'; + +export interface IAutomationTrigger { + id: string; + kind: TAutomationTriggerKind; + expression: string; +} + +export interface IAutomationDefinition { + id: string; + name: string; + sourcePath: string; + enabled: boolean; + triggers: IAutomationTrigger[]; + requiredScopes: TToolScope[]; +} + +export interface IAutomationRun { + id: string; + automationId: string; + triggerId: string; + status: 'running' | 'completed' | 'failed'; + startedAt: string; + finishedAt?: string; + errorMessage?: string; +} diff --git a/ts/data/dashboard.ts b/ts/data/dashboard.ts new file mode 100644 index 0000000..709f268 --- /dev/null +++ b/ts/data/dashboard.ts @@ -0,0 +1,27 @@ +export type TDashboardWidgetKind = + | 'agentRoster' + | 'approvalQueue' + | 'deviceGrid' + | 'floorPlan' + | 'timeline' + | 'activityStream' + | 'energyChart' + | 'custom'; + +export interface IDashboardWidget { + id: string; + kind: TDashboardWidgetKind; + title: string; + x: number; + y: number; + w: number; + h: number; + config: Record; +} + +export interface IDashboardDefinition { + id: string; + homeId: string; + name: string; + widgets: IDashboardWidget[]; +} diff --git a/ts/data/device.ts b/ts/data/device.ts new file mode 100644 index 0000000..14ed7db --- /dev/null +++ b/ts/data/device.ts @@ -0,0 +1,64 @@ +export type TDeviceProtocol = + | 'matter' + | 'zigbee' + | 'zwave' + | 'thread' + | 'mqtt' + | 'homekit' + | 'esphome' + | 'homeassistant' + | 'hue' + | 'http' + | 'cloud' + | 'virtual' + | 'unknown'; + +export type TDeviceCapability = + | 'switch' + | 'sensor' + | 'light' + | 'cover' + | 'lock' + | 'fan' + | 'climate' + | 'camera' + | 'media' + | 'energy' + | 'notification'; + +export type TDeviceStateValue = string | number | boolean | null | Record; + +export interface IDeviceFeature { + id: string; + capability: TDeviceCapability; + name: string; + readable: boolean; + writable: boolean; + unit?: string; +} + +export interface IDeviceState { + featureId: string; + value: TDeviceStateValue; + updatedAt: string; +} + +export interface IDeviceDefinition { + id: string; + integrationDomain?: string; + name: string; + room?: string; + protocol: TDeviceProtocol; + manufacturer?: string; + model?: string; + online: boolean; + features: IDeviceFeature[]; + state: IDeviceState[]; + metadata?: Record; +} + +export interface IRoomDefinition { + id: string; + name: string; + floor?: string; +} diff --git a/ts/data/home.ts b/ts/data/home.ts new file mode 100644 index 0000000..95e0524 --- /dev/null +++ b/ts/data/home.ts @@ -0,0 +1,34 @@ +import type { IAgentDefinition, IAgentStatus } from './agent.js'; +import type { IAuditReceipt } from './audit.js'; +import type { IDashboardDefinition } from './dashboard.js'; +import type { IDeviceDefinition, IRoomDefinition } from './device.js'; +import type { IApprovalRequest } from './tool.js'; + +export interface IHomeMember { + id: string; + name: string; + initials: string; + present: boolean; + locationLabel?: string; + eta?: string; +} + +export interface IHomeDefinition { + id: string; + name: string; + address?: string; + timezone: string; + members: IHomeMember[]; + rooms: IRoomDefinition[]; +} + +export interface IHomeSnapshot { + home: IHomeDefinition; + devices: IDeviceDefinition[]; + agents: IAgentDefinition[]; + agentStatuses: IAgentStatus[]; + approvals: IApprovalRequest[]; + dashboards: IDashboardDefinition[]; + receipts: IAuditReceipt[]; + createdAt: string; +} diff --git a/ts/data/index.ts b/ts/data/index.ts new file mode 100644 index 0000000..e971bcc --- /dev/null +++ b/ts/data/index.ts @@ -0,0 +1,7 @@ +export * from './agent.js'; +export * from './audit.js'; +export * from './automation.js'; +export * from './dashboard.js'; +export * from './device.js'; +export * from './home.js'; +export * from './tool.js'; diff --git a/ts/data/tool.ts b/ts/data/tool.ts new file mode 100644 index 0000000..d79f128 --- /dev/null +++ b/ts/data/tool.ts @@ -0,0 +1,74 @@ +import type { TAgentMode } from './agent.js'; + +export type TToolScope = + | '*.read' + | 'device.read' + | 'device.write' + | 'light.read' + | 'light.write' + | 'climate.read' + | 'climate.write' + | 'energy.read' + | 'energy.write' + | 'lock.read' + | 'lock.write' + | 'camera.read' + | 'alarm.write' + | 'agent.invoke' + | 'automation.write' + | 'approval.write'; + +export type TToolJsonSchema = Record; + +export interface IToolDefinition { + id: string; + ownerId: string; + name: string; + description: string; + inputSchema: TToolJsonSchema; + requiredScopes: TToolScope[]; + mode: TAgentMode; +} + +export interface IToolCall { + id: string; + toolId: string; + callerId: string; + input: Record; +} + +export interface IToolPlan { + id: string; + callerId: string; + title: string; + reason: string; + confidence: number; + calls: IToolCall[]; +} + +export interface IToolExecutionResult { + callId: string; + status: 'executed' | 'queuedForApproval' | 'suggested' | 'failed'; + output?: Record; + approvalId?: string; + errorMessage?: string; +} + +export interface IToolPlanResult { + planId: string; + results: IToolExecutionResult[]; +} + +export interface IApprovalRequest { + id: string; + planId: string; + callId: string; + agentId: string; + title: string; + reason: string; + confidence: number; + requestedScopes: TToolScope[]; + status: 'pending' | 'approved' | 'rejected' | 'expired'; + createdAt: string; + decidedAt?: string; +} diff --git a/ts/index.ts b/ts/index.ts new file mode 100644 index 0000000..6a19010 --- /dev/null +++ b/ts/index.ts @@ -0,0 +1,4 @@ +import * as data from './data/index.js'; +import * as request from './request/index.js'; + +export { data, request }; diff --git a/ts/plugins.ts b/ts/plugins.ts new file mode 100644 index 0000000..c64a5d2 --- /dev/null +++ b/ts/plugins.ts @@ -0,0 +1,9 @@ +// @api.global scope +import * as typedRequestInterfaces from '@api.global/typedrequest-interfaces'; + +export { typedRequestInterfaces }; + +// @tsclass scope +import * as tsclass from '@tsclass/tsclass'; + +export { tsclass }; diff --git a/ts/request/agent.ts b/ts/request/agent.ts new file mode 100644 index 0000000..04d16eb --- /dev/null +++ b/ts/request/agent.ts @@ -0,0 +1,15 @@ +import * as plugins from '../plugins.js'; +import * as data from '../data/index.js'; + +export interface IReq_ListAgents + extends plugins.typedRequestInterfaces.implementsTR< + plugins.typedRequestInterfaces.ITypedRequest, + IReq_ListAgents + > { + method: 'listAgents'; + request: {}; + response: { + agents: data.IAgentDefinition[]; + statuses: data.IAgentStatus[]; + }; +} diff --git a/ts/request/approval.ts b/ts/request/approval.ts new file mode 100644 index 0000000..4c52866 --- /dev/null +++ b/ts/request/approval.ts @@ -0,0 +1,32 @@ +import * as plugins from '../plugins.js'; +import * as data from '../data/index.js'; + +export interface IReq_ListApprovals + extends plugins.typedRequestInterfaces.implementsTR< + plugins.typedRequestInterfaces.ITypedRequest, + IReq_ListApprovals + > { + method: 'listApprovals'; + request: { + status?: data.IApprovalRequest['status']; + }; + response: { + approvals: data.IApprovalRequest[]; + }; +} + +export interface IReq_SubmitApproval + extends plugins.typedRequestInterfaces.implementsTR< + plugins.typedRequestInterfaces.ITypedRequest, + IReq_SubmitApproval + > { + method: 'submitApproval'; + request: { + approvalId: string; + decision: 'approved' | 'rejected'; + }; + response: { + approval: data.IApprovalRequest; + receipt: data.IAuditReceipt; + }; +} diff --git a/ts/request/device.ts b/ts/request/device.ts new file mode 100644 index 0000000..1abbbc0 --- /dev/null +++ b/ts/request/device.ts @@ -0,0 +1,17 @@ +import * as plugins from '../plugins.js'; +import * as data from '../data/index.js'; + +export interface IReq_ListDevices + extends plugins.typedRequestInterfaces.implementsTR< + plugins.typedRequestInterfaces.ITypedRequest, + IReq_ListDevices + > { + method: 'listDevices'; + request: { + room?: string; + capability?: data.TDeviceCapability; + }; + response: { + devices: data.IDeviceDefinition[]; + }; +} diff --git a/ts/request/home.ts b/ts/request/home.ts new file mode 100644 index 0000000..f5861f5 --- /dev/null +++ b/ts/request/home.ts @@ -0,0 +1,14 @@ +import * as plugins from '../plugins.js'; +import * as data from '../data/index.js'; + +export interface IReq_GetHomeSnapshot + extends plugins.typedRequestInterfaces.implementsTR< + plugins.typedRequestInterfaces.ITypedRequest, + IReq_GetHomeSnapshot + > { + method: 'getHomeSnapshot'; + request: {}; + response: { + snapshot: data.IHomeSnapshot; + }; +} diff --git a/ts/request/index.ts b/ts/request/index.ts new file mode 100644 index 0000000..0533793 --- /dev/null +++ b/ts/request/index.ts @@ -0,0 +1,5 @@ +export * from './agent.js'; +export * from './approval.js'; +export * from './device.js'; +export * from './home.js'; +export * from './tool.js'; diff --git a/ts/request/tool.ts b/ts/request/tool.ts new file mode 100644 index 0000000..7f28c85 --- /dev/null +++ b/ts/request/tool.ts @@ -0,0 +1,28 @@ +import * as plugins from '../plugins.js'; +import * as data from '../data/index.js'; + +export interface IReq_ListTools + extends plugins.typedRequestInterfaces.implementsTR< + plugins.typedRequestInterfaces.ITypedRequest, + IReq_ListTools + > { + method: 'listTools'; + request: { + ownerId?: string; + }; + response: { + tools: data.IToolDefinition[]; + }; +} + +export interface IReq_ExecuteToolPlan + extends plugins.typedRequestInterfaces.implementsTR< + plugins.typedRequestInterfaces.ITypedRequest, + IReq_ExecuteToolPlan + > { + method: 'executeToolPlan'; + request: { + plan: data.IToolPlan; + }; + response: data.IToolPlanResult; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7862634 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "verbatimModuleSyntax": true, + "types": ["node"] + }, + "exclude": [ + "dist_*/**/*.d.ts" + ] +}