|
@@ -1,13 +1,15 @@
|
|
|
/** Current-profile plugin and bundle management over shared dsh plugin operations. */
|
|
/** Current-profile plugin and bundle management over shared dsh plugin operations. */
|
|
|
import { readFileSync } from 'node:fs'
|
|
import { readFileSync } from 'node:fs'
|
|
|
import { join } from 'node:path'
|
|
import { join } from 'node:path'
|
|
|
|
|
+import { withFileLock } from '@deepseek-ai/dsh-atomic-write'
|
|
|
import { Context } from '@deepseek-ai/cordis'
|
|
import { Context } from '@deepseek-ai/cordis'
|
|
|
import type { EntryOptions } from '@deepseek-ai/cordis-plugin-loader'
|
|
import type { EntryOptions } from '@deepseek-ai/cordis-plugin-loader'
|
|
|
import z from '@deepseek-ai/schemastery'
|
|
import z from '@deepseek-ai/schemastery'
|
|
|
import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol'
|
|
import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol'
|
|
|
import { readPluginInventory } from '@deepseek-ai/dsh-host-plugin-inventory'
|
|
import { readPluginInventory } from '@deepseek-ai/dsh-host-plugin-inventory'
|
|
|
-import { readProfileManifest, resolveBundleDir, loadOverlayPatches, composeEntries } from '@deepseek-ai/dsh-app-boot'
|
|
|
|
|
-import type { ProfileRuntime } from '@deepseek-ai/dsh-app-boot'
|
|
|
|
|
|
|
+import { readProfileManifest, resolveBundleDir, loadOverlayPatches, composeEntries, reconcileProfilePatches, readProfilePatches } from '@deepseek-ai/dsh-app-boot'
|
|
|
|
|
+import type {} from '@deepseek-ai/dsh-hmr'
|
|
|
|
|
+import type { ProfileContext } from '@deepseek-ai/dsh-app-boot'
|
|
|
import type { Agent } from '@deepseek-ai/dsh-agent'
|
|
import type { Agent } from '@deepseek-ai/dsh-agent'
|
|
|
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
|
import { bundleManifest, runProfilePnpm, saveManifest } from './operations.ts'
|
|
import { bundleManifest, runProfilePnpm, saveManifest } from './operations.ts'
|
|
@@ -32,9 +34,7 @@ const protectedModules = new Set([
|
|
|
'@deepseek-ai/dsh-client-ui-settings-plugin-inventory',
|
|
'@deepseek-ai/dsh-client-ui-settings-plugin-inventory',
|
|
|
'@deepseek-ai/cordis-plugin-timer', '@deepseek-ai/dsh-client-connection',
|
|
'@deepseek-ai/cordis-plugin-timer', '@deepseek-ai/dsh-client-connection',
|
|
|
'@deepseek-ai/dsh-host-frontend-static', '@deepseek-ai/dsh-tools',
|
|
'@deepseek-ai/dsh-host-frontend-static', '@deepseek-ai/dsh-tools',
|
|
|
- '@deepseek-ai/dsh-plugin-manager/tools',
|
|
|
|
|
'@deepseek-ai/dsh-hmr',
|
|
'@deepseek-ai/dsh-hmr',
|
|
|
- '@deepseek-ai/dsh-client-hmr',
|
|
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
/** Flatten only the groups addressable by the profile's patch composer. */
|
|
/** Flatten only the groups addressable by the profile's patch composer. */
|
|
@@ -54,7 +54,7 @@ declare module '@deepseek-ai/cordis' {
|
|
|
|
|
|
|
|
/** Manage profile files and apply their declared reload lifecycle. */
|
|
/** Manage profile files and apply their declared reload lifecycle. */
|
|
|
export class PluginManager extends TypertRemoteService {
|
|
export class PluginManager extends TypertRemoteService {
|
|
|
- static inject = ['loader', 'profileRuntime']
|
|
|
|
|
|
|
+ static inject = ['loader', 'profileContext']
|
|
|
static Config: z<Config> = z.object({
|
|
static Config: z<Config> = z.object({
|
|
|
outputBytes: z.number().step(1).min(1).default(16384),
|
|
outputBytes: z.number().step(1).min(1).default(16384),
|
|
|
notificationDelayMs: z.number().step(1).min(0).default(250),
|
|
notificationDelayMs: z.number().step(1).min(0).default(250),
|
|
@@ -62,7 +62,7 @@ export class PluginManager extends TypertRemoteService {
|
|
|
})
|
|
})
|
|
|
private readonly ownerEntryId: string | undefined
|
|
private readonly ownerEntryId: string | undefined
|
|
|
private readonly packageOperations = new Set<Promise<PackageResult>>()
|
|
private readonly packageOperations = new Set<Promise<PackageResult>>()
|
|
|
- private readonly runtime: ProfileRuntime
|
|
|
|
|
|
|
+ private readonly profile: ProfileContext
|
|
|
private readonly outputBytes: number
|
|
private readonly outputBytes: number
|
|
|
private readonly notificationDelayMs: number
|
|
private readonly notificationDelayMs: number
|
|
|
private readonly lockWaitMs: number
|
|
private readonly lockWaitMs: number
|
|
@@ -78,7 +78,7 @@ export class PluginManager extends TypertRemoteService {
|
|
|
super(ctx, 'pluginManager')
|
|
super(ctx, 'pluginManager')
|
|
|
this.ownerEntryId = ctx.fiber.entry?.id
|
|
this.ownerEntryId = ctx.fiber.entry?.id
|
|
|
this.ownerContext = ctx
|
|
this.ownerContext = ctx
|
|
|
- this.runtime = ctx.profileRuntime
|
|
|
|
|
|
|
+ this.profile = ctx.profileContext
|
|
|
this.outputBytes = (config as Required<Config>).outputBytes
|
|
this.outputBytes = (config as Required<Config>).outputBytes
|
|
|
this.notificationDelayMs = (config as Required<Config>).notificationDelayMs
|
|
this.notificationDelayMs = (config as Required<Config>).notificationDelayMs
|
|
|
this.lockWaitMs = (config as Required<Config>).lockWaitMs
|
|
this.lockWaitMs = (config as Required<Config>).lockWaitMs
|
|
@@ -95,7 +95,7 @@ export class PluginManager extends TypertRemoteService {
|
|
|
*/
|
|
*/
|
|
|
@Remote
|
|
@Remote
|
|
|
async listPlugins(): Promise<PluginInfo[]> {
|
|
async listPlugins(): Promise<PluginInfo[]> {
|
|
|
- const rows = flatten(this.runtime.entries())
|
|
|
|
|
|
|
+ const rows = flatten(composeEntries([readProfilePatches('dsh', this.profile)]))
|
|
|
const snapshot = await readPluginInventory(this.ctx)
|
|
const snapshot = await readPluginInventory(this.ctx)
|
|
|
return snapshot.entries.map((entry) => {
|
|
return snapshot.entries.map((entry) => {
|
|
|
const actual = [...this.ctx.loader.entries()].find(row => row.id === entry.entryId)
|
|
const actual = [...this.ctx.loader.entries()].find(row => row.id === entry.entryId)
|
|
@@ -118,16 +118,16 @@ export class PluginManager extends TypertRemoteService {
|
|
|
*/
|
|
*/
|
|
|
@Remote
|
|
@Remote
|
|
|
listBundles(): Promise<BundleInfo[]> {
|
|
listBundles(): Promise<BundleInfo[]> {
|
|
|
- const manifest = readProfileManifest('dsh', this.runtime.dir)
|
|
|
|
|
|
|
+ const manifest = readProfileManifest('dsh', this.profile.dir)
|
|
|
const selected = manifest.dsh?.profile?.bundles ?? []
|
|
const selected = manifest.dsh?.profile?.bundles ?? []
|
|
|
const dependencies = Object.keys(manifest.dependencies ?? {})
|
|
const dependencies = Object.keys(manifest.dependencies ?? {})
|
|
|
- const installation = JSON.parse(readFileSync(this.runtime.installAnchor, 'utf8')) as { dependencies?: Record<string, string> }
|
|
|
|
|
|
|
+ const installation = JSON.parse(readFileSync(this.profile.installAnchor, 'utf8')) as { dependencies?: Record<string, string> }
|
|
|
const names = [...new Set([...selected, ...dependencies, ...Object.keys(installation.dependencies ?? {})])]
|
|
const names = [...new Set([...selected, ...dependencies, ...Object.keys(installation.dependencies ?? {})])]
|
|
|
const bundles: BundleInfo[] = []
|
|
const bundles: BundleInfo[] = []
|
|
|
for (const name of names) {
|
|
for (const name of names) {
|
|
|
const removable = dependencies.includes(name) && !Object.hasOwn(installation.dependencies ?? {}, name)
|
|
const removable = dependencies.includes(name) && !Object.hasOwn(installation.dependencies ?? {}, name)
|
|
|
try {
|
|
try {
|
|
|
- const info = bundleManifest(name, this.runtime.dir, this.runtime.installAnchor)
|
|
|
|
|
|
|
+ const info = bundleManifest(name, this.profile.dir, this.profile.installAnchor)
|
|
|
if (info === undefined) {
|
|
if (info === undefined) {
|
|
|
if (selected.includes(name)) bundles.push({ name, enabled: true, removable, error: `Not a bundle: ${name}` })
|
|
if (selected.includes(name)) bundles.push({ name, enabled: true, removable, error: `Not a bundle: ${name}` })
|
|
|
continue
|
|
continue
|
|
@@ -156,10 +156,10 @@ export class PluginManager extends TypertRemoteService {
|
|
|
const row = (await this.listPlugins()).find(item => item.entryId === id)
|
|
const row = (await this.listPlugins()).find(item => item.entryId === id)
|
|
|
if (row === undefined) throw new Error(`Unknown plugin entry: ${id}`)
|
|
if (row === undefined) throw new Error(`Unknown plugin entry: ${id}`)
|
|
|
if (row.readOnlyReason !== undefined || row.patchId === undefined) throw new Error(row.readOnlyReason)
|
|
if (row.readOnlyReason !== undefined || row.patchId === undefined) throw new Error(row.readOnlyReason)
|
|
|
- await writePluginEnabled(this.runtime.read().patchPath, row.patchId, enabled)
|
|
|
|
|
- await this.runtime.reload()
|
|
|
|
|
|
|
+ await writePluginEnabled(this.profile.patchPath, row.patchId, enabled)
|
|
|
|
|
+ await this.reload()
|
|
|
const current = (await this.listPlugins()).find(item => item.entryId === id)
|
|
const current = (await this.listPlugins()).find(item => item.entryId === id)
|
|
|
- return current?.enabled !== enabled && this.runtime.patchReload === 'live' ? 'overridden' : undefined
|
|
|
|
|
|
|
+ return current?.enabled !== enabled && this.profile.patchReload === 'live' ? 'overridden' : undefined
|
|
|
}, `Plugin ${id}: ${enabled ? 'enabled' : 'disabled'}.`)
|
|
}, `Plugin ${id}: ${enabled ? 'enabled' : 'disabled'}.`)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -172,7 +172,7 @@ export class PluginManager extends TypertRemoteService {
|
|
|
setBundleEnabled(name: string, enabled: boolean): Promise<ChangeResult> {
|
|
setBundleEnabled(name: string, enabled: boolean): Promise<ChangeResult> {
|
|
|
return this.change(async () => {
|
|
return this.change(async () => {
|
|
|
await this.selectBundle(name, enabled)
|
|
await this.selectBundle(name, enabled)
|
|
|
- await this.runtime.reload()
|
|
|
|
|
|
|
+ await this.reload()
|
|
|
}, `Bundle ${name}: ${enabled ? 'enabled' : 'disabled'}.`)
|
|
}, `Bundle ${name}: ${enabled ? 'enabled' : 'disabled'}.`)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -186,22 +186,22 @@ export class PluginManager extends TypertRemoteService {
|
|
|
let packageResult: PackageResult | undefined
|
|
let packageResult: PackageResult | undefined
|
|
|
return this.change(async () => {
|
|
return this.change(async () => {
|
|
|
if (spec.trim() === '' || spec.startsWith('-')) throw new Error('A package spec is required')
|
|
if (spec.trim() === '' || spec.startsWith('-')) throw new Error('A package spec is required')
|
|
|
- const before = readProfileManifest('dsh', this.runtime.dir).dependencies ?? {}
|
|
|
|
|
|
|
+ const before = readProfileManifest('dsh', this.profile.dir).dependencies ?? {}
|
|
|
packageResult = await this.runPnpm(['add', spec])
|
|
packageResult = await this.runPnpm(['add', spec])
|
|
|
if (packageResult.exitCode !== 0) throw new Error(packageResult.output)
|
|
if (packageResult.exitCode !== 0) throw new Error(packageResult.output)
|
|
|
- const after = readProfileManifest('dsh', this.runtime.dir).dependencies ?? {}
|
|
|
|
|
|
|
+ const after = readProfileManifest('dsh', this.profile.dir).dependencies ?? {}
|
|
|
const installed = Object.keys(after).filter(name => before[name] !== after[name])
|
|
const installed = Object.keys(after).filter(name => before[name] !== after[name])
|
|
|
// Registry retries can retain the saved range after a partial installation.
|
|
// Registry retries can retain the saved range after a partial installation.
|
|
|
if (installed.length === 0) installed.push(...Object.keys(after).filter(name => spec === name || spec.startsWith(`${name}@`)))
|
|
if (installed.length === 0) installed.push(...Object.keys(after).filter(name => spec === name || spec.startsWith(`${name}@`)))
|
|
|
const name = installed[0]
|
|
const name = installed[0]
|
|
|
if (installed.length !== 1 || name === undefined) throw new Error('Cannot identify one installed bundle from the dependency change')
|
|
if (installed.length !== 1 || name === undefined) throw new Error('Cannot identify one installed bundle from the dependency change')
|
|
|
- const dir = resolveBundleDir('dsh', name, this.runtime.installAnchor, this.runtime.dir)
|
|
|
|
|
- const manifest = bundleManifest(name, this.runtime.dir, this.runtime.installAnchor)
|
|
|
|
|
|
|
+ const dir = resolveBundleDir('dsh', name, this.profile.installAnchor, this.profile.dir)
|
|
|
|
|
+ const manifest = bundleManifest(name, this.profile.dir, this.profile.installAnchor)
|
|
|
if (manifest?.dsh?.bundle?.patch === undefined) throw new Error(`${name} declares no dsh.bundle.patch`)
|
|
if (manifest?.dsh?.bundle?.patch === undefined) throw new Error(`${name} declares no dsh.bundle.patch`)
|
|
|
loadOverlayPatches('dsh', join(dir, manifest.dsh.bundle.patch))
|
|
loadOverlayPatches('dsh', join(dir, manifest.dsh.bundle.patch))
|
|
|
if (options?.enabled !== false) await this.selectBundle(name, true)
|
|
if (options?.enabled !== false) await this.selectBundle(name, true)
|
|
|
if (Object.hasOwn(before, name)) return 'restart-required'
|
|
if (Object.hasOwn(before, name)) return 'restart-required'
|
|
|
- await this.runtime.reload()
|
|
|
|
|
|
|
+ await this.reload()
|
|
|
}, `Bundle installation: ${spec}.`, () => packageResult)
|
|
}, `Bundle installation: ${spec}.`, () => packageResult)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -215,19 +215,19 @@ export class PluginManager extends TypertRemoteService {
|
|
|
return this.change(async () => {
|
|
return this.change(async () => {
|
|
|
const bundle = (await this.listBundles()).find(item => item.name === name)
|
|
const bundle = (await this.listBundles()).find(item => item.name === name)
|
|
|
if (bundle === undefined || !bundle.removable) throw new Error(`Bundle is not a profile-owned dependency: ${name}`)
|
|
if (bundle === undefined || !bundle.removable) throw new Error(`Bundle is not a profile-owned dependency: ${name}`)
|
|
|
- if (this.runtime.patchReload === 'startup' && this.runtime.startedBundles.includes(name)) {
|
|
|
|
|
|
|
+ if (this.profile.patchReload === 'startup' && this.profile.startedBundles.includes(name)) {
|
|
|
throw new Error('Stop this startup-only profile and remove the bundle with dsh plugin')
|
|
throw new Error('Stop this startup-only profile and remove the bundle with dsh plugin')
|
|
|
}
|
|
}
|
|
|
await this.selectBundle(name, false)
|
|
await this.selectBundle(name, false)
|
|
|
- await this.runtime.reload()
|
|
|
|
|
|
|
+ await this.reload()
|
|
|
packageResult = await this.runPnpm(['remove', name])
|
|
packageResult = await this.runPnpm(['remove', name])
|
|
|
if (packageResult.exitCode !== 0) throw new Error(packageResult.output)
|
|
if (packageResult.exitCode !== 0) throw new Error(packageResult.output)
|
|
|
- await this.runtime.reload()
|
|
|
|
|
|
|
+ await this.reload()
|
|
|
}, `Bundle removed: ${name}.`, () => packageResult)
|
|
}, `Bundle removed: ${name}.`, () => packageResult)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private async runPnpm(args: readonly string[]): Promise<PackageResult> {
|
|
private async runPnpm(args: readonly string[]): Promise<PackageResult> {
|
|
|
- const task = runProfilePnpm({ ...this.runtime, profile: this.runtime.name }, args, {
|
|
|
|
|
|
|
+ const task = runProfilePnpm({ ...this.profile, profile: this.profile.name }, args, {
|
|
|
signal: this.abort.signal, outputBytes: this.outputBytes, activateNewBundles: false,
|
|
signal: this.abort.signal, outputBytes: this.outputBytes, activateNewBundles: false,
|
|
|
})
|
|
})
|
|
|
this.packageOperations.add(task)
|
|
this.packageOperations.add(task)
|
|
@@ -236,9 +236,9 @@ export class PluginManager extends TypertRemoteService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private async selectBundle(name: string, enabled: boolean): Promise<void> {
|
|
private async selectBundle(name: string, enabled: boolean): Promise<void> {
|
|
|
- const manifest = readProfileManifest('dsh', this.runtime.dir)
|
|
|
|
|
|
|
+ const manifest = readProfileManifest('dsh', this.profile.dir)
|
|
|
const previous = manifest.dsh?.profile?.bundles ?? []
|
|
const previous = manifest.dsh?.profile?.bundles ?? []
|
|
|
- if ((enabled || !previous.includes(name)) && bundleManifest(name, this.runtime.dir, this.runtime.installAnchor) === undefined) {
|
|
|
|
|
|
|
+ if ((enabled || !previous.includes(name)) && bundleManifest(name, this.profile.dir, this.profile.installAnchor) === undefined) {
|
|
|
throw new Error(`Not a bundle: ${name}`)
|
|
throw new Error(`Not a bundle: ${name}`)
|
|
|
}
|
|
}
|
|
|
if (!enabled && previous.includes(name)) {
|
|
if (!enabled && previous.includes(name)) {
|
|
@@ -247,25 +247,30 @@ export class PluginManager extends TypertRemoteService {
|
|
|
const bundles = enabled ? [...previous, ...previous.includes(name) ? [] : [name]] : previous.filter(item => item !== name)
|
|
const bundles = enabled ? [...previous, ...previous.includes(name) ? [] : [name]] : previous.filter(item => item !== name)
|
|
|
if (JSON.stringify(previous) === JSON.stringify(bundles)) return
|
|
if (JSON.stringify(previous) === JSON.stringify(bundles)) return
|
|
|
manifest.dsh = { ...manifest.dsh, profile: { ...manifest.dsh?.profile, bundles } }
|
|
manifest.dsh = { ...manifest.dsh, profile: { ...manifest.dsh?.profile, bundles } }
|
|
|
- await saveManifest(this.runtime.dir, manifest)
|
|
|
|
|
|
|
+ await saveManifest(this.profile.dir, manifest)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private protectsManager(name: string): boolean {
|
|
private protectsManager(name: string): boolean {
|
|
|
- const info = bundleManifest(name, this.runtime.dir, this.runtime.installAnchor)
|
|
|
|
|
|
|
+ const info = bundleManifest(name, this.profile.dir, this.profile.installAnchor)
|
|
|
if (info?.dsh?.bundle === undefined) return false
|
|
if (info?.dsh?.bundle === undefined) return false
|
|
|
- const dir = resolveBundleDir('dsh', name, this.runtime.installAnchor, this.runtime.dir)
|
|
|
|
|
|
|
+ const dir = resolveBundleDir('dsh', name, this.profile.installAnchor, this.profile.dir)
|
|
|
const rows = flatten(composeEntries([loadOverlayPatches('dsh', join(dir, info.dsh.bundle.patch))]))
|
|
const rows = flatten(composeEntries([loadOverlayPatches('dsh', join(dir, info.dsh.bundle.patch))]))
|
|
|
return rows.some(row => protectedModules.has(row.name) || `include:${row.id}` === this.ownerEntryId)
|
|
return rows.some(row => protectedModules.has(row.name) || `include:${row.id}` === this.ownerEntryId)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private async reload(): Promise<void> {
|
|
|
|
|
+ if (this.profile.patchReload === 'startup') return
|
|
|
|
|
+ await reconcileProfilePatches(this.ownerContext.root, readProfilePatches('dsh', this.profile), 'dsh')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private async change(
|
|
private async change(
|
|
|
operation: () => Promise<ChangeResult['application'] | void>, message: string, packageResult?: () => PackageResult | undefined,
|
|
operation: () => Promise<ChangeResult['application'] | void>, message: string, packageResult?: () => PackageResult | undefined,
|
|
|
): Promise<ChangeResult> {
|
|
): Promise<ChangeResult> {
|
|
|
let notice: Promise<void> | undefined
|
|
let notice: Promise<void> | undefined
|
|
|
- const result = await this.runtime.mutate(async () => {
|
|
|
|
|
|
|
+ const locked = () => withFileLock(join(this.profile.dir, 'package.json'), async () => {
|
|
|
this.abort.signal.throwIfAborted()
|
|
this.abort.signal.throwIfAborted()
|
|
|
const before = this.diskState()
|
|
const before = this.diskState()
|
|
|
- let application: ChangeResult['application'] = this.runtime.patchReload === 'live' ? 'applied' : 'restart-required'
|
|
|
|
|
|
|
+ let application: ChangeResult['application'] = this.profile.patchReload === 'live' ? 'applied' : 'restart-required'
|
|
|
try {
|
|
try {
|
|
|
application = await operation() ?? application
|
|
application = await operation() ?? application
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
@@ -277,14 +282,16 @@ export class PluginManager extends TypertRemoteService {
|
|
|
if (packages !== undefined) result.packageResult = packages
|
|
if (packages !== undefined) result.packageResult = packages
|
|
|
notice = this.notify(result)
|
|
notice = this.notify(result)
|
|
|
return result
|
|
return result
|
|
|
- }, this.lockWaitMs)
|
|
|
|
|
|
|
+ }, { waitMs: this.lockWaitMs })
|
|
|
|
|
+ const hmr = this.ownerContext.get('hmr')
|
|
|
|
|
+ const result = await (hmr === undefined ? locked() : hmr.runExclusive(locked))
|
|
|
await notice
|
|
await notice
|
|
|
return result
|
|
return result
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private diskState(): string {
|
|
private diskState(): string {
|
|
|
return ['package.json', 'cordis.patch.yml'].map((file) => {
|
|
return ['package.json', 'cordis.patch.yml'].map((file) => {
|
|
|
- try { return readFileSync(join(this.runtime.dir, file), 'utf8') }
|
|
|
|
|
|
|
+ try { return readFileSync(join(this.profile.dir, file), 'utf8') }
|
|
|
catch (error) {
|
|
catch (error) {
|
|
|
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return ''
|
|
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return ''
|
|
|
throw error
|
|
throw error
|
|
@@ -298,7 +305,7 @@ export class PluginManager extends TypertRemoteService {
|
|
|
for (const agent of agents) this.noticeAgents.add(agent)
|
|
for (const agent of agents) this.noticeAgents.add(agent)
|
|
|
this.noticeDelivered ??= Promise.withResolvers<void>()
|
|
this.noticeDelivered ??= Promise.withResolvers<void>()
|
|
|
const delivered = this.noticeDelivered.promise
|
|
const delivered = this.noticeDelivered.promise
|
|
|
- const notice = `Profile ${this.runtime.name}: ${result.message} (${result.application})`
|
|
|
|
|
|
|
+ const notice = `Profile ${this.profile.name}: ${result.message} (${result.application})`
|
|
|
if (Buffer.byteLength(this.pendingNotice + notice) > this.outputBytes) {
|
|
if (Buffer.byteLength(this.pendingNotice + notice) > this.outputBytes) {
|
|
|
this.omittedNotices += 1
|
|
this.omittedNotices += 1
|
|
|
} else {
|
|
} else {
|