Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .yarn/changelogs/common.53378eac.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!-- version-type: patch -->

# common

<!--
FORMATTING GUIDE:

### Detailed Entry (appears first when merging)

Use h3 (###) and below for detailed entries with paragraphs, code examples, and lists.

### Simple List Items

- Simple changes can be added as list items
- They are collected together at the bottom of each section

TIP: When multiple changelog drafts are merged, heading-based entries
appear before simple list items within each section.
-->

## ⬆️ Dependencies

- Upgrade `@furystack/core` to `^15.1.0` with `useSystemIdentityContext` support
- Upgrade `@furystack/rest` to `^8.0.37`
- Add `@furystack/entity-sync` `^0.1.1` for entity synchronization capabilities
- Upgrade `@types/node` to `^25.3.0`
32 changes: 32 additions & 0 deletions .yarn/changelogs/frontend.53378eac.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!-- version-type: patch -->

# frontend

<!--
FORMATTING GUIDE:

### Detailed Entry (appears first when merging)

Use h3 (###) and below for detailed entries with paragraphs, code examples, and lists.

### Simple List Items

- Simple changes can be added as list items
- They are collected together at the bottom of each section

TIP: When multiple changelog drafts are merged, heading-based entries
appear before simple list items within each section.
-->

## ⬆️ Dependencies

- Upgrade `@furystack/core` to `^15.1.0`
- Upgrade `@furystack/rest` to `^8.0.37`
- Upgrade `@furystack/rest-client-fetch` to `^8.0.37`
- Upgrade `@furystack/shades` to `^12.1.0`
- Upgrade `@furystack/shades-common-components` to `^12.2.0`
- Upgrade `@furystack/shades-lottie` to `^8.0.2`
- Add `@furystack/entity-sync` `^0.1.1` for entity synchronization
- Add `@furystack/entity-sync-client` `^0.1.1` for client-side entity sync
- Upgrade `marked` to `^17.0.3`
- Upgrade `@types/node` to `^25.3.0`
29 changes: 29 additions & 0 deletions .yarn/changelogs/pi-rat.53378eac.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!-- version-type: patch -->

# pi-rat

<!--
FORMATTING GUIDE:

### Detailed Entry (appears first when merging)

Use h3 (###) and below for detailed entries with paragraphs, code examples, and lists.

### Simple List Items

- Simple changes can be added as list items
- They are collected together at the bottom of each section

TIP: When multiple changelog drafts are merged, heading-based entries
appear before simple list items within each section.
-->

## ⬆️ Dependencies

- Upgrade `@types/node` to `^25.3.0`
- Upgrade `eslint-plugin-jsdoc` to `^62.6.1`
- Upgrade `eslint-plugin-playwright` to `^2.7.0`
- Upgrade `jsdom` to `^28.1.0`
- Upgrade `rimraf` to `^6.1.3`
- Upgrade `typescript-eslint` to `^8.56.0`
- Upgrade `@furystack/yarn-plugin-changelog` to `^1.0.4`
46 changes: 46 additions & 0 deletions .yarn/changelogs/service.53378eac.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!-- version-type: patch -->

# service

<!--
FORMATTING GUIDE:

### Detailed Entry (appears first when merging)

Use h3 (###) and below for detailed entries with paragraphs, code examples, and lists.

### Simple List Items

- Simple changes can be added as list items
- They are collected together at the bottom of each section

TIP: When multiple changelog drafts are merged, heading-based entries
appear before simple list items within each section.
-->

## ♻️ Refactoring

### Migrate from `StoreManager`/`PhysicalStore` to `DataSet`/`getDataSetFor`

All service-layer data access now uses `DataSet` from `@furystack/repository` instead of directly accessing `PhysicalStore` via `StoreManager`. This enables authorization-aware data operations through the repository layer.

Affected modules: AI (ollama, setup), chat actions (accept/reject/revoke invitation, setup), drives (file watcher), identity (register, setup), install (service installer), IoT (device availability), logging (db logger), media (scan, OMDB, movie maintainer, stream caches, ensure/link utils), ffprobe, and patcher (setup, run, orphan check).

### Use `useSystemIdentityContext` for elevated operations

Background services and event handlers now use `useSystemIdentityContext` to create system-level injectors with named identities (e.g. `ollama-service`, `chat-events`, `movie-maintainer`) instead of accessing stores directly without authorization context.

- Move `sequelize?.sync()` call from `service.ts` to `setup-identity-store.ts` for proper initialization ordering

## ⬆️ Dependencies

- Upgrade `@furystack/core` to `^15.1.0`
- Upgrade `@furystack/repository` to `^10.0.37`
- Upgrade `@furystack/rest` to `^8.0.37`
- Upgrade `@furystack/rest-service` to `^11.0.5`
- Upgrade `@furystack/security` to `^6.0.37`
- Upgrade `@furystack/sequelize-store` to `^6.0.38`
- Upgrade `@furystack/websocket-api` to `^13.1.9`
- Add `@furystack/entity-sync` `^0.1.1` for entity synchronization
- Add `@furystack/entity-sync-service` `^0.1.1` for server-side entity sync
- Upgrade `@types/node` to `^25.3.0`
5 changes: 5 additions & 0 deletions .yarn/versions/53378eac.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
releases:
common: patch
frontend: patch
pi-rat: patch
service: patch
7 changes: 4 additions & 3 deletions common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@
"create-schemas": "node ./dist/bin/create-schemas.js"
},
"devDependencies": {
"@types/node": "^25.2.3",
"@types/node": "^25.3.0",
"ts-json-schema-generator": "^2.5.0",
"vitest": "^4.0.18"
},
"dependencies": {
"@furystack/core": "^15.0.36",
"@furystack/rest": "^8.0.36",
"@furystack/core": "^15.1.0",
"@furystack/entity-sync": "^0.1.1",
"@furystack/rest": "^8.0.37",
"ollama": "^0.6.3"
}
}
18 changes: 10 additions & 8 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,30 @@
"license": "ISC",
"devDependencies": {
"@codecov/vite-plugin": "^1.9.1",
"@furystack/rest": "^8.0.36",
"@furystack/rest": "^8.0.37",
"@types/marked": "^6.0.0",
"typescript": "^5.9.3",
"vite": "^7.3.1"
},
"dependencies": {
"@furystack/cache": "^6.0.0",
"@furystack/core": "^15.0.36",
"@furystack/core": "^15.1.0",
"@furystack/entity-sync": "^0.1.1",
"@furystack/entity-sync-client": "^0.1.1",
"@furystack/inject": "^12.0.30",
"@furystack/logging": "^8.0.30",
"@furystack/rest-client-fetch": "^8.0.36",
"@furystack/shades": "^12.0.1",
"@furystack/shades-common-components": "^12.1.0",
"@furystack/shades-lottie": "^8.0.1",
"@furystack/rest-client-fetch": "^8.0.37",
"@furystack/shades": "^12.1.0",
"@furystack/shades-common-components": "^12.2.0",
"@furystack/shades-lottie": "^8.0.2",
"@furystack/utils": "^8.1.10",
"@types/node": "^25.2.3",
"@types/node": "^25.3.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-search": "^0.16.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/xterm": "^6.0.0",
"common": "workspace:^",
"marked": "^17.0.2",
"marked": "^17.0.3",
"media-chrome": "^4.17.2",
"monaco-editor": "^0.55.1",
"ollama": "^0.6.3",
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,24 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@furystack/yarn-plugin-changelog": "^1.0.3",
"@furystack/yarn-plugin-changelog": "^1.0.4",
"@playwright/test": "^1.58.2",
"@types/jsdom": "^27.0.0",
"@types/node": "^25.2.3",
"@types/node": "^25.3.0",
"@vitest/coverage-v8": "^4.0.18",
"eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-jsdoc": "^62.5.4",
"eslint-plugin-playwright": "^2.5.1",
"eslint-plugin-jsdoc": "^62.6.1",
"eslint-plugin-playwright": "^2.7.0",
"eslint-plugin-prettier": "^5.5.5",
"husky": "^9.1.7",
"jsdom": "^28.0.0",
"jsdom": "^28.1.0",
"lint-staged": "^16.2.7",
"prettier": "^3.8.1",
"rimraf": "^6.1.2",
"rimraf": "^6.1.3",
"typescript": "^5.9.3",
"typescript-eslint": "^8.55.0",
"typescript-eslint": "^8.56.0",
"vite": "^7.3.1",
"vitest": "^4.0.18"
},
Expand Down
18 changes: 10 additions & 8 deletions service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,24 @@
"devDependencies": {
"@types/ffprobe": "^1.1.8",
"@types/formidable": "^3.4.6",
"@types/node": "^25.2.3",
"@types/node": "^25.3.0",
"@types/ping": "^0.4.4",
"typescript": "^5.9.3"
},
"dependencies": {
"@furystack/cache": "^6.0.0",
"@furystack/core": "^15.0.36",
"@furystack/core": "^15.1.0",
"@furystack/entity-sync": "^0.1.1",
"@furystack/entity-sync-service": "^0.1.1",
"@furystack/inject": "^12.0.30",
"@furystack/logging": "^8.0.30",
"@furystack/repository": "^10.0.36",
"@furystack/rest": "^8.0.36",
"@furystack/rest-service": "^11.0.4",
"@furystack/security": "^6.0.36",
"@furystack/sequelize-store": "^6.0.37",
"@furystack/repository": "^10.0.37",
"@furystack/rest": "^8.0.37",
"@furystack/rest-service": "^11.0.5",
"@furystack/security": "^6.0.37",
"@furystack/sequelize-store": "^6.0.38",
"@furystack/utils": "^8.1.10",
"@furystack/websocket-api": "^13.1.8",
"@furystack/websocket-api": "^13.1.9",
"chokidar": "^5.0.0",
"common": "workspace:^",
"formidable": "^3.5.4",
Expand Down
25 changes: 15 additions & 10 deletions service/src/ai/ollama-client-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getCurrentUser, getStoreManager, type PhysicalStore } from '@furystack/core'
import { getCurrentUser, useSystemIdentityContext } from '@furystack/core'
import { Injectable, Injected, type Injector } from '@furystack/inject'
import { getLogger, type ScopedLogger } from '@furystack/logging'
import { getDataSetFor, type DataSet } from '@furystack/repository'
import { AiChatMessage, Config, type AiChat, type OllamaConfig } from 'common'
import type { Message } from 'ollama'
import { Ollama, type ChatRequest } from 'ollama'
Expand Down Expand Up @@ -40,8 +41,14 @@ export class OllamaClientService {
@Injected((injector) => getLogger(injector).withScope('Ollama Client Service'))
declare private logger: ScopedLogger

@Injected((injector) => getStoreManager(injector).getStoreFor(AiChatMessage, 'id'))
declare private chatMessageStore: PhysicalStore<AiChatMessage, 'id'>
@Injected((injector) => getDataSetFor(injector, AiChatMessage, 'id'))
declare private chatMessageDataSet: DataSet<AiChatMessage, 'id'>

@Injected((injector) => getDataSetFor(injector, Config, 'id'))
declare private configDataSet: DataSet<Config, 'id'>

@Injected((injector) => useSystemIdentityContext({ injector, username: 'ollama-service' }))
declare private systemInjector: Injector

declare ollama: Ollama

Expand All @@ -56,10 +63,8 @@ export class OllamaClientService {
)
}

private getOllamaConfig = async (injector: Injector): Promise<OllamaConfig | undefined> => {
const storeManager = getStoreManager(injector)
const configStore = storeManager.getStoreFor(Config, 'id')
const [ollamaConfig] = await configStore.find({
private getOllamaConfig = async (): Promise<OllamaConfig | undefined> => {
const [ollamaConfig] = await this.configDataSet.find(this.systemInjector, {
top: 1,
filter: {
id: { $eq: 'OLLAMA_CONFIG' },
Expand All @@ -73,8 +78,8 @@ export class OllamaClientService {
return this.isValidOllamaConfig(ollamaConfig) ? ollamaConfig : undefined
}

public async init(injector: Injector) {
const config = await this.getOllamaConfig(injector)
public async init() {
const config = await this.getOllamaConfig()
if (!config) {
this.config = undefined
await this.logger.information({
Expand Down Expand Up @@ -239,7 +244,7 @@ export class OllamaClientService {
})
: result

await this.chatMessageStore.add({
await this.chatMessageDataSet.add(this.systemInjector, {
aiChatId: chat.id,
role: 'assistant',
content: resultWithToolResponses.message.content,
Expand Down
31 changes: 16 additions & 15 deletions service/src/ai/setup-ai.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getCurrentUser, getStoreManager, IdentityContext } from '@furystack/core'
import { getCurrentUser, IdentityContext, useSystemIdentityContext } from '@furystack/core'
import type { Injector } from '@furystack/inject'
import { getLogger } from '@furystack/logging'
import { getDataSetFor } from '@furystack/repository'
import { usingAsync } from '@furystack/utils'
import { AiChat, AiChatMessage, Config, User } from 'common'
import { ImpersonatedIdentityContext } from '../utils/impersonated-identity-context.js'
Expand All @@ -12,30 +13,30 @@ export const setupAi = async (injector: Injector) => {
const logger = getLogger(injector).withScope('AI Setup')
const clientService = injector.getInstance(OllamaClientService)

const storeManager = getStoreManager(injector)
const configDataSet = getDataSetFor(injector, Config, 'id')

const configStore = storeManager.getStoreFor(Config, 'id')

configStore.subscribe('onEntityAdded', async ({ entity }) => {
configDataSet.subscribe('onEntityAdded', async ({ entity }) => {
if (entity.id === 'OLLAMA_CONFIG') {
await logger.verbose({ message: '🔄 Config changed, reinitializing AI Services' })
await clientService.init(injector)
await clientService.init()
}
})

configStore.subscribe('onEntityUpdated', async ({ id }) => {
configDataSet.subscribe('onEntityUpdated', async ({ id }) => {
if (id === 'OLLAMA_CONFIG') {
await logger.verbose({ message: '🔄 Config changed, reinitializing AI Services' })
await clientService.init(injector)
await clientService.init()
}
})

await setupAiStore(injector)

const chatMessageStore = storeManager.getStoreFor(AiChatMessage, 'id')
const chatStore = storeManager.getStoreFor(AiChat, 'id')
const systemInjector = useSystemIdentityContext({ injector, username: 'ai-setup' })
const chatMessageDataSet = getDataSetFor(injector, AiChatMessage, 'id')
const chatDataSet = getDataSetFor(injector, AiChat, 'id')
const userDataSet = getDataSetFor(injector, User, 'username')

chatMessageStore.subscribe('onEntityAdded', async ({ entity }) => {
chatMessageDataSet.subscribe('onEntityAdded', async ({ entity }) => {
const ws = injector.getInstance(WebsocketService)
await ws.announce(
{
Expand All @@ -49,19 +50,19 @@ export const setupAi = async (injector: Injector) => {
)
})

chatMessageStore.subscribe('onEntityAdded', async ({ entity }) => {
const chat = await chatStore.get(entity.aiChatId)
chatMessageDataSet.subscribe('onEntityAdded', async ({ entity }) => {
const chat = await chatDataSet.get(systemInjector, entity.aiChatId)
if (!chat) {
await logger.error({ message: `❌ Chat with ID ${entity.aiChatId} not found for message ${entity.id}` })
return
}
const chatHistory = await chatMessageStore.find({
const chatHistory = await chatMessageDataSet.find(systemInjector, {
filter: { aiChatId: { $eq: chat.id } },
order: { createdAt: 'DESC' },
top: 20,
})

const currentUser = await getStoreManager(injector).getStoreFor(User, 'username').get(entity.owner)
const currentUser = await userDataSet.get(systemInjector, entity.owner)

await usingAsync(injector.createChild({}), async (handlerInjector) => {
handlerInjector.setExplicitInstance(new ImpersonatedIdentityContext(currentUser), IdentityContext)
Expand Down
Loading
Loading