feat: expand multi-runtime workflows for 0.10.0

GoodBuddy previously exposed Runtime capabilities, MCP assignments, plugin controls, context compaction, usage reporting, and task-completion behavior through incomplete or inconsistent paths. This release unifies managed OpenCode, Continue, and DeepSeek Harness controls; adds DSH plugin and image workflows; strengthens MCP and Runtime lifecycle bounds; fixes Windows notification activation; validates cross-architecture packages; and presents the approved bilingual four-section release notes.

The DSH plugin marketplace remains a default-off preview whose trusted third-party code runs with current-user permissions. Ask remains read-only, Execute keeps approval controls, and context compaction may use the selected model without deleting GoodBuddy chat history.

Release note: GoodBuddy 0.10.0 重点完善多 Runtime 工作流,统一 OpenCode、Continue 与 DeepSeek Harness 的能力、MCP、插件和上下文管理,并提升长对话、多会话与任务通知的连贯性。
This commit is contained in:
mesalogo
2026-08-17 12:57:12 +08:00
parent f5ee9b2198
commit cbbe30896e
50 changed files with 4077 additions and 605 deletions
+133
View File
@@ -0,0 +1,133 @@
import { createHash } from 'node:crypto'
import { statSync } from 'node:fs'
import { dirname, join, resolve, win32 } from 'node:path'
import type { ShortcutDetails } from 'electron'
export const WINDOWS_APP_USER_MODEL_ID = 'live.digiman.goodbuddy'
const windowsUninstallerName = 'Uninstall GoodBuddy.exe'
const standaloneIdentityPrefix = `${WINDOWS_APP_USER_MODEL_ID}.standalone`
const knownShortcutNames = ['Electron.lnk', 'GoodBuddy.lnk'] as const
interface ShortcutAccess {
readShortcutLink(shortcutPath: string): ShortcutDetails
writeShortcutLink(
shortcutPath: string,
operation: 'update',
options: ShortcutDetails
): boolean
}
export interface WindowsNotificationShortcutRepairResult {
scanned: number
repaired: number
failed: number
}
function normalizeWindowsExecutablePath(executablePath: string): string {
return win32.resolve(executablePath).toLocaleLowerCase('en-US')
}
function resolveStandaloneWindowsAppUserModelId(
executablePath: string
): string {
const executableHash = createHash('sha256')
.update(normalizeWindowsExecutablePath(executablePath))
.digest('hex')
.slice(0, 24)
return `${standaloneIdentityPrefix}.${executableHash}`
}
export function isInstalledWindowsBuild(input: {
packaged: boolean
platform: NodeJS.Platform
executablePath: string
}): boolean {
if (!input.packaged || input.platform !== 'win32') {
return false
}
try {
const uninstallerPath = join(
dirname(resolve(input.executablePath)),
windowsUninstallerName
)
return statSync(uninstallerPath, {
throwIfNoEntry: false
})?.isFile() === true
} catch {
return false
}
}
export function resolveWindowsAppUserModelId(input: {
installed: boolean
executablePath: string
}): string {
return input.installed
? WINDOWS_APP_USER_MODEL_ID
: resolveStandaloneWindowsAppUserModelId(input.executablePath)
}
export async function repairStaleWindowsNotificationShortcuts(input: {
platform: NodeJS.Platform
installed: boolean
executablePath: string
programsDirectory: string
shortcutAccess: ShortcutAccess
}): Promise<WindowsNotificationShortcutRepairResult> {
if (input.platform !== 'win32' || !input.installed) {
return { scanned: 0, repaired: 0, failed: 0 }
}
const shortcutPaths = knownShortcutNames.map((shortcutName) =>
join(input.programsDirectory, shortcutName)
)
const currentExecutablePath = normalizeWindowsExecutablePath(
input.executablePath
)
let scanned = 0
let repaired = 0
let failed = 0
for (const shortcutPath of shortcutPaths) {
let details: ShortcutDetails
try {
details = input.shortcutAccess.readShortcutLink(shortcutPath)
} catch {
continue
}
scanned += 1
if (
details.appUserModelId !== WINDOWS_APP_USER_MODEL_ID ||
normalizeWindowsExecutablePath(details.target) ===
currentExecutablePath
) {
continue
}
try {
const updated = input.shortcutAccess.writeShortcutLink(
shortcutPath,
'update',
{
...details,
appUserModelId:
resolveStandaloneWindowsAppUserModelId(details.target)
}
)
if (updated) {
repaired += 1
} else {
failed += 1
}
} catch {
failed += 1
}
}
return {
scanned,
repaired,
failed
}
}