From 00572774934ab2523806b4ec6a0747eeb594c3c1 Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 16:07:58 +0700 Subject: [PATCH 01/10] docs: add implementation plan for extracting Slack plugin Co-Authored-By: Claude Opus 4.6 (1M context) --- ...026-03-27-extract-slack-plugin-redesign.md | 837 ++++++++++++++++++ 1 file changed, 837 insertions(+) create mode 100644 docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md diff --git a/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md b/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md new file mode 100644 index 00000000..d4ae148d --- /dev/null +++ b/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md @@ -0,0 +1,837 @@ +# Extract Slack Plugin from Redesign Branch — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Extract the Slack adapter from OpenACP's built-in plugins into a standalone `@openacp/adapter-slack` package in the `slack-plugin` repo, update the Plugin SDK exports, add registry discovery to the setup wizard, and update the plugin-registry manifest. + +**Architecture:** The Slack plugin source code lives at `src/plugins/slack/` on the `redesign/microkernel-plugin-architecture` branch as a built-in plugin. We copy it to the `slack-plugin` repo, rewrite all `../../core/...` imports to use `@openacp/plugin-sdk`, remove the built-in plugin from core, and wire the wizard to discover community adapters from the registry. + +**Tech Stack:** TypeScript, ESM, Vitest, `@openacp/plugin-sdk`, `@slack/bolt`, `@slack/web-api`, `@clack/prompts`, `p-queue`, `nanoid`, `zod` + +**Repos involved (all under `/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/`):** +- `OpenACP` — branch `redesign/microkernel-plugin-architecture` +- `slack-plugin` — create new branch from `main` +- `plugin-registry` — create new branch from `main` + +--- + +### Task 1: Update Plugin SDK Exports (OpenACP repo) + +**Files:** +- Modify: `OpenACP/src/packages/plugin-sdk/src/index.ts` +- Modify: `OpenACP/src/index.ts` (if types aren't already exported from the main package) + +The SDK currently exports adapter base classes (`MessagingAdapter`, `BaseRenderer`, `SendQueue`) but is missing several types that adapter plugins need. These must be added before the Slack plugin can compile. + +- [ ] **Step 1: Checkout redesign branch in OpenACP** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP +git stash +git checkout redesign/microkernel-plugin-architecture +git checkout -b feat/extract-slack-plugin +``` + +- [ ] **Step 2: Check which types are already exported from `src/index.ts`** + +Run: +```bash +grep -n "DisplayVerbosity\|AdapterCapabilities\|Attachment\|MessagingAdapterConfig\|IRenderer\|RenderedMessage\|RenderedPermission" src/index.ts +``` + +This tells us which types are already in the main package's public API (the SDK re-exports from `@openacp/cli`). + +- [ ] **Step 3: Add missing type exports to `src/index.ts`** + +Add the following exports to `src/index.ts` if not already present. These are the types adapter plugins need: + +```typescript +// Adapter types for external plugins +export type { DisplayVerbosity } from './core/adapter-primitives/format-types.js' +export type { AdapterCapabilities } from './core/channel.js' +export type { Attachment } from './core/types.js' +export type { MessagingAdapterConfig } from './core/adapter-primitives/messaging-adapter.js' +export type { IRenderer, RenderedMessage, RenderedPermission } from './core/adapter-primitives/rendering/renderer.js' +``` + +- [ ] **Step 4: Update Plugin SDK to re-export new types** + +In `src/packages/plugin-sdk/src/index.ts`, add re-exports for the newly exported types: + +```typescript +// Add to existing re-exports from '@openacp/cli' +export type { + // ... existing exports ... + DisplayVerbosity, + AdapterCapabilities, + Attachment, + MessagingAdapterConfig, + IRenderer, + RenderedMessage, + RenderedPermission, +} from '@openacp/cli' +``` + +- [ ] **Step 5: Verify build** + +```bash +pnpm build +``` + +Expected: Build succeeds with no errors. + +- [ ] **Step 6: Commit** + +```bash +git add src/index.ts src/packages/plugin-sdk/src/index.ts +git commit -m "feat(plugin-sdk): export adapter types for external plugins" +``` + +--- + +### Task 2: Reset slack-plugin Repo + +**Files:** +- Modify: `slack-plugin/package.json` +- Modify: `slack-plugin/tsconfig.json` +- Modify: `slack-plugin/vitest.config.ts` +- Delete: all existing `src/` files + +- [ ] **Step 1: Create new branch in slack-plugin** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin +git checkout main +git checkout -b feat/redesign-plugin-architecture +``` + +- [ ] **Step 2: Delete all existing source files** + +```bash +rm -rf src/ dist/ +``` + +- [ ] **Step 3: Create new `src/` directory** + +```bash +mkdir -p src/__tests__ +``` + +- [ ] **Step 4: Update `package.json`** + +Replace the entire `package.json` content: + +```json +{ + "name": "@openacp/adapter-slack", + "version": "0.1.0", + "description": "Slack messaging platform adapter plugin for OpenACP", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": ["dist"], + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "test": "vitest run", + "test:watch": "vitest", + "prepublishOnly": "npm run build" + }, + "keywords": ["openacp", "openacp-plugin", "slack", "adapter"], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Open-ACP/slack-adapter" + }, + "peerDependencies": { + "@openacp/plugin-sdk": "^1.0.0" + }, + "dependencies": { + "@clack/prompts": "^1.1.0", + "@slack/bolt": "^4.6.0", + "@slack/web-api": "^7.15.0", + "nanoid": "^5.0.0", + "p-queue": "^9.1.0", + "zod": "^3.25.0" + }, + "devDependencies": { + "@openacp/plugin-sdk": "file:../OpenACP/src/packages/plugin-sdk", + "typescript": "^5.4.0", + "vitest": "^3.0.0" + } +} +``` + +Note: `devDependencies` uses `file:` link to the local SDK for development. For publishing, change to `"^1.0.0"`. + +- [ ] **Step 5: Update `tsconfig.json`** + +```json +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "skipLibCheck": true, + "outDir": "dist", + "rootDir": "src", + "types": ["node"] + }, + "include": ["src"], + "exclude": ["src/__tests__"] +} +``` + +- [ ] **Step 6: Update `vitest.config.ts`** + +```typescript +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['src/**/*.test.ts'], + }, +}) +``` + +- [ ] **Step 7: Install dependencies** + +```bash +npm install +``` + +- [ ] **Step 8: Commit** + +```bash +git add -A +git commit -m "chore: reset repo for redesign plugin architecture" +``` + +--- + +### Task 3: Copy and Rewrite Slack Plugin Source Files + +**Files:** +- Create: `slack-plugin/src/index.ts` +- Create: `slack-plugin/src/adapter.ts` +- Create: `slack-plugin/src/event-router.ts` +- Create: `slack-plugin/src/channel-manager.ts` +- Create: `slack-plugin/src/formatter.ts` +- Create: `slack-plugin/src/renderer.ts` +- Create: `slack-plugin/src/text-buffer.ts` +- Create: `slack-plugin/src/send-queue.ts` +- Create: `slack-plugin/src/permission-handler.ts` +- Create: `slack-plugin/src/slug.ts` +- Create: `slack-plugin/src/types.ts` +- Create: `slack-plugin/src/utils.ts` + +**Source:** Copy from `OpenACP/src/plugins/slack/` on `redesign/microkernel-plugin-architecture` branch. + +**Import rewrite rules (apply to ALL files):** + +| Old import path | New import | Notes | +|---|---|---| +| `from '../../core/plugin/types.js'` | `from '@openacp/plugin-sdk'` | All plugin types | +| `from '../../core/types.js'` | `from '@openacp/plugin-sdk'` | OutgoingMessage, PermissionRequest, etc. | +| `from '../../core/channel.js'` | `from '@openacp/plugin-sdk'` | AdapterCapabilities | +| `from '../../core/adapter-primitives/messaging-adapter.js'` | `from '@openacp/plugin-sdk'` | MessagingAdapter, MessagingAdapterConfig | +| `from '../../core/adapter-primitives/format-types.js'` | `from '@openacp/plugin-sdk'` | DisplayVerbosity | +| `from '../../core/adapter-primitives/rendering/renderer.js'` | `from '@openacp/plugin-sdk'` | BaseRenderer, IRenderer, etc. | +| `from '../../core/core.js'` | Remove — use `ctx.kernel` | OpenACPCore | +| `from '../../core/utils/log.js'` | Remove — use `ctx.log` | createChildLogger | +| `from '../../core/config/config.js'` | Remove — define locally | SlackChannelConfig | + +- [ ] **Step 1: Copy all source files from redesign branch** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group + +# Copy source files (not tests) +for file in index.ts adapter.ts event-router.ts channel-manager.ts formatter.ts renderer.ts text-buffer.ts send-queue.ts permission-handler.ts slug.ts types.ts utils.ts; do + cp OpenACP/src/plugins/slack/$file slack-plugin/src/$file +done +``` + +Note: The OpenACP repo must be on the `redesign/microkernel-plugin-architecture` branch (or `feat/extract-slack-plugin` which was branched from it). + +- [ ] **Step 2: Rewrite `types.ts`** + +This file currently re-exports `SlackChannelConfig` from `../../core/config/config.js`. Instead, define the Zod schema locally. Read the current content of `OpenACP/src/core/config/config.ts` to find the `SlackChannelConfig` schema, then write it directly in `types.ts`: + +```typescript +import { z } from 'zod' + +export const SlackChannelConfigSchema = z.object({ + enabled: z.boolean().default(true), + botToken: z.string(), + appToken: z.string(), + signingSecret: z.string().optional(), + channelPrefix: z.string().default('openacp'), + notificationChannelId: z.string().optional(), + allowedUserIds: z.array(z.string()).default([]), + autoCreateSession: z.boolean().default(true), + startupChannelId: z.string().optional(), +}) + +export type SlackChannelConfig = z.infer + +export interface SlackSessionMeta { + channelId: string + channelSlug: string +} + +export interface SlackFileInfo { + id: string + name: string + mimetype: string + size: number + url_private: string +} +``` + +Verify: Check `OpenACP/src/core/config/config.ts` for the exact `SlackChannelConfig` schema fields. The fields above are from the research but must match exactly. + +- [ ] **Step 3: Rewrite imports in `index.ts`** + +Change: +```typescript +import type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js' +import type { OpenACPCore } from '../../core/core.js' +``` +To: +```typescript +import type { OpenACPPlugin, InstallContext, PluginContext } from '@openacp/plugin-sdk' +``` + +Remove any direct reference to `OpenACPCore`. In the `setup()` hook, access core via `ctx.kernel` instead of importing `OpenACPCore` type. The adapter constructor that takes `core` should receive it from `ctx.kernel`. + +Also update `essential: true` to `essential: false` (external plugins should not be marked essential). + +Remove `pluginDependencies` on `@openacp/security` and `@openacp/notifications` if they are only needed at runtime via `ctx.getService()` — external plugins should use optional lookups rather than hard dependencies on built-in plugins. + +- [ ] **Step 4: Rewrite imports in `adapter.ts`** + +Change all `../../core/...` imports to `@openacp/plugin-sdk`: + +```typescript +import type { OutgoingMessage, PermissionRequest, NotificationMessage, Attachment } from '@openacp/plugin-sdk' +import type { AdapterCapabilities } from '@openacp/plugin-sdk' +import type { FileServiceInterface } from '@openacp/plugin-sdk' +import { MessagingAdapter, type MessagingAdapterConfig } from '@openacp/plugin-sdk' +import type { DisplayVerbosity } from '@openacp/plugin-sdk' +import type { IRenderer } from '@openacp/plugin-sdk' +``` + +Consolidate into a single import: +```typescript +import { + MessagingAdapter, + type MessagingAdapterConfig, + type OutgoingMessage, + type PermissionRequest, + type NotificationMessage, + type Attachment, + type AdapterCapabilities, + type FileServiceInterface, + type DisplayVerbosity, + type IRenderer, +} from '@openacp/plugin-sdk' +``` + +Remove `import { createChildLogger } from '../../core/utils/log.js'`. The adapter receives a logger from the plugin context. Update the constructor to accept a logger parameter, or use a module-level logger approach. The `index.ts` setup() hook should pass `ctx.log` to the adapter constructor. + +Remove `import type { OpenACPCore } from '../../core/core.js'`. The adapter constructor currently takes `OpenACPCore` — change the type to `any` or create a minimal interface for what the adapter actually uses from core (typically `handleNewSession`, `handleIncomingMessage`, `findSession`, `handlePermissionResponse`). + +- [ ] **Step 5: Rewrite imports in `renderer.ts`** + +Change: +```typescript +import { BaseRenderer, type RenderedMessage, type RenderedPermission } from '../../core/adapter-primitives/rendering/renderer.js' +import type { OutgoingMessage, PermissionRequest, NotificationMessage } from '../../core/types.js' +import type { DisplayVerbosity } from '../../core/adapter-primitives/format-types.js' +``` +To: +```typescript +import { + BaseRenderer, + type RenderedMessage, + type RenderedPermission, + type OutgoingMessage, + type PermissionRequest, + type NotificationMessage, + type DisplayVerbosity, +} from '@openacp/plugin-sdk' +``` + +- [ ] **Step 6: Rewrite imports in `formatter.ts`** + +Change: +```typescript +import type { OutgoingMessage, PermissionRequest } from '../../core/types.js' +``` +To: +```typescript +import type { OutgoingMessage, PermissionRequest } from '@openacp/plugin-sdk' +``` + +- [ ] **Step 7: Rewrite imports in `event-router.ts`** + +Remove `import { createChildLogger } from '../../core/utils/log.js'`. The event router should accept a logger via constructor parameter instead. + +- [ ] **Step 8: Rewrite imports in `text-buffer.ts`** + +Remove `import { createChildLogger } from '../../core/utils/log.js'`. Accept logger via constructor parameter. + +- [ ] **Step 9: Verify no remaining core imports** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin +grep -rn "from '../../core" src/ --include="*.ts" | grep -v __tests__ +``` + +Expected: No matches. + +- [ ] **Step 10: Build** + +```bash +npm run build +``` + +Expected: Build succeeds. If there are type errors, fix them by checking what the SDK actually exports and adjusting imports. + +- [ ] **Step 11: Commit** + +```bash +git add src/ +git commit -m "feat: add Slack plugin source with @openacp/plugin-sdk imports" +``` + +--- + +### Task 4: Copy and Rewrite Slack Plugin Tests + +**Files:** +- Create: `slack-plugin/src/__tests__/adapter-lifecycle.test.ts` +- Create: `slack-plugin/src/__tests__/channel-manager.test.ts` +- Create: `slack-plugin/src/__tests__/conformance.test.ts` +- Create: `slack-plugin/src/__tests__/event-router.test.ts` +- Create: `slack-plugin/src/__tests__/formatter.test.ts` +- Create: `slack-plugin/src/__tests__/permission-handler.test.ts` +- Create: `slack-plugin/src/__tests__/send-queue.test.ts` +- Create: `slack-plugin/src/__tests__/slack-voice.test.ts` +- Create: `slack-plugin/src/__tests__/slug.test.ts` +- Create: `slack-plugin/src/__tests__/text-buffer.test.ts` + +- [ ] **Step 1: Copy all test files** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group +cp OpenACP/src/plugins/slack/__tests__/*.test.ts slack-plugin/src/__tests__/ +``` + +- [ ] **Step 2: Rewrite test imports** + +In each test file, change any `../../core/...` imports to `@openacp/plugin-sdk` or `@openacp/plugin-sdk/testing`: + +```bash +cd slack-plugin +# Find all core imports in test files +grep -rn "from '.*core" src/__tests__/ --include="*.ts" +``` + +For each match, apply the same import rewrite rules as Task 3. Additionally: +- `createTestContext` → `from '@openacp/plugin-sdk/testing'` +- `createTestInstallContext` → `from '@openacp/plugin-sdk/testing'` +- `mockServices` → `from '@openacp/plugin-sdk/testing'` + +Change relative imports to source modules (e.g., `from '../../plugins/slack/adapter.js'`) to local relative paths (e.g., `from '../adapter.js'`). + +- [ ] **Step 3: Update conformance test** + +The `conformance.test.ts` imports `runAdapterConformanceTests` from core. Check if this is exported by the SDK. If not, either: +- Add it to SDK exports, or +- Skip this test for now and note it as a follow-up + +- [ ] **Step 4: Run tests** + +```bash +npm test +``` + +Expected: All tests pass. Fix any import errors or type mismatches. + +- [ ] **Step 5: Commit** + +```bash +git add src/__tests__/ +git commit -m "test: add Slack plugin tests with SDK imports" +``` + +--- + +### Task 5: Remove Slack Built-in Plugin from OpenACP + +**Files:** +- Delete: `OpenACP/src/plugins/slack/` (entire directory) +- Modify: `OpenACP/src/plugins/index.ts` +- Modify: `OpenACP/src/plugins/core-plugins.ts` +- Modify: `OpenACP/package.json` (remove Slack deps if unused elsewhere) + +- [ ] **Step 1: Delete Slack plugin directory** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP +rm -rf src/plugins/slack/ +``` + +- [ ] **Step 2: Update `src/plugins/index.ts`** + +Remove the Slack import and entry from the `builtInPlugins` array. The file imports all 10 plugins — remove the Slack one so only 9 remain. + +Remove: +```typescript +import { createSlackPlugin } from './slack/index.js' +``` +And remove the `createSlackPlugin()` entry from the `builtInPlugins` array. + +- [ ] **Step 3: Update `src/plugins/core-plugins.ts`** + +Same change — remove Slack from the `corePlugins` array. + +- [ ] **Step 4: Check if Slack dependencies are used elsewhere** + +```bash +grep -rn "@slack/bolt\|@slack/web-api" src/ --include="*.ts" | grep -v plugins/slack +``` + +If no results: remove `@slack/bolt` and `@slack/web-api` from `package.json` dependencies. + +```bash +pnpm remove @slack/bolt @slack/web-api +``` + +If other files still use them: leave them. + +- [ ] **Step 5: Build and test** + +```bash +pnpm build +pnpm test +``` + +Expected: Build succeeds, all tests pass (no tests should reference Slack since the tests were in `src/plugins/slack/__tests__/` which was deleted). + +- [ ] **Step 6: Commit** + +```bash +git add -A +git commit -m "feat: remove Slack built-in plugin (extracted to @openacp/adapter-slack)" +``` + +--- + +### Task 6: Add Registry Discovery to Setup Wizard + +**Files:** +- Modify: `OpenACP/src/core/setup/wizard.ts` +- Modify: `OpenACP/src/core/setup/setup-channels.ts` +- Modify: `OpenACP/src/core/setup/types.ts` + +- [ ] **Step 1: Update `src/core/setup/types.ts`** + +The `ChannelId` type is currently `"telegram" | "discord"`. For community plugins, we need a dynamic approach. Add a type for community channel options: + +```typescript +export interface CommunityAdapterOption { + name: string // npm package name, e.g. "@openacp/adapter-slack" + displayName: string // e.g. "Slack Adapter" + icon: string // e.g. "💬" + verified: boolean +} +``` + +- [ ] **Step 2: Update `wizard.ts` — fetch community adapters** + +Add a helper function that queries the registry for verified adapter plugins: + +```typescript +import { RegistryClient } from '../plugin/registry-client.js' + +async function fetchCommunityAdapters(): Promise { + try { + const client = new RegistryClient() + const registry = await client.getRegistry() + return registry.plugins + .filter(p => p.category === 'adapter' && p.verified) + .map(p => ({ + name: p.npm, + displayName: p.displayName ?? p.name, + icon: p.icon, + verified: p.verified, + })) + } catch { + // Offline or registry unavailable — graceful fallback + return [] + } +} +``` + +- [ ] **Step 3: Update `wizard.ts` — merge community adapters into channel selection** + +In `runSetup()`, replace the hardcoded `clack.select` options with dynamic options that include community adapters: + +```typescript +const communityAdapters = await fetchCommunityAdapters() + +const builtInOptions = [ + { label: 'Telegram', value: 'telegram' }, + { label: 'Discord', value: 'discord' }, + { label: 'Both (Telegram + Discord)', value: 'both' }, +] + +const communityOptions = communityAdapters.map(a => ({ + label: `${a.icon} ${a.displayName}${a.verified ? ' (verified)' : ''}`, + value: `community:${a.name}`, // prefix to distinguish from built-in +})) + +const channelChoice = guardCancel( + await clack.select({ + message: 'Which messaging platform do you want to use?', + options: [ + ...builtInOptions, + ...(communityOptions.length > 0 + ? [{ label: '── Community Adapters ──', value: '__separator__', disabled: true }, ...communityOptions] + : []), + ], + }), +) as string +``` + +- [ ] **Step 4: Update `wizard.ts` — handle community adapter selection** + +Add handling for `community:` prefixed selections: + +```typescript +if (typeof channelChoice === 'string' && channelChoice.startsWith('community:')) { + const npmPackage = channelChoice.replace('community:', '') + currentStep++ + + if (settingsManager && pluginRegistry) { + // Auto-install the plugin from npm + clack.spinner() + const s = clack.spinner() + s.start(`Installing ${npmPackage}...`) + + try { + const { exec } = await import('node:child_process') + const { promisify } = await import('node:util') + const execAsync = promisify(exec) + const pluginsDir = path.join(os.homedir(), '.openacp', 'plugins') + await execAsync(`npm install ${npmPackage}`, { cwd: pluginsDir }) + s.stop(ok(`${npmPackage} installed`)) + } catch (err) { + s.stop(fail(`Failed to install ${npmPackage}: ${(err as Error).message}`)) + return false + } + + // Load and run the plugin's install() hook + const pluginModule = await import(npmPackage) + const plugin = pluginModule.default + if (plugin?.install) { + const { createInstallContext } = await import('../plugin/install-context.js') + const ctx = createInstallContext({ + pluginName: plugin.name, + settingsManager, + basePath: settingsManager.getBasePath(), + }) + await plugin.install(ctx) + pluginRegistry.register(plugin.name, { + version: plugin.version, + source: 'npm', + enabled: true, + settingsPath: settingsManager.getSettingsPath(plugin.name), + description: plugin.description, + }) + } + } +} +``` + +Note: The npm install approach should match how `openacp plugin install` works in `src/cli.ts`. Check the existing install command implementation and reuse the same logic. + +- [ ] **Step 5: Update `setup-channels.ts` — show installed community adapters** + +In `configureChannels()`, add installed community plugins to the channel list. After the built-in channels, query `PluginRegistry` for installed npm plugins with `adapter` in the name: + +Update `configureViaPlugin()` to handle community plugins by dynamically importing them from `node_modules`: + +```typescript +async function configureViaPlugin(channelId: string): Promise { + // Built-in plugins + const pluginMap: Record = { + telegram: { importPath: '../../plugins/telegram/index.js', name: '@openacp/telegram' }, + discord: { importPath: '../../plugins/discord/index.js', name: '@openacp/discord' }, + } + + const pluginInfo = pluginMap[channelId] + + let plugin: any + if (pluginInfo) { + const pluginModule = await import(pluginInfo.importPath) + plugin = pluginModule.default + } else { + // Community plugin — channelId is the npm package name + try { + const pluginModule = await import(channelId) + plugin = pluginModule.default + } catch { + return + } + } + + if (plugin?.configure) { + const { SettingsManager } = await import('../plugin/settings-manager.js') + const { createInstallContext } = await import('../plugin/install-context.js') + const basePath = path.join(os.homedir(), '.openacp', 'plugins') + const settingsManager = new SettingsManager(basePath) + const ctx = createInstallContext({ + pluginName: plugin.name, + settingsManager, + basePath, + }) + await plugin.configure(ctx) + } +} +``` + +- [ ] **Step 6: Build and test** + +```bash +pnpm build +pnpm test +``` + +Expected: Build succeeds, all tests pass. + +- [ ] **Step 7: Commit** + +```bash +git add src/core/setup/ +git commit -m "feat(wizard): discover community adapter plugins from registry" +``` + +--- + +### Task 7: Update Plugin Registry Manifest + +**Files:** +- Modify: `plugin-registry/plugins/openacp--adapter-slack.json` + +- [ ] **Step 1: Create new branch** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/plugin-registry +git checkout main +git checkout -b feat/update-slack-manifest-redesign +``` + +- [ ] **Step 2: Update manifest** + +Edit `plugins/openacp--adapter-slack.json`: + +```json +{ + "name": "adapter-slack", + "displayName": "Slack Adapter", + "description": "Slack messaging platform adapter for OpenACP", + "npm": "@openacp/adapter-slack", + "repository": "https://github.com/Open-ACP/slack-adapter", + "author": { + "name": "OpenACP", + "github": "Open-ACP" + }, + "version": "0.1.0", + "minCliVersion": "2026.0327.1", + "category": "adapter", + "tags": ["slack", "messaging", "adapter"], + "icon": "💬", + "license": "MIT", + "verified": true, + "createdAt": "2026-03-27T00:00:00Z" +} +``` + +Changes from old manifest: +- `repository` → updated to correct URL +- `minCliVersion` → updated to `2026.0327.1` (redesign branch version) + +- [ ] **Step 3: Rebuild registry** + +```bash +npm run build +``` + +This regenerates `registry.json` from the manifests. + +- [ ] **Step 4: Commit** + +```bash +git add plugins/openacp--adapter-slack.json registry.json +git commit -m "feat: update Slack adapter manifest for redesign architecture" +``` + +--- + +### Task 8: Final Verification + +- [ ] **Step 1: Verify OpenACP builds and tests pass** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP +pnpm build +pnpm test +``` + +Expected: All pass. No references to Slack in core. + +- [ ] **Step 2: Verify slack-plugin builds and tests pass** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin +npm run build +npm test +``` + +Expected: All 10 test files pass. + +- [ ] **Step 3: Verify no remaining core imports in slack-plugin** + +```bash +grep -rn "from '.*core\|from '.*plugin/types\|from '.*config/config" slack-plugin/src/ --include="*.ts" +``` + +Expected: No matches. + +- [ ] **Step 4: Verify SDK exports are complete** + +```bash +cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP +grep -c "DisplayVerbosity\|AdapterCapabilities\|Attachment\|MessagingAdapterConfig\|IRenderer\|RenderedMessage\|RenderedPermission" src/packages/plugin-sdk/src/index.ts +``` + +Expected: At least 7 matches (one per type). + +- [ ] **Step 5: Create PRs** + +Create PRs for each repo: + +1. **OpenACP**: `feat/extract-slack-plugin` → `redesign/microkernel-plugin-architecture` +2. **slack-plugin**: `feat/redesign-plugin-architecture` → `main` +3. **plugin-registry**: `feat/update-slack-manifest-redesign` → `main` + +PR descriptions should reference each other as related PRs. From ee8387383672f9d24a3f6acf7d6d208c2f91492d Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 16:12:10 +0700 Subject: [PATCH 02/10] docs: add clear repo indicators to each task in implementation plan Co-Authored-By: Claude Opus 4.6 (1M context) --- ...026-03-27-extract-slack-plugin-redesign.md | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md b/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md index d4ae148d..3be28058 100644 --- a/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md +++ b/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md @@ -9,13 +9,26 @@ **Tech Stack:** TypeScript, ESM, Vitest, `@openacp/plugin-sdk`, `@slack/bolt`, `@slack/web-api`, `@clack/prompts`, `p-queue`, `nanoid`, `zod` **Repos involved (all under `/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/`):** -- `OpenACP` — branch `redesign/microkernel-plugin-architecture` -- `slack-plugin` — create new branch from `main` -- `plugin-registry` — create new branch from `main` + +| Repo | Path | Branch | Tasks | +|------|------|--------|-------| +| `OpenACP` | `openacp-group/OpenACP` | `feat/extract-slack-plugin` (from `redesign/microkernel-plugin-architecture`) | 1, 5, 6 | +| `slack-plugin` | `openacp-group/slack-plugin` | `feat/redesign-plugin-architecture` (from `main`) | 2, 3, 4 | +| `plugin-registry` | `openacp-group/plugin-registry` | `feat/update-slack-manifest-redesign` (from `main`) | 7 | +| All repos | — | — | 8 | + +**Task execution order:** Task 1 → Task 2 → Task 3 → Task 4 → Task 5 → Task 6 → Task 7 → Task 8 + +Tasks 1 must complete before Tasks 3-4 (SDK exports needed for plugin imports). +Tasks 2-4 (slack-plugin) and Task 5 (OpenACP remove) can run in parallel. +Task 7 (plugin-registry) is independent. --- -### Task 1: Update Plugin SDK Exports (OpenACP repo) +### Task 1: Update Plugin SDK Exports + +> **REPO: `OpenACP`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP`) +> **BRANCH: `feat/extract-slack-plugin`** (created from `redesign/microkernel-plugin-architecture`) **Files:** - Modify: `OpenACP/src/packages/plugin-sdk/src/index.ts` @@ -91,6 +104,9 @@ git commit -m "feat(plugin-sdk): export adapter types for external plugins" ### Task 2: Reset slack-plugin Repo +> **REPO: `slack-plugin`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin`) +> **BRANCH: `feat/redesign-plugin-architecture`** (created from `main`) + **Files:** - Modify: `slack-plugin/package.json` - Modify: `slack-plugin/tsconfig.json` @@ -216,6 +232,10 @@ git commit -m "chore: reset repo for redesign plugin architecture" ### Task 3: Copy and Rewrite Slack Plugin Source Files +> **REPO: `slack-plugin`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin`) +> **BRANCH: `feat/redesign-plugin-architecture`** (same as Task 2) +> **SOURCE: `OpenACP/src/plugins/slack/`** (on `redesign/microkernel-plugin-architecture` branch) + **Files:** - Create: `slack-plugin/src/index.ts` - Create: `slack-plugin/src/adapter.ts` @@ -415,6 +435,10 @@ git commit -m "feat: add Slack plugin source with @openacp/plugin-sdk imports" ### Task 4: Copy and Rewrite Slack Plugin Tests +> **REPO: `slack-plugin`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin`) +> **BRANCH: `feat/redesign-plugin-architecture`** (same as Task 2) +> **SOURCE: `OpenACP/src/plugins/slack/__tests__/`** (on `redesign/microkernel-plugin-architecture` branch) + **Files:** - Create: `slack-plugin/src/__tests__/adapter-lifecycle.test.ts` - Create: `slack-plugin/src/__tests__/channel-manager.test.ts` @@ -476,6 +500,9 @@ git commit -m "test: add Slack plugin tests with SDK imports" ### Task 5: Remove Slack Built-in Plugin from OpenACP +> **REPO: `OpenACP`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP`) +> **BRANCH: `feat/extract-slack-plugin`** (same as Task 1) + **Files:** - Delete: `OpenACP/src/plugins/slack/` (entire directory) - Modify: `OpenACP/src/plugins/index.ts` @@ -537,6 +564,9 @@ git commit -m "feat: remove Slack built-in plugin (extracted to @openacp/adapter ### Task 6: Add Registry Discovery to Setup Wizard +> **REPO: `OpenACP`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP`) +> **BRANCH: `feat/extract-slack-plugin`** (same as Task 1) + **Files:** - Modify: `OpenACP/src/core/setup/wizard.ts` - Modify: `OpenACP/src/core/setup/setup-channels.ts` @@ -729,6 +759,9 @@ git commit -m "feat(wizard): discover community adapter plugins from registry" ### Task 7: Update Plugin Registry Manifest +> **REPO: `plugin-registry`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/plugin-registry`) +> **BRANCH: `feat/update-slack-manifest-redesign`** (created from `main`) + **Files:** - Modify: `plugin-registry/plugins/openacp--adapter-slack.json` @@ -789,6 +822,8 @@ git commit -m "feat: update Slack adapter manifest for redesign architecture" ### Task 8: Final Verification +> **ALL REPOS** — cross-repo verification + - [ ] **Step 1: Verify OpenACP builds and tests pass** ```bash From dc4bafbfc355205d9da55a12aa8754fd74775717 Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 16:37:42 +0700 Subject: [PATCH 03/10] feat: remove Slack built-in plugin (extracted to @openacp/adapter-slack) Co-Authored-By: Claude Sonnet 4.6 --- package.json | 2 - pnpm-lock.yaml | 4792 ++++++----------- src/main.ts | 2 - src/plugins/__tests__/plugin-wrappers.test.ts | 7 +- src/plugins/core-plugins.ts | 2 - src/plugins/index.ts | 2 - .../slack/__tests__/adapter-lifecycle.test.ts | 108 - .../slack/__tests__/channel-manager.test.ts | 124 - .../slack/__tests__/conformance.test.ts | 26 - .../slack/__tests__/event-router.test.ts | 249 - src/plugins/slack/__tests__/formatter.test.ts | 87 - .../__tests__/permission-handler.test.ts | 118 - .../slack/__tests__/send-queue.test.ts | 65 - .../slack/__tests__/slack-voice.test.ts | 112 - src/plugins/slack/__tests__/slug.test.ts | 37 - .../slack/__tests__/text-buffer.test.ts | 128 - src/plugins/slack/adapter.ts | 564 -- src/plugins/slack/channel-manager.ts | 67 - src/plugins/slack/event-router.ts | 105 - src/plugins/slack/formatter.ts | 133 - src/plugins/slack/index.ts | 147 - src/plugins/slack/permission-handler.ts | 68 - src/plugins/slack/renderer.ts | 201 - src/plugins/slack/send-queue.ts | 51 - src/plugins/slack/slug.ts | 26 - src/plugins/slack/text-buffer.ts | 102 - src/plugins/slack/types.ts | 17 - src/plugins/slack/utils.ts | 33 - 28 files changed, 1725 insertions(+), 5650 deletions(-) delete mode 100644 src/plugins/slack/__tests__/adapter-lifecycle.test.ts delete mode 100644 src/plugins/slack/__tests__/channel-manager.test.ts delete mode 100644 src/plugins/slack/__tests__/conformance.test.ts delete mode 100644 src/plugins/slack/__tests__/event-router.test.ts delete mode 100644 src/plugins/slack/__tests__/formatter.test.ts delete mode 100644 src/plugins/slack/__tests__/permission-handler.test.ts delete mode 100644 src/plugins/slack/__tests__/send-queue.test.ts delete mode 100644 src/plugins/slack/__tests__/slack-voice.test.ts delete mode 100644 src/plugins/slack/__tests__/slug.test.ts delete mode 100644 src/plugins/slack/__tests__/text-buffer.test.ts delete mode 100644 src/plugins/slack/adapter.ts delete mode 100644 src/plugins/slack/channel-manager.ts delete mode 100644 src/plugins/slack/event-router.ts delete mode 100644 src/plugins/slack/formatter.ts delete mode 100644 src/plugins/slack/index.ts delete mode 100644 src/plugins/slack/permission-handler.ts delete mode 100644 src/plugins/slack/renderer.ts delete mode 100644 src/plugins/slack/send-queue.ts delete mode 100644 src/plugins/slack/slug.ts delete mode 100644 src/plugins/slack/text-buffer.ts delete mode 100644 src/plugins/slack/types.ts delete mode 100644 src/plugins/slack/utils.ts diff --git a/package.json b/package.json index a3a45f0e..c8432ffe 100644 --- a/package.json +++ b/package.json @@ -44,11 +44,9 @@ "@clack/prompts": "^1.1.0", "@hono/node-server": "^1.19.11", "@inquirer/prompts": "^8.3.2", - "@slack/web-api": "7.15.0", "@zed-industries/claude-agent-acp": "^0.22.2", "diff": "^8.0.3", "fastest-levenshtein": "^1.0.16", - "@slack/bolt": "^4.6.0", "grammy": "^1.41.1", "hono": "^4.12.8", "msedge-tts": "^2.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d88c213..d103b7ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '9.0' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -20,12 +20,6 @@ importers: '@inquirer/prompts': specifier: ^8.3.2 version: 8.3.2(@types/node@25.5.0) - '@slack/bolt': - specifier: ^4.6.0 - version: 4.6.0(@types/express@5.0.6) - '@slack/web-api': - specifier: 7.15.0 - version: 7.15.0 '@zed-industries/claude-agent-acp': specifier: ^0.22.2 version: 0.22.2 @@ -89,7 +83,7 @@ importers: version: 9.1.7 tsup: specifier: ^8.5.1 - version: 8.5.1(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3) + version: 8.5.1(tsx@4.21.0)(typescript@5.9.3) tsx: specifier: ^4.21.0 version: 4.21.0 @@ -98,335 +92,553 @@ importers: version: 5.9.3 vitest: specifier: ^4.1.0 - version: 4.1.0(@types/node@25.5.0)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0)) - - packages/plugin-sdk: - devDependencies: - '@openacp/cli': - specifier: workspace:* - version: link:../.. - vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0) + version: 4.1.0(@types/node@25.5.0)(vite@8.0.1) packages: - '@agentclientprotocol/sdk@0.16.1': + /@agentclientprotocol/sdk@0.16.1(zod@3.25.76): resolution: {integrity: sha512-1ad+Sc/0sCtZGHthxxvgEUo5Wsbw16I+aF+YwdiLnPwkZG8KAGUEAPK6LM6Pf69lCyJPt1Aomk1d+8oE3C4ZEw==} peerDependencies: zod: ^3.25.0 || ^4.0.0 + dependencies: + zod: 3.25.76 + dev: false - '@anthropic-ai/claude-agent-sdk@0.2.76': + /@anthropic-ai/claude-agent-sdk@0.2.76(zod@3.25.76): resolution: {integrity: sha512-HZxvnT8ZWkzCnQygaYCA0dl8RSUzuVbxE1YG4ecy6vh4nQbTT36CxUxBy+QVdR12pPQluncC0mCOLhI2918Eaw==} engines: {node: '>=18.0.0'} peerDependencies: zod: ^4.0.0 + dependencies: + zod: 3.25.76 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + dev: false - '@clack/core@1.1.0': + /@clack/core@1.1.0: resolution: {integrity: sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==} + dependencies: + sisteransi: 1.0.5 + dev: false - '@clack/prompts@1.1.0': + /@clack/prompts@1.1.0: resolution: {integrity: sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==} + dependencies: + '@clack/core': 1.1.0 + sisteransi: 1.0.5 + dev: false - '@discordjs/builders@1.14.0': + /@discordjs/builders@1.14.0: resolution: {integrity: sha512-7pVKxVWkeLUtrTo9nTYkjRcJk0Hlms6lYervXAD7E7+K5lil9ms2JrEB1TalMiHvQMh7h1HJZ4fCJa0/vHpl4w==} engines: {node: '>=16.11.0'} + dependencies: + '@discordjs/formatters': 0.6.2 + '@discordjs/util': 1.2.0 + '@sapphire/shapeshift': 4.0.0 + discord-api-types: 0.38.42 + fast-deep-equal: 3.1.3 + ts-mixer: 6.0.4 + tslib: 2.8.1 + dev: false - '@discordjs/collection@1.5.3': + /@discordjs/collection@1.5.3: resolution: {integrity: sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==} engines: {node: '>=16.11.0'} + dev: false - '@discordjs/collection@2.1.1': + /@discordjs/collection@2.1.1: resolution: {integrity: sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==} engines: {node: '>=18'} + dev: false - '@discordjs/formatters@0.6.2': + /@discordjs/formatters@0.6.2: resolution: {integrity: sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ==} engines: {node: '>=16.11.0'} + dependencies: + discord-api-types: 0.38.42 + dev: false - '@discordjs/rest@2.6.1': + /@discordjs/rest@2.6.1: resolution: {integrity: sha512-wwQdgjeaoYFiaG+atbqx6aJDpqW7JHAo0HrQkBTbYzM3/PJ3GweQIpgElNcGZ26DCUOXMyawYd0YF7vtr+fZXg==} engines: {node: '>=18'} + dependencies: + '@discordjs/collection': 2.1.1 + '@discordjs/util': 1.2.0 + '@sapphire/async-queue': 1.5.5 + '@sapphire/snowflake': 3.5.5 + '@vladfrangu/async_event_emitter': 2.4.7 + discord-api-types: 0.38.42 + magic-bytes.js: 1.13.0 + tslib: 2.8.1 + undici: 6.24.1 + dev: false - '@discordjs/util@1.2.0': + /@discordjs/util@1.2.0: resolution: {integrity: sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg==} engines: {node: '>=18'} + dependencies: + discord-api-types: 0.38.42 + dev: false - '@discordjs/ws@1.2.3': + /@discordjs/ws@1.2.3: resolution: {integrity: sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==} engines: {node: '>=16.11.0'} + dependencies: + '@discordjs/collection': 2.1.1 + '@discordjs/rest': 2.6.1 + '@discordjs/util': 1.2.0 + '@sapphire/async-queue': 1.5.5 + '@types/ws': 8.18.1 + '@vladfrangu/async_event_emitter': 2.4.7 + discord-api-types: 0.38.42 + tslib: 2.8.1 + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false - '@emnapi/core@1.9.0': - resolution: {integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==} + /@emnapi/core@1.9.1: + resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} + requiresBuild: true + dependencies: + '@emnapi/wasi-threads': 1.2.0 + tslib: 2.8.1 + dev: true + optional: true - '@emnapi/runtime@1.9.0': - resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==} + /@emnapi/runtime@1.9.1: + resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + requiresBuild: true + dependencies: + tslib: 2.8.1 + dev: true + optional: true - '@emnapi/wasi-threads@1.2.0': + /@emnapi/wasi-threads@1.2.0: resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + requiresBuild: true + dependencies: + tslib: 2.8.1 + dev: true + optional: true - '@esbuild/aix-ppc64@0.27.4': + /@esbuild/aix-ppc64@0.27.4: resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] + requiresBuild: true + dev: true + optional: true - '@esbuild/android-arm64@0.27.4': + /@esbuild/android-arm64@0.27.4: resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} engines: {node: '>=18'} cpu: [arm64] os: [android] + requiresBuild: true + dev: true + optional: true - '@esbuild/android-arm@0.27.4': + /@esbuild/android-arm@0.27.4: resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} engines: {node: '>=18'} cpu: [arm] os: [android] + requiresBuild: true + dev: true + optional: true - '@esbuild/android-x64@0.27.4': + /@esbuild/android-x64@0.27.4: resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} engines: {node: '>=18'} cpu: [x64] os: [android] + requiresBuild: true + dev: true + optional: true - '@esbuild/darwin-arm64@0.27.4': + /@esbuild/darwin-arm64@0.27.4: resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + requiresBuild: true + dev: true + optional: true - '@esbuild/darwin-x64@0.27.4': + /@esbuild/darwin-x64@0.27.4: resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + requiresBuild: true + dev: true + optional: true - '@esbuild/freebsd-arm64@0.27.4': + /@esbuild/freebsd-arm64@0.27.4: resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/freebsd-x64@0.27.4': + /@esbuild/freebsd-x64@0.27.4: resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-arm64@0.27.4': + /@esbuild/linux-arm64@0.27.4: resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-arm@0.27.4': + /@esbuild/linux-arm@0.27.4: resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} engines: {node: '>=18'} cpu: [arm] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-ia32@0.27.4': + /@esbuild/linux-ia32@0.27.4: resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-loong64@0.27.4': + /@esbuild/linux-loong64@0.27.4: resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-mips64el@0.27.4': + /@esbuild/linux-mips64el@0.27.4: resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-ppc64@0.27.4': + /@esbuild/linux-ppc64@0.27.4: resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-riscv64@0.27.4': + /@esbuild/linux-riscv64@0.27.4: resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-s390x@0.27.4': + /@esbuild/linux-s390x@0.27.4: resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-x64@0.27.4': + /@esbuild/linux-x64@0.27.4: resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} engines: {node: '>=18'} cpu: [x64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/netbsd-arm64@0.27.4': + /@esbuild/netbsd-arm64@0.27.4: resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/netbsd-x64@0.27.4': + /@esbuild/netbsd-x64@0.27.4: resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/openbsd-arm64@0.27.4': + /@esbuild/openbsd-arm64@0.27.4: resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/openbsd-x64@0.27.4': + /@esbuild/openbsd-x64@0.27.4: resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/openharmony-arm64@0.27.4': + /@esbuild/openharmony-arm64@0.27.4: resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + requiresBuild: true + dev: true + optional: true - '@esbuild/sunos-x64@0.27.4': + /@esbuild/sunos-x64@0.27.4: resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + requiresBuild: true + dev: true + optional: true - '@esbuild/win32-arm64@0.27.4': + /@esbuild/win32-arm64@0.27.4: resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@esbuild/win32-ia32@0.27.4': + /@esbuild/win32-ia32@0.27.4: resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + requiresBuild: true + dev: true + optional: true - '@esbuild/win32-x64@0.27.4': + /@esbuild/win32-x64@0.27.4: resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} engines: {node: '>=18'} cpu: [x64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@eshaz/web-worker@1.2.2': + /@eshaz/web-worker@1.2.2: resolution: {integrity: sha512-WxXiHFmD9u/owrzempiDlBB1ZYqiLnm9s6aPc8AlFQalq2tKmqdmMr9GXOupDgzXtqnBipj8Un0gkIm7Sjf8mw==} + dev: false - '@grammyjs/types@3.25.0': + /@grammyjs/types@3.25.0: resolution: {integrity: sha512-iN9i5p+8ZOu9OMxWNcguojQfz4K/PDyMPOnL7PPCON+SoA/F8OKMH3uR7CVUkYfdNe0GCz8QOzAWrnqusQYFOg==} + dev: false - '@hono/node-server@1.19.11': + /@hono/node-server@1.19.11(hono@4.12.8): resolution: {integrity: sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 + dependencies: + hono: 4.12.8 + dev: false - '@img/sharp-darwin-arm64@0.34.5': + /@img/sharp-darwin-arm64@0.34.5: resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + dev: false + optional: true - '@img/sharp-darwin-x64@0.34.5': + /@img/sharp-darwin-x64@0.34.5: resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + dev: false + optional: true - '@img/sharp-libvips-darwin-arm64@1.2.4': + /@img/sharp-libvips-darwin-arm64@1.2.4: resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] + requiresBuild: true + dev: false + optional: true - '@img/sharp-libvips-darwin-x64@1.2.4': + /@img/sharp-libvips-darwin-x64@1.2.4: resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] + requiresBuild: true + dev: false + optional: true - '@img/sharp-libvips-linux-arm64@1.2.4': + /@img/sharp-libvips-linux-arm64@1.2.4: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + requiresBuild: true + dev: false + optional: true - '@img/sharp-libvips-linux-arm@1.2.4': + /@img/sharp-libvips-linux-arm@1.2.4: resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + requiresBuild: true + dev: false + optional: true - '@img/sharp-libvips-linux-x64@1.2.4': + /@img/sharp-libvips-linux-x64@1.2.4: resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + requiresBuild: true + dev: false + optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + /@img/sharp-libvips-linuxmusl-arm64@1.2.4: resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + requiresBuild: true + dev: false + optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.4': + /@img/sharp-libvips-linuxmusl-x64@1.2.4: resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + requiresBuild: true + dev: false + optional: true - '@img/sharp-linux-arm64@0.34.5': + /@img/sharp-linux-arm64@0.34.5: resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + dev: false + optional: true - '@img/sharp-linux-arm@0.34.5': + /@img/sharp-linux-arm@0.34.5: resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + dev: false + optional: true - '@img/sharp-linux-x64@0.34.5': + /@img/sharp-linux-x64@0.34.5: resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + dev: false + optional: true - '@img/sharp-linuxmusl-arm64@0.34.5': + /@img/sharp-linuxmusl-arm64@0.34.5: resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + dev: false + optional: true - '@img/sharp-linuxmusl-x64@0.34.5': + /@img/sharp-linuxmusl-x64@0.34.5: resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + dev: false + optional: true - '@img/sharp-win32-arm64@0.34.5': + /@img/sharp-win32-arm64@0.34.5: resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] + requiresBuild: true + dev: false + optional: true - '@img/sharp-win32-x64@0.34.5': + /@img/sharp-win32-x64@0.34.5: resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] + requiresBuild: true + dev: false + optional: true - '@inquirer/ansi@2.0.4': + /@inquirer/ansi@2.0.4: resolution: {integrity: sha512-DpcZrQObd7S0R/U3bFdkcT5ebRwbTTC4D3tCc1vsJizmgPLxNJBo+AAFmrZwe8zk30P2QzgzGWZ3Q9uJwWuhIg==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + dev: false - '@inquirer/checkbox@5.1.2': + /@inquirer/checkbox@5.1.2(@types/node@25.5.0): resolution: {integrity: sha512-PubpMPO2nJgMufkoB3P2wwxNXEMUXnBIKi/ACzDUYfaoPuM7gSTmuxJeMscoLVEsR4qqrCMf5p0SiYGWnVJ8kw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -434,8 +646,15 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/ansi': 2.0.4 + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/figures': 2.0.4 + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/confirm@6.0.10': + /@inquirer/confirm@6.0.10(@types/node@25.5.0): resolution: {integrity: sha512-tiNyA73pgpQ0FQ7axqtoLUe4GDYjNCDcVsbgcA5anvwg2z6i+suEngLKKJrWKJolT//GFPZHwN30binDIHgSgQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -443,8 +662,13 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/core@11.1.7': + /@inquirer/core@11.1.7(@types/node@25.5.0): resolution: {integrity: sha512-1BiBNDk9btIwYIzNZpkikIHXWeNzNncJePPqwDyVMhXhD1ebqbpn1mKGctpoqAbzywZfdG0O4tvmsGIcOevAPQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -452,8 +676,18 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/ansi': 2.0.4 + '@inquirer/figures': 2.0.4 + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.0 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + dev: false - '@inquirer/editor@5.0.10': + /@inquirer/editor@5.0.10(@types/node@25.5.0): resolution: {integrity: sha512-VJx4XyaKea7t8hEApTw5dxeIyMtWXre2OiyJcICCRZI4hkoHsMoCnl/KbUnJJExLbH9csLLHMVR144ZhFE1CwA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -461,8 +695,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/external-editor': 2.0.4(@types/node@25.5.0) + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/expand@5.0.10': + /@inquirer/expand@5.0.10(@types/node@25.5.0): resolution: {integrity: sha512-fC0UHJPXsTRvY2fObiwuQYaAnHrp3aDqfwKUJSdfpgv18QUG054ezGbaRNStk/BKD5IPijeMKWej8VV8O5Q/eQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -470,8 +710,13 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/external-editor@2.0.4': + /@inquirer/external-editor@2.0.4(@types/node@25.5.0): resolution: {integrity: sha512-Prenuv9C1PHj2Itx0BcAOVBTonz02Hc2Nd2DbU67PdGUaqn0nPCnV34oDyyoaZHnmfRxkpuhh/u51ThkrO+RdA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -479,12 +724,18 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@types/node': 25.5.0 + chardet: 2.1.1 + iconv-lite: 0.7.2 + dev: false - '@inquirer/figures@2.0.4': + /@inquirer/figures@2.0.4: resolution: {integrity: sha512-eLBsjlS7rPS3WEhmOmh1znQ5IsQrxWzxWDxO51e4urv+iVrSnIHbq4zqJIOiyNdYLa+BVjwOtdetcQx1lWPpiQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + dev: false - '@inquirer/input@5.0.10': + /@inquirer/input@5.0.10(@types/node@25.5.0): resolution: {integrity: sha512-nvZ6qEVeX/zVtZ1dY2hTGDQpVGD3R7MYPLODPgKO8Y+RAqxkrP3i/3NwF3fZpLdaMiNuK0z2NaYIx9tPwiSegQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -492,8 +743,13 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/number@4.0.10': + /@inquirer/number@4.0.10(@types/node@25.5.0): resolution: {integrity: sha512-Ht8OQstxiS3APMGjHV0aYAjRAysidWdwurWEo2i8yI5xbhOBWqizT0+MU1S2GCcuhIBg+3SgWVjEoXgfhY+XaA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -501,8 +757,13 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/password@5.0.10': + /@inquirer/password@5.0.10(@types/node@25.5.0): resolution: {integrity: sha512-QbNyvIE8q2GTqKLYSsA8ATG+eETo+m31DSR0+AU7x3d2FhaTWzqQek80dj3JGTo743kQc6mhBR0erMjYw5jQ0A==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -510,8 +771,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/ansi': 2.0.4 + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/prompts@8.3.2': + /@inquirer/prompts@8.3.2(@types/node@25.5.0): resolution: {integrity: sha512-yFroiSj2iiBFlm59amdTvAcQFvWS6ph5oKESls/uqPBect7rTU2GbjyZO2DqxMGuIwVA8z0P4K6ViPcd/cp+0w==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -519,8 +786,21 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/checkbox': 5.1.2(@types/node@25.5.0) + '@inquirer/confirm': 6.0.10(@types/node@25.5.0) + '@inquirer/editor': 5.0.10(@types/node@25.5.0) + '@inquirer/expand': 5.0.10(@types/node@25.5.0) + '@inquirer/input': 5.0.10(@types/node@25.5.0) + '@inquirer/number': 4.0.10(@types/node@25.5.0) + '@inquirer/password': 5.0.10(@types/node@25.5.0) + '@inquirer/rawlist': 5.2.6(@types/node@25.5.0) + '@inquirer/search': 4.1.6(@types/node@25.5.0) + '@inquirer/select': 5.1.2(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/rawlist@5.2.6': + /@inquirer/rawlist@5.2.6(@types/node@25.5.0): resolution: {integrity: sha512-jfw0MLJ5TilNsa9zlJ6nmRM0ZFVZhhTICt4/6CU2Dv1ndY7l3sqqo1gIYZyMMDw0LvE1u1nzJNisfHEhJIxq5w==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -528,8 +808,13 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/search@4.1.6': + /@inquirer/search@4.1.6(@types/node@25.5.0): resolution: {integrity: sha512-3/6kTRae98hhDevENScy7cdFEuURnSpM3JbBNg8yfXLw88HgTOl+neUuy/l9W0No5NzGsLVydhBzTIxZP7yChQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -537,8 +822,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/figures': 2.0.4 + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/select@5.1.2': + /@inquirer/select@5.1.2(@types/node@25.5.0): resolution: {integrity: sha512-kTK8YIkHV+f02y7bWCh7E0u2/11lul5WepVTclr3UMBtBr05PgcZNWfMa7FY57ihpQFQH/spLMHTcr0rXy50tA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -546,8 +837,15 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@inquirer/ansi': 2.0.4 + '@inquirer/core': 11.1.7(@types/node@25.5.0) + '@inquirer/figures': 2.0.4 + '@inquirer/type': 4.0.4(@types/node@25.5.0) + '@types/node': 25.5.0 + dev: false - '@inquirer/type@4.0.4': + /@inquirer/type@4.0.4(@types/node@25.5.0): resolution: {integrity: sha512-PamArxO3cFJZoOzspzo6cxVlLeIftyBsZw/S9bKY5DzxqJVZgjoj1oP8d0rskKtp7sZxBycsoer1g6UeJV1BBA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: @@ -555,409 +853,552 @@ packages: peerDependenciesMeta: '@types/node': optional: true + dependencies: + '@types/node': 25.5.0 + dev: false - '@jridgewell/gen-mapping@0.3.13': + /@jridgewell/gen-mapping@0.3.13: resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + dev: true - '@jridgewell/resolve-uri@3.1.2': + /@jridgewell/resolve-uri@3.1.2: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} + dev: true - '@jridgewell/sourcemap-codec@1.5.5': + /@jridgewell/sourcemap-codec@1.5.5: resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + dev: true - '@jridgewell/trace-mapping@0.3.31': + /@jridgewell/trace-mapping@0.3.31: resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + dev: true - '@napi-rs/wasm-runtime@1.1.1': + /@napi-rs/wasm-runtime@1.1.1: resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + requiresBuild: true + dependencies: + '@emnapi/core': 1.9.1 + '@emnapi/runtime': 1.9.1 + '@tybys/wasm-util': 0.10.1 + dev: true + optional: true - '@oxc-project/types@0.120.0': + /@oxc-project/types@0.120.0: resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} + dev: true - '@pinojs/redact@0.4.0': + /@pinojs/redact@0.4.0: resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + dev: false - '@rolldown/binding-android-arm64@1.0.0-rc.10': + /@rolldown/binding-android-arm64@1.0.0-rc.10: resolution: {integrity: sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.10': + /@rolldown/binding-darwin-arm64@1.0.0-rc.10: resolution: {integrity: sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w==} engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] os: [darwin] + dev: true - '@rolldown/binding-darwin-x64@1.0.0-rc.10': + /@rolldown/binding-darwin-x64@1.0.0-rc.10: resolution: {integrity: sha512-rI15NcM1mA48lqrIxVkHfAqcyFLcQwyXWThy+BQ5+mkKKPvSO26ir+ZDp36AgYoYVkqvMcdS8zOE6SeBsR9e8A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.10': + /@rolldown/binding-freebsd-x64@1.0.0-rc.10: resolution: {integrity: sha512-XZRXHdTa+4ME1MuDVp021+doQ+z6Ei4CCFmNc5/sKbqb8YmkiJdj8QKlV3rCI0AJtAeSB5n0WGPuJWNL9p/L2w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10': + /@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10: resolution: {integrity: sha512-R0SQMRluISSLzFE20sPWYHVmJdDQnRyc/FzSCN72BqQmh2SOZUFG+N3/vBZpR4C6WpEUVYJLrYUXaj43sJsNLA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10': + /@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10: resolution: {integrity: sha512-Y1reMrV/o+cwpduYhJuOE3OMKx32RMYCidf14y+HssARRmhDuWXJ4yVguDg2R/8SyyGNo+auzz64LnPK9Hq6jg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.10': + /@rolldown/binding-linux-arm64-musl@1.0.0-rc.10: resolution: {integrity: sha512-vELN+HNb2IzuzSBUOD4NHmP9yrGwl1DVM29wlQvx1OLSclL0NgVWnVDKl/8tEks79EFek/kebQKnNJkIAA4W2g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10': + /@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10: resolution: {integrity: sha512-ZqrufYTgzxbHwpqOjzSsb0UV/aV2TFIY5rP8HdsiPTv/CuAgCRjM6s9cYFwQ4CNH+hf9Y4erHW1GjZuZ7WoI7w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10': + /@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10: resolution: {integrity: sha512-gSlmVS1FZJSRicA6IyjoRoKAFK7IIHBs7xJuHRSmjImqk3mPPWbR7RhbnfH2G6bcmMEllCt2vQ/7u9e6bBnByg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.10': + /@rolldown/binding-linux-x64-gnu@1.0.0-rc.10: resolution: {integrity: sha512-eOCKUpluKgfObT2pHjztnaWEIbUabWzk3qPZ5PuacuPmr4+JtQG4k2vGTY0H15edaTnicgU428XW/IH6AimcQw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.10': + /@rolldown/binding-linux-x64-musl@1.0.0-rc.10: resolution: {integrity: sha512-Xdf2jQbfQowJnLcgYfD/m0Uu0Qj5OdxKallD78/IPPfzaiaI4KRAwZzHcKQ4ig1gtg1SuzC7jovNiM2TzQsBXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.10': + /@rolldown/binding-openharmony-arm64@1.0.0-rc.10: resolution: {integrity: sha512-o1hYe8hLi1EY6jgPFyxQgQ1wcycX+qz8eEbVmot2hFkgUzPxy9+kF0u0NIQBeDq+Mko47AkaFFaChcvZa9UX9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.10': + /@rolldown/binding-wasm32-wasi@1.0.0-rc.10: resolution: {integrity: sha512-Ugv9o7qYJudqQO5Y5y2N2SOo6S4WiqiNOpuQyoPInnhVzCY+wi/GHltcLHypG9DEUYMB0iTB/huJrpadiAcNcA==} engines: {node: '>=14.0.0'} cpu: [wasm32] + requiresBuild: true + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + dev: true + optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10': + /@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10: resolution: {integrity: sha512-7UODQb4fQUNT/vmgDZBl3XOBAIOutP5R3O/rkxg0aLfEGQ4opbCgU5vOw/scPe4xOqBwL9fw7/RP1vAMZ6QlAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.10': + /@rolldown/binding-win32-x64-msvc@1.0.0-rc.10: resolution: {integrity: sha512-PYxKHMVHOb5NJuDL53vBUl1VwUjymDcYI6rzpIni0C9+9mTiJedvUxSk7/RPp7OOAm3v+EjgMu9bIy3N6b408w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@rolldown/pluginutils@1.0.0-rc.10': + /@rolldown/pluginutils@1.0.0-rc.10: resolution: {integrity: sha512-UkVDEFk1w3mveXeKgaTuYfKWtPbvgck1dT8TUG3bnccrH0XtLTuAyfCoks4Q/M5ZGToSVJTIQYCzy2g/atAOeg==} + dev: true - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + /@rollup/rollup-android-arm-eabi@4.60.0: + resolution: {integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==} cpu: [arm] os: [android] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + /@rollup/rollup-android-arm64@4.60.0: + resolution: {integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==} cpu: [arm64] os: [android] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + /@rollup/rollup-darwin-arm64@4.60.0: + resolution: {integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==} cpu: [arm64] os: [darwin] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + /@rollup/rollup-darwin-x64@4.60.0: + resolution: {integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==} cpu: [x64] os: [darwin] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + /@rollup/rollup-freebsd-arm64@4.60.0: + resolution: {integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==} cpu: [arm64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + /@rollup/rollup-freebsd-x64@4.60.0: + resolution: {integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==} cpu: [x64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + /@rollup/rollup-linux-arm-gnueabihf@4.60.0: + resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==} cpu: [arm] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + /@rollup/rollup-linux-arm-musleabihf@4.60.0: + resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==} cpu: [arm] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + /@rollup/rollup-linux-arm64-gnu@4.60.0: + resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==} cpu: [arm64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + /@rollup/rollup-linux-arm64-musl@4.60.0: + resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==} cpu: [arm64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + /@rollup/rollup-linux-loong64-gnu@4.60.0: + resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==} cpu: [loong64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + /@rollup/rollup-linux-loong64-musl@4.60.0: + resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==} cpu: [loong64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + /@rollup/rollup-linux-ppc64-gnu@4.60.0: + resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==} cpu: [ppc64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + /@rollup/rollup-linux-ppc64-musl@4.60.0: + resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==} cpu: [ppc64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + /@rollup/rollup-linux-riscv64-gnu@4.60.0: + resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==} cpu: [riscv64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + /@rollup/rollup-linux-riscv64-musl@4.60.0: + resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==} cpu: [riscv64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + /@rollup/rollup-linux-s390x-gnu@4.60.0: + resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==} cpu: [s390x] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + /@rollup/rollup-linux-x64-gnu@4.60.0: + resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==} cpu: [x64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + /@rollup/rollup-linux-x64-musl@4.60.0: + resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==} cpu: [x64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + /@rollup/rollup-openbsd-x64@4.60.0: + resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==} cpu: [x64] os: [openbsd] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + /@rollup/rollup-openharmony-arm64@4.60.0: + resolution: {integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==} cpu: [arm64] os: [openharmony] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + /@rollup/rollup-win32-arm64-msvc@4.60.0: + resolution: {integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==} cpu: [arm64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + /@rollup/rollup-win32-ia32-msvc@4.60.0: + resolution: {integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==} cpu: [ia32] os: [win32] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + /@rollup/rollup-win32-x64-gnu@4.60.0: + resolution: {integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==} cpu: [x64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + /@rollup/rollup-win32-x64-msvc@4.60.0: + resolution: {integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==} cpu: [x64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@sapphire/async-queue@1.5.5': + /@sapphire/async-queue@1.5.5: resolution: {integrity: sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false - '@sapphire/shapeshift@4.0.0': + /@sapphire/shapeshift@4.0.0: resolution: {integrity: sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==} engines: {node: '>=v16'} + dependencies: + fast-deep-equal: 3.1.3 + lodash: 4.17.23 + dev: false - '@sapphire/snowflake@3.5.3': + /@sapphire/snowflake@3.5.3: resolution: {integrity: sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false - '@sapphire/snowflake@3.5.5': + /@sapphire/snowflake@3.5.5: resolution: {integrity: sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false - '@shikijs/core@4.0.2': + /@shikijs/core@4.0.2: resolution: {integrity: sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==} engines: {node: '>=20'} + dependencies: + '@shikijs/primitive': 4.0.2 + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + dev: false + + /@shikijs/engine-javascript@4.0.2: + resolution: {integrity: sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==} + engines: {node: '>=20'} + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.5 + dev: false - '@shikijs/engine-javascript@4.0.2': - resolution: {integrity: sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==} - engines: {node: '>=20'} - - '@shikijs/engine-oniguruma@4.0.2': + /@shikijs/engine-oniguruma@4.0.2: resolution: {integrity: sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==} engines: {node: '>=20'} + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + dev: false - '@shikijs/langs@4.0.2': + /@shikijs/langs@4.0.2: resolution: {integrity: sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==} engines: {node: '>=20'} + dependencies: + '@shikijs/types': 4.0.2 + dev: false - '@shikijs/primitive@4.0.2': + /@shikijs/primitive@4.0.2: resolution: {integrity: sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==} engines: {node: '>=20'} + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + dev: false - '@shikijs/themes@4.0.2': + /@shikijs/themes@4.0.2: resolution: {integrity: sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==} engines: {node: '>=20'} + dependencies: + '@shikijs/types': 4.0.2 + dev: false - '@shikijs/types@4.0.2': + /@shikijs/types@4.0.2: resolution: {integrity: sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==} engines: {node: '>=20'} + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + dev: false - '@shikijs/vscode-textmate@10.0.2': + /@shikijs/vscode-textmate@10.0.2: resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + dev: false - '@slack/bolt@4.6.0': - resolution: {integrity: sha512-xPgfUs2+OXSugz54Ky07pA890+Qydk22SYToi8uGpXeHSt1JWwFJkRyd/9Vlg5I1AdfdpGXExDpwnbuN9Q/2dQ==} - engines: {node: '>=18', npm: '>=8.6.0'} - peerDependencies: - '@types/express': ^5.0.0 - - '@slack/logger@4.0.1': - resolution: {integrity: sha512-6cmdPrV/RYfd2U0mDGiMK8S7OJqpCTm7enMLRR3edccsPX8j7zXTLnaEF4fhxxJJTAIOil6+qZrnUPTuaLvwrQ==} - engines: {node: '>= 18', npm: '>= 8.6.0'} - - '@slack/oauth@3.0.5': - resolution: {integrity: sha512-exqFQySKhNDptWYSWhvRUJ4/+ndu2gayIy7vg/JfmJq3wGtGdHk531P96fAZyBm5c1Le3yaPYqv92rL4COlU3A==} - engines: {node: '>=18', npm: '>=8.6.0'} - - '@slack/socket-mode@2.0.6': - resolution: {integrity: sha512-Aj5RO3MoYVJ+b2tUjHUXuA3tiIaCUMOf1Ss5tPiz29XYVUi6qNac2A8ulcU1pUPERpXVHTmT1XW6HzQIO74daQ==} - engines: {node: '>= 18', npm: '>= 8.6.0'} - - '@slack/types@2.20.1': - resolution: {integrity: sha512-eWX2mdt1ktpn8+40iiMc404uGrih+2fxiky3zBcPjtXKj6HLRdYlmhrPkJi7JTJm8dpXR6BWVWEDBXtaWMKD6A==} - engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} - - '@slack/web-api@7.15.0': - resolution: {integrity: sha512-va7zYIt3QHG1x9M/jqXXRPFMoOVlVSSRHC5YH+DzKYsrz5xUKOA3lR4THsu/Zxha9N1jOndbKFKLtr0WOPW1Vw==} - engines: {node: '>= 18', npm: '>= 8.6.0'} - - '@standard-schema/spec@1.1.0': + /@standard-schema/spec@1.1.0: resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + dev: true - '@tybys/wasm-util@0.10.1': + /@tybys/wasm-util@0.10.1: resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + requiresBuild: true + dependencies: + tslib: 2.8.1 + dev: true + optional: true - '@types/body-parser@1.19.6': - resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} - - '@types/chai@5.2.3': + /@types/chai@5.2.3: resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + dev: true - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/deep-eql@4.0.2': + /@types/deep-eql@4.0.2: resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + dev: true - '@types/diff@8.0.0': + /@types/diff@8.0.0: resolution: {integrity: sha512-o7jqJM04gfaYrdCecCVMbZhNdG6T1MHg/oQoRFdERLV+4d+V7FijhiEAbFu0Usww84Yijk9yH58U4Jk4HbtzZw==} deprecated: This is a stub types definition. diff provides its own type definitions, so you do not need this installed. + dependencies: + diff: 8.0.3 + dev: true - '@types/estree@1.0.8': + /@types/estree@1.0.8: resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + dev: true - '@types/express-serve-static-core@5.1.1': - resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} - - '@types/express@5.0.6': - resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} - - '@types/hast@3.0.4': + /@types/hast@3.0.4: resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + dependencies: + '@types/unist': 3.0.3 + dev: false - '@types/http-errors@2.0.5': - resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} - - '@types/jsonwebtoken@9.0.10': - resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} - - '@types/mdast@4.0.4': + /@types/mdast@4.0.4: resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + dependencies: + '@types/unist': 3.0.3 + dev: false - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - - '@types/node@25.5.0': + /@types/node@25.5.0: resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} + dependencies: + undici-types: 7.18.2 - '@types/qs@6.15.0': - resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - - '@types/retry@0.12.0': - resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - - '@types/send@1.2.1': - resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} - - '@types/serve-static@2.2.0': - resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} - - '@types/unist@3.0.3': + /@types/unist@3.0.3: resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + dev: false - '@types/ws@8.18.1': + /@types/ws@8.18.1: resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + dependencies: + '@types/node': 25.5.0 + dev: false - '@ungap/structured-clone@1.3.0': + /@ungap/structured-clone@1.3.0: resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + dev: false - '@vitest/expect@3.2.4': - resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - - '@vitest/expect@4.1.0': + /@vitest/expect@4.1.0: resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 + chai: 6.2.2 + tinyrainbow: 3.1.0 + dev: true - '@vitest/mocker@3.2.4': - resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} - peerDependencies: - msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - '@vitest/mocker@4.1.0': + /@vitest/mocker@4.1.0(vite@8.0.1): resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} peerDependencies: msw: ^2.4.9 @@ -967,214 +1408,250 @@ packages: optional: true vite: optional: true + dependencies: + '@vitest/spy': 4.1.0 + estree-walker: 3.0.3 + magic-string: 0.30.21 + vite: 8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0) + dev: true - '@vitest/pretty-format@3.2.4': - resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - - '@vitest/pretty-format@4.1.0': + /@vitest/pretty-format@4.1.0: resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} + dependencies: + tinyrainbow: 3.1.0 + dev: true - '@vitest/runner@3.2.4': - resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} - - '@vitest/runner@4.1.0': + /@vitest/runner@4.1.0: resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + dependencies: + '@vitest/utils': 4.1.0 + pathe: 2.0.3 + dev: true - '@vitest/snapshot@3.2.4': - resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - - '@vitest/snapshot@4.1.0': + /@vitest/snapshot@4.1.0: resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + dependencies: + '@vitest/pretty-format': 4.1.0 + '@vitest/utils': 4.1.0 + magic-string: 0.30.21 + pathe: 2.0.3 + dev: true - '@vitest/spy@3.2.4': - resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - - '@vitest/spy@4.1.0': + /@vitest/spy@4.1.0: resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} + dev: true - '@vitest/utils@3.2.4': - resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - - '@vitest/utils@4.1.0': + /@vitest/utils@4.1.0: resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + dependencies: + '@vitest/pretty-format': 4.1.0 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + dev: true - '@vladfrangu/async_event_emitter@2.4.7': + /@vladfrangu/async_event_emitter@2.4.7: resolution: {integrity: sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: false - '@wasm-audio-decoders/common@9.0.7': + /@wasm-audio-decoders/common@9.0.7: resolution: {integrity: sha512-WRaUuWSKV7pkttBygml/a6dIEpatq2nnZGFIoPTc5yPLkxL6Wk4YaslPM98OPQvWacvNZ+Py9xROGDtrFBDzag==} + dependencies: + '@eshaz/web-worker': 1.2.2 + simple-yenc: 1.0.4 + dev: false - '@wasm-audio-decoders/opus-ml@0.0.2': + /@wasm-audio-decoders/opus-ml@0.0.2: resolution: {integrity: sha512-58rWEqDGg+CKCyEeKm2KoxxSwTWtHh/NLTW9ObR4K8CGF6VwuuGudEI1CtniS/oSRmL1nJq/eh8MKARiluw4DQ==} + dependencies: + '@wasm-audio-decoders/common': 9.0.7 + dev: false - '@zed-industries/claude-agent-acp@0.22.2': + /@zed-industries/claude-agent-acp@0.22.2: resolution: {integrity: sha512-GLiKxy5MBNS9UoiE1XaM9EHVxlEcvk0sXSMCnyDp9JNAQliynt0axZrhptTl5AWe6PXGjVh5hMFdPp+yulw2uQ==} hasBin: true + dependencies: + '@agentclientprotocol/sdk': 0.16.1(zod@3.25.76) + '@anthropic-ai/claude-agent-sdk': 0.2.76(zod@3.25.76) + zod: 3.25.76 + dev: false - abort-controller@3.0.0: + /abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + dependencies: + event-target-shim: 5.0.1 + dev: false - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - - acorn@8.16.0: + /acorn@8.16.0: resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} hasBin: true + dev: true - ansi-regex@6.2.2: + /ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} + dev: false - any-promise@1.3.0: + /any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: true - assertion-error@2.0.1: + /assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + dev: true - asynckit@0.4.0: + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false - atomic-sleep@1.0.0: + /atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + dev: false - axios@1.13.6: + /axios@1.13.6: resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false - base64-js@1.5.1: + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false - body-parser@2.2.2: - resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} - engines: {node: '>=18'} - - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - - buffer@6.0.3: + /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false - bundle-require@5.1.0: + /bundle-require@5.1.0(esbuild@0.27.4): resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.18' + dependencies: + esbuild: 0.27.4 + load-tsconfig: 0.2.5 + dev: true - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - cac@6.7.14: + /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + dev: true - call-bind-apply-helpers@1.0.2: + /call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + dev: false - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - ccount@2.0.1: + /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + dev: false - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} - engines: {node: '>=18'} - - chai@6.2.2: + /chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} + dev: true - chalk@5.6.2: + /chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: false - character-entities-html4@2.1.0: + /character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + dev: false - character-entities-legacy@3.0.0: + /character-entities-legacy@3.0.0: resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + dev: false - chardet@2.1.1: + /chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + dev: false - check-error@2.1.3: - resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} - engines: {node: '>= 16'} - - chokidar@4.0.3: + /chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + dependencies: + readdirp: 4.1.2 + dev: true - cli-cursor@5.0.0: + /cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} + dependencies: + restore-cursor: 5.1.0 + dev: false - cli-spinners@3.4.0: + /cli-spinners@3.4.0: resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} engines: {node: '>=18.20'} + dev: false - cli-width@4.1.0: + /cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + dev: false - codec-parser@2.5.0: + /codec-parser@2.5.0: resolution: {integrity: sha512-Ru9t80fV8B0ZiixQl8xhMTLru+dzuis/KQld32/x5T/+3LwZb0/YvQdSKytX9JqCnRdiupvAvyYJINKrXieziQ==} + dev: false - colorette@2.0.20: + /colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + dev: false - combined-stream@1.0.8: + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false - comma-separated-tokens@2.0.3: + /comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + dev: false - commander@4.1.1: + /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + dev: true - confbox@0.1.8: + /confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + dev: true - consola@3.4.2: + /consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + dev: true - content-disposition@1.0.1: - resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} - engines: {node: '>=18'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - convert-source-map@2.0.0: + /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - date-fns@4.1.0: + /date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dev: false - dateformat@4.6.3: + /dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + dev: false - debug@4.4.3: + /debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: @@ -1182,136 +1659,194 @@ packages: peerDependenciesMeta: supports-color: optional: true + dependencies: + ms: 2.1.3 - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - - delayed-stream@1.0.0: + /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + dev: false - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - - dequal@2.0.3: + /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + dev: false - detect-libc@2.1.2: + /detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dev: true - devlop@1.1.0: + /devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dependencies: + dequal: 2.0.3 + dev: false - diff@8.0.3: + /diff@8.0.3: resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} engines: {node: '>=0.3.1'} - discord-api-types@0.38.42: + /discord-api-types@0.38.42: resolution: {integrity: sha512-qs1kya7S84r5RR8m9kgttywGrmmoHaRifU1askAoi+wkoSefLpZP6aGXusjNw5b0jD3zOg3LTwUa3Tf2iHIceQ==} + dev: false - discord.js@14.25.1: + /discord.js@14.25.1: resolution: {integrity: sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g==} engines: {node: '>=18'} + dependencies: + '@discordjs/builders': 1.14.0 + '@discordjs/collection': 1.5.3 + '@discordjs/formatters': 0.6.2 + '@discordjs/rest': 2.6.1 + '@discordjs/util': 1.2.0 + '@discordjs/ws': 1.2.3 + '@sapphire/snowflake': 3.5.3 + discord-api-types: 0.38.42 + fast-deep-equal: 3.1.3 + lodash.snakecase: 4.1.1 + magic-bytes.js: 1.13.0 + tslib: 2.8.1 + undici: 6.21.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false - dunder-proto@1.0.1: + /dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + dev: false - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - end-of-stream@1.4.5: + /end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + dependencies: + once: 1.4.0 + dev: false - es-define-property@1.0.1: + /es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} + dev: false - es-errors@1.3.0: + /es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + dev: false - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - - es-module-lexer@2.0.0: + /es-module-lexer@2.0.0: resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + dev: true - es-object-atoms@1.1.1: + /es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + dev: false - es-set-tostringtag@2.1.0: + /es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + dev: false - esbuild@0.27.4: + /esbuild@0.27.4: resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} engines: {node: '>=18'} hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 + dev: true - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - estree-walker@3.0.3: + /estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.8 + dev: true - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - event-target-shim@5.0.1: + /event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + dev: false - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - eventemitter3@5.0.4: + /eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + dev: false - expect-type@1.3.0: + /expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + dev: true - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - - fast-copy@4.0.2: + /fast-copy@4.0.2: resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} + dev: false - fast-deep-equal@3.1.3: + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: false - fast-safe-stringify@2.1.1: + /fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + dev: false - fast-string-truncated-width@3.0.3: + /fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + dev: false - fast-string-width@3.0.2: + /fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + dependencies: + fast-string-truncated-width: 3.0.3 + dev: false - fast-wrap-ansi@0.2.0: + /fast-wrap-ansi@0.2.0: resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + dependencies: + fast-string-width: 3.0.2 + dev: false - fastest-levenshtein@1.0.16: + /fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} + dev: false - fdir@6.5.0: + /fdir@6.5.0(picomatch@4.0.3): resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1319,15 +1854,19 @@ packages: peerDependenciesMeta: picomatch: optional: true + dependencies: + picomatch: 4.0.3 + dev: true - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} - - fix-dts-default-cjs-exports@1.0.1: + /fix-dts-default-cjs-exports@1.0.1: resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + dependencies: + magic-string: 0.30.21 + mlly: 1.8.2 + rollup: 4.60.0 + dev: true - follow-redirects@1.15.11: + /follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} peerDependencies: @@ -1335,347 +1874,460 @@ packages: peerDependenciesMeta: debug: optional: true + dev: false - form-data@4.0.5: + /form-data@4.0.5: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + dev: false - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - - fsevents@2.3.3: + /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + requiresBuild: true + dev: true + optional: true - function-bind@1.1.2: + /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false - get-east-asian-width@1.5.0: + /get-east-asian-width@1.5.0: resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} engines: {node: '>=18'} + dev: false - get-intrinsic@1.3.0: + /get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-tsconfig@4.13.6: - resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} - - gopd@1.2.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + dev: false + + /get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + dev: false + + /get-tsconfig@4.13.7: + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + dev: false - grammy@1.41.1: + /grammy@1.41.1: resolution: {integrity: sha512-wcHAQ1e7svL3fJMpDchcQVcWUmywhuepOOjHUHmMmWAwUJEIyK5ea5sbSjZd+Gy1aMpZeP8VYJa+4tP+j1YptQ==} engines: {node: ^12.20.0 || >=14.13.1} + dependencies: + '@grammyjs/types': 3.25.0 + abort-controller: 3.0.0 + debug: 4.4.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false - has-symbols@1.1.0: + /has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + dev: false - has-tostringtag@1.0.2: + /has-tostringtag@1.0.2: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.1.0 + dev: false - hasown@2.0.2: + /hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false - hast-util-to-html@9.0.5: + /hast-util-to-html@9.0.5: resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + dev: false - hast-util-whitespace@3.0.0: + /hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + dependencies: + '@types/hast': 3.0.4 + dev: false - help-me@5.0.0: + /help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + dev: false - hono@4.12.8: + /hono@4.12.8: resolution: {integrity: sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==} engines: {node: '>=16.9.0'} + dev: false - html-void-elements@3.0.0: + /html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + dev: false - http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} - engines: {node: '>= 0.8'} - - husky@9.1.7: + /husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} hasBin: true + dev: true - iconv-lite@0.7.2: + /iconv-lite@0.7.2: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false - ieee754@1.2.1: + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false - inherits@2.0.4: + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - is-electron@2.2.2: - resolution: {integrity: sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==} - - is-interactive@2.0.0: + /is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} + dev: false - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-unicode-supported@2.1.0: + /is-unicode-supported@2.1.0: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + dev: false - isomorphic-ws@5.0.0: + /isomorphic-ws@5.0.0(ws@8.20.0): resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} peerDependencies: ws: '*' + dependencies: + ws: 8.20.0 + dev: false - joycon@3.1.1: + /joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} - js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - - jsonwebtoken@9.0.3: - resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} - engines: {node: '>=12', npm: '>=6'} - - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.1: - resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - - lightningcss-android-arm64@1.32.0: + /lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] + requiresBuild: true + dev: true + optional: true - lightningcss-darwin-arm64@1.32.0: + /lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] + requiresBuild: true + dev: true + optional: true - lightningcss-darwin-x64@1.32.0: + /lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + requiresBuild: true + dev: true + optional: true - lightningcss-freebsd-x64@1.32.0: + /lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - lightningcss-linux-arm-gnueabihf@1.32.0: + /lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + requiresBuild: true + dev: true + optional: true - lightningcss-linux-arm64-gnu@1.32.0: + /lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + requiresBuild: true + dev: true + optional: true - lightningcss-linux-arm64-musl@1.32.0: + /lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + requiresBuild: true + dev: true + optional: true - lightningcss-linux-x64-gnu@1.32.0: + /lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + requiresBuild: true + dev: true + optional: true - lightningcss-linux-x64-musl@1.32.0: + /lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + requiresBuild: true + dev: true + optional: true - lightningcss-win32-arm64-msvc@1.32.0: + /lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + requiresBuild: true + dev: true + optional: true - lightningcss-win32-x64-msvc@1.32.0: + /lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] + requiresBuild: true + dev: true + optional: true - lightningcss@1.32.0: + /lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + dev: true - lilconfig@3.1.3: + /lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} + dev: true - lines-and-columns@1.2.4: + /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true - load-tsconfig@0.2.5: + /load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true - lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - - lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - - lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - - lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - - lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - - lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - - lodash.snakecase@4.1.1: + /lodash.snakecase@4.1.1: resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + dev: false - lodash@4.17.23: + /lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + dev: false - log-symbols@7.0.1: + /log-symbols@7.0.1: resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + dev: false - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - - magic-bytes.js@1.13.0: + /magic-bytes.js@1.13.0: resolution: {integrity: sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg==} + dev: false - magic-string@0.30.21: + /magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + dev: true - math-intrinsics@1.1.0: + /math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + dev: false - mdast-util-to-hast@13.2.1: + /mdast-util-to-hast@13.2.1: resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + dev: false - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - - micromark-util-character@2.1.1: + /micromark-util-character@2.1.1: resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + dev: false - micromark-util-encode@2.0.1: + /micromark-util-encode@2.0.1: resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + dev: false - micromark-util-sanitize-uri@2.0.1: + /micromark-util-sanitize-uri@2.0.1: resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + dev: false - micromark-util-symbol@2.0.1: + /micromark-util-symbol@2.0.1: resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + dev: false - micromark-util-types@2.0.2: + /micromark-util-types@2.0.2: resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + dev: false - mime-db@1.52.0: + /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + dev: false - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: + /mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false - mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} - engines: {node: '>=18'} - - mimic-function@5.0.1: + /mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} + dev: false - minimist@1.2.8: + /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: false - mlly@1.8.1: - resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==} + /mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.3 + dev: true - ms@2.1.3: + /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msedge-tts@2.0.4: + /msedge-tts@2.0.4: resolution: {integrity: sha512-+wcjj5F+J08xxQ6DmGcesHWxiEyr9CqX2YlWbtyrRArhOZ96MYxqzLqv0qzLx1nrSzZOlpvlvL1vYwg0VQ1s3g==} engines: {node: '>=16.0.0'} + requiresBuild: true + dependencies: + axios: 1.13.6 + buffer: 6.0.3 + isomorphic-ws: 5.0.0(ws@8.20.0) + stream-browserify: 3.0.0 + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + dev: false - mute-stream@3.0.0: + /mute-stream@3.0.0: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} + dev: false - mz@2.7.0: + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true - nanoid@3.3.11: + /nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + dev: true - nanoid@5.1.7: + /nanoid@5.1.7: resolution: {integrity: sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==} engines: {node: ^18 || >=20} hasBin: true + dev: false - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - - node-fetch@2.7.0: + /node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} peerDependencies: @@ -1683,123 +2335,176 @@ packages: peerDependenciesMeta: encoding: optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false - node-wav@0.0.2: + /node-wav@0.0.2: resolution: {integrity: sha512-M6Rm/bbG6De/gKGxOpeOobx/dnGuP0dz40adqx38boqHhlWssBJZgLCPBNtb9NkrmnKYiV04xELq+R6PFOnoLA==} engines: {node: '>=4.4.0'} + dev: false - object-assign@4.1.1: + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + dev: true - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - obug@2.1.1: + /obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + dev: true - ogg-opus-decoder@1.7.3: + /ogg-opus-decoder@1.7.3: resolution: {integrity: sha512-w47tiZpkLgdkpa+34VzYD8mHUj8I9kfWVZa82mBbNwDvB1byfLXSSzW/HxA4fI3e9kVlICSpXGFwMLV1LPdjwg==} + dependencies: + '@wasm-audio-decoders/common': 9.0.7 + '@wasm-audio-decoders/opus-ml': 0.0.2 + codec-parser: 2.5.0 + opus-decoder: 0.7.11 + dev: false - on-exit-leak-free@2.1.2: + /on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} + dev: false - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: false - onetime@7.0.0: + /onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} + dependencies: + mimic-function: 5.0.1 + dev: false - oniguruma-parser@0.12.1: + /oniguruma-parser@0.12.1: resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + dev: false - oniguruma-to-es@4.3.5: + /oniguruma-to-es@4.3.5: resolution: {integrity: sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==} + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.1.0 + regex-recursion: 6.0.2 + dev: false - opus-decoder@0.7.11: + /opus-decoder@0.7.11: resolution: {integrity: sha512-+e+Jz3vGQLxRTBHs8YJQPRPc1Tr+/aC6coV/DlZylriA29BdHQAYXhvNRKtjftof17OFng0+P4wsFIqQu3a48A==} + dependencies: + '@wasm-audio-decoders/common': 9.0.7 + dev: false - ora@9.3.0: + /ora@9.3.0: resolution: {integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==} engines: {node: '>=20'} + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 3.4.0 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 7.0.1 + stdin-discarder: 0.3.1 + string-width: 8.2.0 + dev: false - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - - p-queue@6.6.2: - resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} - engines: {node: '>=8'} - - p-queue@9.1.0: + /p-queue@9.1.0: resolution: {integrity: sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==} engines: {node: '>=20'} + dependencies: + eventemitter3: 5.0.4 + p-timeout: 7.0.1 + dev: false - p-retry@4.6.2: - resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} - engines: {node: '>=8'} - - p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} - - p-timeout@7.0.1: + /p-timeout@7.0.1: resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} engines: {node: '>=20'} + dev: false - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} - - pathe@2.0.3: + /pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + dev: true - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} - - picocolors@1.1.1: + /picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + dev: true - picomatch@4.0.3: + /picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + dev: true - pino-abstract-transport@3.0.0: + /pino-abstract-transport@3.0.0: resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + dependencies: + split2: 4.2.0 + dev: false - pino-pretty@13.1.3: + /pino-pretty@13.1.3: resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} hasBin: true + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 4.0.2 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pump: 3.0.4 + secure-json-parse: 4.1.0 + sonic-boom: 4.2.1 + strip-json-comments: 5.0.3 + dev: false - pino-roll@4.0.0: + /pino-roll@4.0.0: resolution: {integrity: sha512-axI1aQaIxXdw1F4OFFli1EDxIrdYNGLowkw/ZoZogX8oCSLHUghzwVVXUS8U+xD/Savwa5IXpiXmsSGKFX/7Sg==} + dependencies: + date-fns: 4.1.0 + sonic-boom: 4.2.1 + dev: false - pino-std-serializers@7.1.0: + /pino-std-serializers@7.1.0: resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + dev: false - pino@10.3.1: + /pino@10.3.1: resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} hasBin: true + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.1 + thread-stream: 4.0.0 + dev: false - pirates@4.0.7: + /pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + dev: true - pkg-types@1.3.1: + /pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + dev: true - postcss-load-config@6.0.1: + /postcss-load-config@6.0.1(tsx@4.21.0): resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} engines: {node: '>= 18'} peerDependencies: @@ -1816,287 +2521,367 @@ packages: optional: true yaml: optional: true + dependencies: + lilconfig: 3.1.3 + tsx: 4.21.0 + dev: true - postcss@8.5.8: + /postcss@8.5.8: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + dev: true - process-warning@5.0.0: + /process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + dev: false - property-information@7.1.0: + /property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + dev: false - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - proxy-from-env@1.1.0: + /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: false - pump@3.0.4: + /pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + dev: false - qs@6.15.0: - resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} - engines: {node: '>=0.6'} - - quick-format-unescaped@4.0.4: + /quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + dev: false - range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} - - readable-stream@3.6.2: + /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: false - readdirp@4.1.2: + /readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + dev: true - real-require@0.2.0: + /real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + dev: false - regex-recursion@6.0.2: + /regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + dependencies: + regex-utilities: 2.3.0 + dev: false - regex-utilities@2.3.0: + /regex-utilities@2.3.0: resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + dev: false - regex@6.1.0: + /regex@6.1.0: resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + dependencies: + regex-utilities: 2.3.0 + dev: false - resolve-from@5.0.0: + /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + dev: true - resolve-pkg-maps@1.0.0: + /resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true - restore-cursor@5.1.0: + /restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + dev: false - retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - - rolldown@1.0.0-rc.10: + /rolldown@1.0.0-rc.10: resolution: {integrity: sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + dependencies: + '@oxc-project/types': 0.120.0 + '@rolldown/pluginutils': 1.0.0-rc.10 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.10 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.10 + '@rolldown/binding-darwin-x64': 1.0.0-rc.10 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.10 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.10 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.10 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.10 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.10 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.10 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.10 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.10 + dev: true - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} + /rollup@4.60.0: + resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.0 + '@rollup/rollup-android-arm64': 4.60.0 + '@rollup/rollup-darwin-arm64': 4.60.0 + '@rollup/rollup-darwin-x64': 4.60.0 + '@rollup/rollup-freebsd-arm64': 4.60.0 + '@rollup/rollup-freebsd-x64': 4.60.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.0 + '@rollup/rollup-linux-arm-musleabihf': 4.60.0 + '@rollup/rollup-linux-arm64-gnu': 4.60.0 + '@rollup/rollup-linux-arm64-musl': 4.60.0 + '@rollup/rollup-linux-loong64-gnu': 4.60.0 + '@rollup/rollup-linux-loong64-musl': 4.60.0 + '@rollup/rollup-linux-ppc64-gnu': 4.60.0 + '@rollup/rollup-linux-ppc64-musl': 4.60.0 + '@rollup/rollup-linux-riscv64-gnu': 4.60.0 + '@rollup/rollup-linux-riscv64-musl': 4.60.0 + '@rollup/rollup-linux-s390x-gnu': 4.60.0 + '@rollup/rollup-linux-x64-gnu': 4.60.0 + '@rollup/rollup-linux-x64-musl': 4.60.0 + '@rollup/rollup-openbsd-x64': 4.60.0 + '@rollup/rollup-openharmony-arm64': 4.60.0 + '@rollup/rollup-win32-arm64-msvc': 4.60.0 + '@rollup/rollup-win32-ia32-msvc': 4.60.0 + '@rollup/rollup-win32-x64-gnu': 4.60.0 + '@rollup/rollup-win32-x64-msvc': 4.60.0 + fsevents: 2.3.3 + dev: true - safe-buffer@5.2.1: + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false - safe-stable-stringify@2.5.0: + /safe-stable-stringify@2.5.0: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} + dev: false - safer-buffer@2.1.2: + /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false - secure-json-parse@4.1.0: + /secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + dev: false - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} - engines: {node: '>= 18'} - - serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} - engines: {node: '>= 18'} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - shiki@4.0.2: + /shiki@4.0.2: resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==} engines: {node: '>=20'} + dependencies: + '@shikijs/core': 4.0.2 + '@shikijs/engine-javascript': 4.0.2 + '@shikijs/engine-oniguruma': 4.0.2 + '@shikijs/langs': 4.0.2 + '@shikijs/themes': 4.0.2 + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + dev: false - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - siginfo@2.0.0: + /siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true - signal-exit@4.1.0: + /signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + dev: false - simple-yenc@1.0.4: + /simple-yenc@1.0.4: resolution: {integrity: sha512-5gvxpSd79e9a3V4QDYUqnqxeD4HGlhCakVpb6gMnDD7lexJggSBJRBO5h52y/iJrdXRilX9UCuDaIJhSWm5OWw==} + dev: false - sisteransi@1.0.5: + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: false - sonic-boom@4.2.1: + /sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + dependencies: + atomic-sleep: 1.0.0 + dev: false - source-map-js@1.2.1: + /source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + dev: true - source-map@0.7.6: + /source-map@0.7.6: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + dev: true - space-separated-tokens@2.0.2: + /space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + dev: false - split2@4.2.0: + /split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + dev: false - stackback@0.0.2: + /stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true - statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} - - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - - std-env@4.0.0: + /std-env@4.0.0: resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + dev: true - stdin-discarder@0.3.1: + /stdin-discarder@0.3.1: resolution: {integrity: sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==} engines: {node: '>=18'} + dev: false - stream-browserify@3.0.0: + /stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false - string-width@8.2.0: + /string-width@8.2.0: resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} engines: {node: '>=20'} + dependencies: + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + dev: false - string_decoder@1.3.0: + /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: false - stringify-entities@4.0.4: + /stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + dev: false - strip-ansi@7.2.0: + /strip-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + dependencies: + ansi-regex: 6.2.2 + dev: false - strip-json-comments@5.0.3: + /strip-json-comments@5.0.3: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} + dev: false - strip-literal@3.1.0: - resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - - sucrase@3.35.1: + /sucrase@3.35.1: resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.15 + ts-interface-checker: 0.1.13 + dev: true - thenify-all@1.6.0: + /thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: true - thenify@3.3.1: + /thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: true - thread-stream@4.0.0: + /thread-stream@4.0.0: resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} engines: {node: '>=20'} + dependencies: + real-require: 0.2.0 + dev: false - tinybench@2.9.0: + /tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + dev: true - tinyexec@0.3.2: + /tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + dev: true - tinyexec@1.0.4: + /tinyexec@1.0.4: resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} engines: {node: '>=18'} + dev: true - tinyglobby@0.2.15: + /tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + dev: true - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} - engines: {node: '>=14.0.0'} - - tinyrainbow@3.1.0: + /tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} + dev: true - tinyspy@4.0.4: - resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} - engines: {node: '>=14.0.0'} - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - tr46@0.0.3: + /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false - tree-kill@1.2.2: + /tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + dev: true - trim-lines@3.0.1: + /trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + dev: false - ts-interface-checker@0.1.13: + /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: true - ts-mixer@6.0.4: + /ts-mixer@6.0.4: resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==} + dev: false - tslib@2.8.1: + /tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + requiresBuild: true - tsscmp@1.0.6: - resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} - engines: {node: '>=0.6.x'} - - tsup@8.5.1: + /tsup@8.5.1(tsx@4.21.0)(typescript@5.9.3): resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} engines: {node: '>=18'} hasBin: true @@ -2114,113 +2899,118 @@ packages: optional: true typescript: optional: true + dependencies: + bundle-require: 5.1.0(esbuild@0.27.4) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.3 + esbuild: 0.27.4 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(tsx@4.21.0) + resolve-from: 5.0.0 + rollup: 4.60.0 + source-map: 0.7.6 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tree-kill: 1.2.2 + typescript: 5.9.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + dev: true - tsx@4.21.0: + /tsx@4.21.0: resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} engines: {node: '>=18.0.0'} hasBin: true + dependencies: + esbuild: 0.27.4 + get-tsconfig: 4.13.7 + optionalDependencies: + fsevents: 2.3.3 + dev: true - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - - typescript@5.9.3: + /typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true + dev: true - ufo@1.6.3: + /ufo@1.6.3: resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + dev: true - undici-types@7.18.2: + /undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici@6.21.3: + /undici@6.21.3: resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} engines: {node: '>=18.17'} + dev: false - undici@6.24.1: + /undici@6.24.1: resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==} engines: {node: '>=18.17'} + dev: false - unist-util-is@6.0.1: + /unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + dependencies: + '@types/unist': 3.0.3 + dev: false - unist-util-position@5.0.0: + /unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + dependencies: + '@types/unist': 3.0.3 + dev: false - unist-util-stringify-position@4.0.0: + /unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + dependencies: + '@types/unist': 3.0.3 + dev: false - unist-util-visit-parents@6.0.2: + /unist-util-visit-parents@6.0.2: resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + dev: false - unist-util-visit@5.1.0: + /unist-util-visit@5.1.0: resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + dev: false - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - util-deprecate@1.0.2: + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: false - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - vfile-message@4.0.3: + /vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + dev: false - vfile@6.0.3: + /vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + dev: false - vite-node@3.2.4: - resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vite@8.0.1: + /vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0): resolution: {integrity: sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2262,36 +3052,20 @@ packages: optional: true yaml: optional: true + dependencies: + '@types/node': 25.5.0 + esbuild: 0.27.4 + lightningcss: 1.32.0 + picomatch: 4.0.3 + postcss: 8.5.8 + rolldown: 1.0.0-rc.10 + tinyglobby: 0.2.15 + tsx: 4.21.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true - vitest@3.2.4: - resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.2.4 - '@vitest/ui': 3.2.4 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/debug': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - vitest@4.1.0: + /vitest@4.1.0(@types/node@25.5.0)(vite@8.0.1): resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true @@ -2325,22 +3099,57 @@ packages: optional: true jsdom: optional: true + dependencies: + '@types/node': 25.5.0 + '@vitest/expect': 4.1.0 + '@vitest/mocker': 4.1.0(vite@8.0.1) + '@vitest/pretty-format': 4.1.0 + '@vitest/runner': 4.1.0 + '@vitest/snapshot': 4.1.0 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 + es-module-lexer: 2.0.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 4.0.0 + tinybench: 2.9.0 + tinyexec: 1.0.4 + tinyglobby: 0.2.15 + tinyrainbow: 3.1.0 + vite: 8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0) + why-is-node-running: 2.3.0 + transitivePeerDependencies: + - msw + dev: true - webidl-conversions@3.0.1: + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false - whatwg-url@5.0.0: + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false - why-is-node-running@2.3.0: + /why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true - wrappy@1.0.2: + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: false - ws@8.20.0: + /ws@8.20.0: resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} engines: {node: '>=10.0.0'} peerDependencies: @@ -2351,2174 +3160,17 @@ packages: optional: true utf-8-validate: optional: true + dev: false - yoctocolors@2.1.2: + /yoctocolors@2.1.2: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + dev: false - zod@3.25.76: + /zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + dev: false - zwitch@2.0.4: + /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@agentclientprotocol/sdk@0.16.1(zod@3.25.76)': - dependencies: - zod: 3.25.76 - - '@anthropic-ai/claude-agent-sdk@0.2.76(zod@3.25.76)': - dependencies: - zod: 3.25.76 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - - '@clack/core@1.1.0': - dependencies: - sisteransi: 1.0.5 - - '@clack/prompts@1.1.0': - dependencies: - '@clack/core': 1.1.0 - sisteransi: 1.0.5 - - '@discordjs/builders@1.14.0': - dependencies: - '@discordjs/formatters': 0.6.2 - '@discordjs/util': 1.2.0 - '@sapphire/shapeshift': 4.0.0 - discord-api-types: 0.38.42 - fast-deep-equal: 3.1.3 - ts-mixer: 6.0.4 - tslib: 2.8.1 - - '@discordjs/collection@1.5.3': {} - - '@discordjs/collection@2.1.1': {} - - '@discordjs/formatters@0.6.2': - dependencies: - discord-api-types: 0.38.42 - - '@discordjs/rest@2.6.1': - dependencies: - '@discordjs/collection': 2.1.1 - '@discordjs/util': 1.2.0 - '@sapphire/async-queue': 1.5.5 - '@sapphire/snowflake': 3.5.5 - '@vladfrangu/async_event_emitter': 2.4.7 - discord-api-types: 0.38.42 - magic-bytes.js: 1.13.0 - tslib: 2.8.1 - undici: 6.24.1 - - '@discordjs/util@1.2.0': - dependencies: - discord-api-types: 0.38.42 - - '@discordjs/ws@1.2.3': - dependencies: - '@discordjs/collection': 2.1.1 - '@discordjs/rest': 2.6.1 - '@discordjs/util': 1.2.0 - '@sapphire/async-queue': 1.5.5 - '@types/ws': 8.18.1 - '@vladfrangu/async_event_emitter': 2.4.7 - discord-api-types: 0.38.42 - tslib: 2.8.1 - ws: 8.20.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@emnapi/core@1.9.0': - dependencies: - '@emnapi/wasi-threads': 1.2.0 - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.9.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/wasi-threads@1.2.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.27.4': - optional: true - - '@esbuild/android-arm64@0.27.4': - optional: true - - '@esbuild/android-arm@0.27.4': - optional: true - - '@esbuild/android-x64@0.27.4': - optional: true - - '@esbuild/darwin-arm64@0.27.4': - optional: true - - '@esbuild/darwin-x64@0.27.4': - optional: true - - '@esbuild/freebsd-arm64@0.27.4': - optional: true - - '@esbuild/freebsd-x64@0.27.4': - optional: true - - '@esbuild/linux-arm64@0.27.4': - optional: true - - '@esbuild/linux-arm@0.27.4': - optional: true - - '@esbuild/linux-ia32@0.27.4': - optional: true - - '@esbuild/linux-loong64@0.27.4': - optional: true - - '@esbuild/linux-mips64el@0.27.4': - optional: true - - '@esbuild/linux-ppc64@0.27.4': - optional: true - - '@esbuild/linux-riscv64@0.27.4': - optional: true - - '@esbuild/linux-s390x@0.27.4': - optional: true - - '@esbuild/linux-x64@0.27.4': - optional: true - - '@esbuild/netbsd-arm64@0.27.4': - optional: true - - '@esbuild/netbsd-x64@0.27.4': - optional: true - - '@esbuild/openbsd-arm64@0.27.4': - optional: true - - '@esbuild/openbsd-x64@0.27.4': - optional: true - - '@esbuild/openharmony-arm64@0.27.4': - optional: true - - '@esbuild/sunos-x64@0.27.4': - optional: true - - '@esbuild/win32-arm64@0.27.4': - optional: true - - '@esbuild/win32-ia32@0.27.4': - optional: true - - '@esbuild/win32-x64@0.27.4': - optional: true - - '@eshaz/web-worker@1.2.2': {} - - '@grammyjs/types@3.25.0': {} - - '@hono/node-server@1.19.11(hono@4.12.8)': - dependencies: - hono: 4.12.8 - - '@img/sharp-darwin-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 - optional: true - - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.2.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - optional: true - - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 - optional: true - - '@img/sharp-linux-arm@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true - - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true - - '@img/sharp-win32-arm64@0.34.5': - optional: true - - '@img/sharp-win32-x64@0.34.5': - optional: true - - '@inquirer/ansi@2.0.4': {} - - '@inquirer/checkbox@5.1.2(@types/node@25.5.0)': - dependencies: - '@inquirer/ansi': 2.0.4 - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/figures': 2.0.4 - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/confirm@6.0.10(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/core@11.1.7(@types/node@25.5.0)': - dependencies: - '@inquirer/ansi': 2.0.4 - '@inquirer/figures': 2.0.4 - '@inquirer/type': 4.0.4(@types/node@25.5.0) - cli-width: 4.1.0 - fast-wrap-ansi: 0.2.0 - mute-stream: 3.0.0 - signal-exit: 4.1.0 - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/editor@5.0.10(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/external-editor': 2.0.4(@types/node@25.5.0) - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/expand@5.0.10(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/external-editor@2.0.4(@types/node@25.5.0)': - dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.2 - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/figures@2.0.4': {} - - '@inquirer/input@5.0.10(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/number@4.0.10(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/password@5.0.10(@types/node@25.5.0)': - dependencies: - '@inquirer/ansi': 2.0.4 - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/prompts@8.3.2(@types/node@25.5.0)': - dependencies: - '@inquirer/checkbox': 5.1.2(@types/node@25.5.0) - '@inquirer/confirm': 6.0.10(@types/node@25.5.0) - '@inquirer/editor': 5.0.10(@types/node@25.5.0) - '@inquirer/expand': 5.0.10(@types/node@25.5.0) - '@inquirer/input': 5.0.10(@types/node@25.5.0) - '@inquirer/number': 4.0.10(@types/node@25.5.0) - '@inquirer/password': 5.0.10(@types/node@25.5.0) - '@inquirer/rawlist': 5.2.6(@types/node@25.5.0) - '@inquirer/search': 4.1.6(@types/node@25.5.0) - '@inquirer/select': 5.1.2(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/rawlist@5.2.6(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/search@4.1.6(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/figures': 2.0.4 - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/select@5.1.2(@types/node@25.5.0)': - dependencies: - '@inquirer/ansi': 2.0.4 - '@inquirer/core': 11.1.7(@types/node@25.5.0) - '@inquirer/figures': 2.0.4 - '@inquirer/type': 4.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/type@4.0.4(@types/node@25.5.0)': - optionalDependencies: - '@types/node': 25.5.0 - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@napi-rs/wasm-runtime@1.1.1': - dependencies: - '@emnapi/core': 1.9.0 - '@emnapi/runtime': 1.9.0 - '@tybys/wasm-util': 0.10.1 - optional: true - - '@oxc-project/types@0.120.0': {} - - '@pinojs/redact@0.4.0': {} - - '@rolldown/binding-android-arm64@1.0.0-rc.10': - optional: true - - '@rolldown/binding-darwin-arm64@1.0.0-rc.10': {} - - '@rolldown/binding-darwin-x64@1.0.0-rc.10': - optional: true - - '@rolldown/binding-freebsd-x64@1.0.0-rc.10': - optional: true - - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10': - optional: true - - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10': - optional: true - - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.10': - optional: true - - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10': - optional: true - - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10': - optional: true - - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.10': - optional: true - - '@rolldown/binding-linux-x64-musl@1.0.0-rc.10': - optional: true - - '@rolldown/binding-openharmony-arm64@1.0.0-rc.10': - optional: true - - '@rolldown/binding-wasm32-wasi@1.0.0-rc.10': - dependencies: - '@napi-rs/wasm-runtime': 1.1.1 - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.10': - optional: true - - '@rolldown/pluginutils@1.0.0-rc.10': {} - - '@rollup/rollup-android-arm-eabi@4.59.0': - optional: true - - '@rollup/rollup-android-arm64@4.59.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.59.0': - optional: true - - '@rollup/rollup-darwin-x64@4.59.0': - optional: true - - '@rollup/rollup-freebsd-arm64@4.59.0': - optional: true - - '@rollup/rollup-freebsd-x64@4.59.0': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.59.0': - optional: true - - '@rollup/rollup-openbsd-x64@4.59.0': - optional: true - - '@rollup/rollup-openharmony-arm64@4.59.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.59.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.59.0': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.59.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.59.0': - optional: true - - '@sapphire/async-queue@1.5.5': {} - - '@sapphire/shapeshift@4.0.0': - dependencies: - fast-deep-equal: 3.1.3 - lodash: 4.17.23 - - '@sapphire/snowflake@3.5.3': {} - - '@sapphire/snowflake@3.5.5': {} - - '@shikijs/core@4.0.2': - dependencies: - '@shikijs/primitive': 4.0.2 - '@shikijs/types': 4.0.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - - '@shikijs/engine-javascript@4.0.2': - dependencies: - '@shikijs/types': 4.0.2 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.5 - - '@shikijs/engine-oniguruma@4.0.2': - dependencies: - '@shikijs/types': 4.0.2 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/langs@4.0.2': - dependencies: - '@shikijs/types': 4.0.2 - - '@shikijs/primitive@4.0.2': - dependencies: - '@shikijs/types': 4.0.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/themes@4.0.2': - dependencies: - '@shikijs/types': 4.0.2 - - '@shikijs/types@4.0.2': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/vscode-textmate@10.0.2': {} - - '@slack/bolt@4.6.0(@types/express@5.0.6)': - dependencies: - '@slack/logger': 4.0.1 - '@slack/oauth': 3.0.5 - '@slack/socket-mode': 2.0.6 - '@slack/types': 2.20.1 - '@slack/web-api': 7.15.0 - '@types/express': 5.0.6 - axios: 1.13.6 - express: 5.2.1 - path-to-regexp: 8.3.0 - raw-body: 3.0.2 - tsscmp: 1.0.6 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - - '@slack/logger@4.0.1': - dependencies: - '@types/node': 25.5.0 - - '@slack/oauth@3.0.5': - dependencies: - '@slack/logger': 4.0.1 - '@slack/web-api': 7.15.0 - '@types/jsonwebtoken': 9.0.10 - '@types/node': 25.5.0 - jsonwebtoken: 9.0.3 - transitivePeerDependencies: - - debug - - '@slack/socket-mode@2.0.6': - dependencies: - '@slack/logger': 4.0.1 - '@slack/web-api': 7.15.0 - '@types/node': 25.5.0 - '@types/ws': 8.18.1 - eventemitter3: 5.0.4 - ws: 8.20.0 - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@slack/types@2.20.1': {} - - '@slack/web-api@7.15.0': - dependencies: - '@slack/logger': 4.0.1 - '@slack/types': 2.20.1 - '@types/node': 25.5.0 - '@types/retry': 0.12.0 - axios: 1.13.6 - eventemitter3: 5.0.4 - form-data: 4.0.5 - is-electron: 2.2.2 - is-stream: 2.0.1 - p-queue: 6.6.2 - p-retry: 4.6.2 - retry: 0.13.1 - transitivePeerDependencies: - - debug - - '@standard-schema/spec@1.1.0': {} - - '@tybys/wasm-util@0.10.1': - dependencies: - tslib: 2.8.1 - optional: true - - '@types/body-parser@1.19.6': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 25.5.0 - - '@types/chai@5.2.3': - dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 - - '@types/connect@3.4.38': - dependencies: - '@types/node': 25.5.0 - - '@types/deep-eql@4.0.2': {} - - '@types/diff@8.0.0': - dependencies: - diff: 8.0.3 - - '@types/estree@1.0.8': {} - - '@types/express-serve-static-core@5.1.1': - dependencies: - '@types/node': 25.5.0 - '@types/qs': 6.15.0 - '@types/range-parser': 1.2.7 - '@types/send': 1.2.1 - - '@types/express@5.0.6': - dependencies: - '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 5.1.1 - '@types/serve-static': 2.2.0 - - '@types/hast@3.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/http-errors@2.0.5': {} - - '@types/jsonwebtoken@9.0.10': - dependencies: - '@types/ms': 2.1.0 - '@types/node': 25.5.0 - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/ms@2.1.0': {} - - '@types/node@25.5.0': - dependencies: - undici-types: 7.18.2 - - '@types/qs@6.15.0': {} - - '@types/range-parser@1.2.7': {} - - '@types/retry@0.12.0': {} - - '@types/send@1.2.1': - dependencies: - '@types/node': 25.5.0 - - '@types/serve-static@2.2.0': - dependencies: - '@types/http-errors': 2.0.5 - '@types/node': 25.5.0 - - '@types/unist@3.0.3': {} - - '@types/ws@8.18.1': - dependencies: - '@types/node': 25.5.0 - - '@ungap/structured-clone@1.3.0': {} - - '@vitest/expect@3.2.4': - dependencies: - '@types/chai': 5.2.3 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - tinyrainbow: 2.0.0 - - '@vitest/expect@4.1.0': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 - chai: 6.2.2 - tinyrainbow: 3.1.0 - - '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0))': - dependencies: - '@vitest/spy': 3.2.4 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0) - - '@vitest/mocker@4.1.0(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0))': - dependencies: - '@vitest/spy': 4.1.0 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0) - - '@vitest/pretty-format@3.2.4': - dependencies: - tinyrainbow: 2.0.0 - - '@vitest/pretty-format@4.1.0': - dependencies: - tinyrainbow: 3.1.0 - - '@vitest/runner@3.2.4': - dependencies: - '@vitest/utils': 3.2.4 - pathe: 2.0.3 - strip-literal: 3.1.0 - - '@vitest/runner@4.1.0': - dependencies: - '@vitest/utils': 4.1.0 - pathe: 2.0.3 - - '@vitest/snapshot@3.2.4': - dependencies: - '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.0': - dependencies: - '@vitest/pretty-format': 4.1.0 - '@vitest/utils': 4.1.0 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@3.2.4': - dependencies: - tinyspy: 4.0.4 - - '@vitest/spy@4.1.0': {} - - '@vitest/utils@3.2.4': - dependencies: - '@vitest/pretty-format': 3.2.4 - loupe: 3.2.1 - tinyrainbow: 2.0.0 - - '@vitest/utils@4.1.0': - dependencies: - '@vitest/pretty-format': 4.1.0 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 - - '@vladfrangu/async_event_emitter@2.4.7': {} - - '@wasm-audio-decoders/common@9.0.7': - dependencies: - '@eshaz/web-worker': 1.2.2 - simple-yenc: 1.0.4 - - '@wasm-audio-decoders/opus-ml@0.0.2': - dependencies: - '@wasm-audio-decoders/common': 9.0.7 - - '@zed-industries/claude-agent-acp@0.22.2': - dependencies: - '@agentclientprotocol/sdk': 0.16.1(zod@3.25.76) - '@anthropic-ai/claude-agent-sdk': 0.2.76(zod@3.25.76) - zod: 3.25.76 - - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - - accepts@2.0.0: - dependencies: - mime-types: 3.0.2 - negotiator: 1.0.0 - - acorn@8.16.0: {} - - ansi-regex@6.2.2: {} - - any-promise@1.3.0: {} - - assertion-error@2.0.1: {} - - asynckit@0.4.0: {} - - atomic-sleep@1.0.0: {} - - axios@1.13.6: - dependencies: - follow-redirects: 1.15.11 - form-data: 4.0.5 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - - base64-js@1.5.1: {} - - body-parser@2.2.2: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.3 - http-errors: 2.0.1 - iconv-lite: 0.7.2 - on-finished: 2.4.1 - qs: 6.15.0 - raw-body: 3.0.2 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - - buffer-equal-constant-time@1.0.1: {} - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - bundle-require@5.1.0(esbuild@0.27.4): - dependencies: - esbuild: 0.27.4 - load-tsconfig: 0.2.5 - - bytes@3.1.2: {} - - cac@6.7.14: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - ccount@2.0.1: {} - - chai@5.3.3: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.3 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 - - chai@6.2.2: {} - - chalk@5.6.2: {} - - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - chardet@2.1.1: {} - - check-error@2.1.3: {} - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - cli-cursor@5.0.0: - dependencies: - restore-cursor: 5.1.0 - - cli-spinners@3.4.0: {} - - cli-width@4.1.0: {} - - codec-parser@2.5.0: {} - - colorette@2.0.20: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - comma-separated-tokens@2.0.3: {} - - commander@4.1.1: {} - - confbox@0.1.8: {} - - consola@3.4.2: {} - - content-disposition@1.0.1: {} - - content-type@1.0.5: {} - - convert-source-map@2.0.0: {} - - cookie-signature@1.2.2: {} - - cookie@0.7.2: {} - - date-fns@4.1.0: {} - - dateformat@4.6.3: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - deep-eql@5.0.2: {} - - delayed-stream@1.0.0: {} - - depd@2.0.0: {} - - dequal@2.0.3: {} - - detect-libc@2.1.2: {} - - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - diff@8.0.3: {} - - discord-api-types@0.38.42: {} - - discord.js@14.25.1: - dependencies: - '@discordjs/builders': 1.14.0 - '@discordjs/collection': 1.5.3 - '@discordjs/formatters': 0.6.2 - '@discordjs/rest': 2.6.1 - '@discordjs/util': 1.2.0 - '@discordjs/ws': 1.2.3 - '@sapphire/snowflake': 3.5.3 - discord-api-types: 0.38.42 - fast-deep-equal: 3.1.3 - lodash.snakecase: 4.1.1 - magic-bytes.js: 1.13.0 - tslib: 2.8.1 - undici: 6.21.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - - ee-first@1.1.1: {} - - encodeurl@2.0.0: {} - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@1.7.0: {} - - es-module-lexer@2.0.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - esbuild@0.27.4: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.4 - '@esbuild/android-arm': 0.27.4 - '@esbuild/android-arm64': 0.27.4 - '@esbuild/android-x64': 0.27.4 - '@esbuild/darwin-arm64': 0.27.4 - '@esbuild/darwin-x64': 0.27.4 - '@esbuild/freebsd-arm64': 0.27.4 - '@esbuild/freebsd-x64': 0.27.4 - '@esbuild/linux-arm': 0.27.4 - '@esbuild/linux-arm64': 0.27.4 - '@esbuild/linux-ia32': 0.27.4 - '@esbuild/linux-loong64': 0.27.4 - '@esbuild/linux-mips64el': 0.27.4 - '@esbuild/linux-ppc64': 0.27.4 - '@esbuild/linux-riscv64': 0.27.4 - '@esbuild/linux-s390x': 0.27.4 - '@esbuild/linux-x64': 0.27.4 - '@esbuild/netbsd-arm64': 0.27.4 - '@esbuild/netbsd-x64': 0.27.4 - '@esbuild/openbsd-arm64': 0.27.4 - '@esbuild/openbsd-x64': 0.27.4 - '@esbuild/openharmony-arm64': 0.27.4 - '@esbuild/sunos-x64': 0.27.4 - '@esbuild/win32-arm64': 0.27.4 - '@esbuild/win32-ia32': 0.27.4 - '@esbuild/win32-x64': 0.27.4 - - escape-html@1.0.3: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.8 - - etag@1.8.1: {} - - event-target-shim@5.0.1: {} - - eventemitter3@4.0.7: {} - - eventemitter3@5.0.4: {} - - expect-type@1.3.0: {} - - express@5.2.1: - dependencies: - accepts: 2.0.0 - body-parser: 2.2.2 - content-disposition: 1.0.1 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.1 - fresh: 2.0.0 - http-errors: 2.0.1 - merge-descriptors: 2.0.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.15.0 - range-parser: 1.2.1 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 - statuses: 2.0.2 - type-is: 2.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - fast-copy@4.0.2: {} - - fast-deep-equal@3.1.3: {} - - fast-safe-stringify@2.1.1: {} - - fast-string-truncated-width@3.0.3: {} - - fast-string-width@3.0.2: - dependencies: - fast-string-truncated-width: 3.0.3 - - fast-wrap-ansi@0.2.0: - dependencies: - fast-string-width: 3.0.2 - - fastest-levenshtein@1.0.16: {} - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - finalhandler@2.1.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - fix-dts-default-cjs-exports@1.0.1: - dependencies: - magic-string: 0.30.21 - mlly: 1.8.1 - rollup: 4.59.0 - - follow-redirects@1.15.11: {} - - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - forwarded@0.2.0: {} - - fresh@2.0.0: {} - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - get-east-asian-width@1.5.0: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-tsconfig@4.13.6: - dependencies: - resolve-pkg-maps: 1.0.0 - - gopd@1.2.0: {} - - grammy@1.41.1: - dependencies: - '@grammyjs/types': 3.25.0 - abort-controller: 3.0.0 - debug: 4.4.3 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - supports-color - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - hast-util-to-html@9.0.5: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.1 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.4 - zwitch: 2.0.4 - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.4 - - help-me@5.0.0: {} - - hono@4.12.8: {} - - html-void-elements@3.0.0: {} - - http-errors@2.0.1: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.2 - toidentifier: 1.0.1 - - husky@9.1.7: {} - - iconv-lite@0.7.2: - dependencies: - safer-buffer: 2.1.2 - - ieee754@1.2.1: {} - - inherits@2.0.4: {} - - ipaddr.js@1.9.1: {} - - is-electron@2.2.2: {} - - is-interactive@2.0.0: {} - - is-promise@4.0.0: {} - - is-stream@2.0.1: {} - - is-unicode-supported@2.1.0: {} - - isomorphic-ws@5.0.0(ws@8.20.0): - dependencies: - ws: 8.20.0 - - joycon@3.1.1: {} - - js-tokens@9.0.1: {} - - jsonwebtoken@9.0.3: - dependencies: - jws: 4.0.1 - lodash.includes: 4.3.0 - lodash.isboolean: 3.0.3 - lodash.isinteger: 4.0.4 - lodash.isnumber: 3.0.3 - lodash.isplainobject: 4.0.6 - lodash.isstring: 4.0.1 - lodash.once: 4.1.1 - ms: 2.1.3 - semver: 7.7.4 - - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.1: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - - lightningcss-android-arm64@1.32.0: - optional: true - - lightningcss-darwin-arm64@1.32.0: - optional: true - - lightningcss-darwin-x64@1.32.0: - optional: true - - lightningcss-freebsd-x64@1.32.0: - optional: true - - lightningcss-linux-arm-gnueabihf@1.32.0: - optional: true - - lightningcss-linux-arm64-gnu@1.32.0: - optional: true - - lightningcss-linux-arm64-musl@1.32.0: - optional: true - - lightningcss-linux-x64-gnu@1.32.0: - optional: true - - lightningcss-linux-x64-musl@1.32.0: - optional: true - - lightningcss-win32-arm64-msvc@1.32.0: - optional: true - - lightningcss-win32-x64-msvc@1.32.0: - optional: true - - lightningcss@1.32.0: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.32.0 - lightningcss-darwin-arm64: 1.32.0 - lightningcss-darwin-x64: 1.32.0 - lightningcss-freebsd-x64: 1.32.0 - lightningcss-linux-arm-gnueabihf: 1.32.0 - lightningcss-linux-arm64-gnu: 1.32.0 - lightningcss-linux-arm64-musl: 1.32.0 - lightningcss-linux-x64-gnu: 1.32.0 - lightningcss-linux-x64-musl: 1.32.0 - lightningcss-win32-arm64-msvc: 1.32.0 - lightningcss-win32-x64-msvc: 1.32.0 - - lilconfig@3.1.3: {} - - lines-and-columns@1.2.4: {} - - load-tsconfig@0.2.5: {} - - lodash.includes@4.3.0: {} - - lodash.isboolean@3.0.3: {} - - lodash.isinteger@4.0.4: {} - - lodash.isnumber@3.0.3: {} - - lodash.isplainobject@4.0.6: {} - - lodash.isstring@4.0.1: {} - - lodash.once@4.1.1: {} - - lodash.snakecase@4.1.1: {} - - lodash@4.17.23: {} - - log-symbols@7.0.1: - dependencies: - is-unicode-supported: 2.1.0 - yoctocolors: 2.1.2 - - loupe@3.2.1: {} - - magic-bytes.js@1.13.0: {} - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - math-intrinsics@1.1.0: {} - - mdast-util-to-hast@13.2.1: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - - media-typer@1.1.0: {} - - merge-descriptors@2.0.0: {} - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-encode@2.0.1: {} - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - mime-db@1.52.0: {} - - mime-db@1.54.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mime-types@3.0.2: - dependencies: - mime-db: 1.54.0 - - mimic-function@5.0.1: {} - - minimist@1.2.8: {} - - mlly@1.8.1: - dependencies: - acorn: 8.16.0 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.3 - - ms@2.1.3: {} - - msedge-tts@2.0.4: - dependencies: - axios: 1.13.6 - buffer: 6.0.3 - isomorphic-ws: 5.0.0(ws@8.20.0) - stream-browserify: 3.0.0 - ws: 8.20.0 - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - mute-stream@3.0.0: {} - - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - - nanoid@3.3.11: {} - - nanoid@5.1.7: {} - - negotiator@1.0.0: {} - - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - - node-wav@0.0.2: {} - - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - obug@2.1.1: {} - - ogg-opus-decoder@1.7.3: - dependencies: - '@wasm-audio-decoders/common': 9.0.7 - '@wasm-audio-decoders/opus-ml': 0.0.2 - codec-parser: 2.5.0 - opus-decoder: 0.7.11 - - on-exit-leak-free@2.1.2: {} - - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - onetime@7.0.0: - dependencies: - mimic-function: 5.0.1 - - oniguruma-parser@0.12.1: {} - - oniguruma-to-es@4.3.5: - dependencies: - oniguruma-parser: 0.12.1 - regex: 6.1.0 - regex-recursion: 6.0.2 - - opus-decoder@0.7.11: - dependencies: - '@wasm-audio-decoders/common': 9.0.7 - - ora@9.3.0: - dependencies: - chalk: 5.6.2 - cli-cursor: 5.0.0 - cli-spinners: 3.4.0 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 7.0.1 - stdin-discarder: 0.3.1 - string-width: 8.2.0 - - p-finally@1.0.0: {} - - p-queue@6.6.2: - dependencies: - eventemitter3: 4.0.7 - p-timeout: 3.2.0 - - p-queue@9.1.0: - dependencies: - eventemitter3: 5.0.4 - p-timeout: 7.0.1 - - p-retry@4.6.2: - dependencies: - '@types/retry': 0.12.0 - retry: 0.13.1 - - p-timeout@3.2.0: - dependencies: - p-finally: 1.0.0 - - p-timeout@7.0.1: {} - - parseurl@1.3.3: {} - - path-to-regexp@8.3.0: {} - - pathe@2.0.3: {} - - pathval@2.0.1: {} - - picocolors@1.1.1: {} - - picomatch@4.0.3: {} - - pino-abstract-transport@3.0.0: - dependencies: - split2: 4.2.0 - - pino-pretty@13.1.3: - dependencies: - colorette: 2.0.20 - dateformat: 4.6.3 - fast-copy: 4.0.2 - fast-safe-stringify: 2.1.1 - help-me: 5.0.0 - joycon: 3.1.1 - minimist: 1.2.8 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pump: 3.0.4 - secure-json-parse: 4.1.0 - sonic-boom: 4.2.1 - strip-json-comments: 5.0.3 - - pino-roll@4.0.0: - dependencies: - date-fns: 4.1.0 - sonic-boom: 4.2.1 - - pino-std-serializers@7.1.0: {} - - pino@10.3.1: - dependencies: - '@pinojs/redact': 0.4.0 - atomic-sleep: 1.0.0 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pino-std-serializers: 7.1.0 - process-warning: 5.0.0 - quick-format-unescaped: 4.0.4 - real-require: 0.2.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 4.2.1 - thread-stream: 4.0.0 - - pirates@4.0.7: {} - - pkg-types@1.3.1: - dependencies: - confbox: 0.1.8 - mlly: 1.8.1 - pathe: 2.0.3 - - postcss-load-config@6.0.1(postcss@8.5.8)(tsx@4.21.0): - dependencies: - lilconfig: 3.1.3 - optionalDependencies: - postcss: 8.5.8 - tsx: 4.21.0 - - postcss@8.5.8: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - process-warning@5.0.0: {} - - property-information@7.1.0: {} - - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - - proxy-from-env@1.1.0: {} - - pump@3.0.4: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - qs@6.15.0: - dependencies: - side-channel: 1.1.0 - - quick-format-unescaped@4.0.4: {} - - range-parser@1.2.1: {} - - raw-body@3.0.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.2 - unpipe: 1.0.0 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - readdirp@4.1.2: {} - - real-require@0.2.0: {} - - regex-recursion@6.0.2: - dependencies: - regex-utilities: 2.3.0 - - regex-utilities@2.3.0: {} - - regex@6.1.0: - dependencies: - regex-utilities: 2.3.0 - - resolve-from@5.0.0: {} - - resolve-pkg-maps@1.0.0: {} - - restore-cursor@5.1.0: - dependencies: - onetime: 7.0.0 - signal-exit: 4.1.0 - - retry@0.13.1: {} - - rolldown@1.0.0-rc.10: - dependencies: - '@oxc-project/types': 0.120.0 - '@rolldown/pluginutils': 1.0.0-rc.10 - optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.10 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.10 - '@rolldown/binding-darwin-x64': 1.0.0-rc.10 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.10 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.10 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.10 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.10 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.10 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.10 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.10 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.10 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.10 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.10 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.10 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.10 - - rollup@4.59.0: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 - fsevents: 2.3.3 - - router@2.2.0: - dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.3.0 - transitivePeerDependencies: - - supports-color - - safe-buffer@5.2.1: {} - - safe-stable-stringify@2.5.0: {} - - safer-buffer@2.1.2: {} - - secure-json-parse@4.1.0: {} - - semver@7.7.4: {} - - send@1.2.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.1 - mime-types: 3.0.2 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - serve-static@2.2.1: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.1 - transitivePeerDependencies: - - supports-color - - setprototypeof@1.2.0: {} - - shiki@4.0.2: - dependencies: - '@shikijs/core': 4.0.2 - '@shikijs/engine-javascript': 4.0.2 - '@shikijs/engine-oniguruma': 4.0.2 - '@shikijs/langs': 4.0.2 - '@shikijs/themes': 4.0.2 - '@shikijs/types': 4.0.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - siginfo@2.0.0: {} - - signal-exit@4.1.0: {} - - simple-yenc@1.0.4: {} - - sisteransi@1.0.5: {} - - sonic-boom@4.2.1: - dependencies: - atomic-sleep: 1.0.0 - - source-map-js@1.2.1: {} - - source-map@0.7.6: {} - - space-separated-tokens@2.0.2: {} - - split2@4.2.0: {} - - stackback@0.0.2: {} - - statuses@2.0.2: {} - - std-env@3.10.0: {} - - std-env@4.0.0: {} - - stdin-discarder@0.3.1: {} - - stream-browserify@3.0.0: - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.2 - - string-width@8.2.0: - dependencies: - get-east-asian-width: 1.5.0 - strip-ansi: 7.2.0 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - - strip-ansi@7.2.0: - dependencies: - ansi-regex: 6.2.2 - - strip-json-comments@5.0.3: {} - - strip-literal@3.1.0: - dependencies: - js-tokens: 9.0.1 - - sucrase@3.35.1: - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - commander: 4.1.1 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.7 - tinyglobby: 0.2.15 - ts-interface-checker: 0.1.13 - - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - - thread-stream@4.0.0: - dependencies: - real-require: 0.2.0 - - tinybench@2.9.0: {} - - tinyexec@0.3.2: {} - - tinyexec@1.0.4: {} - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - - tinypool@1.1.1: {} - - tinyrainbow@2.0.0: {} - - tinyrainbow@3.1.0: {} - - tinyspy@4.0.4: {} - - toidentifier@1.0.1: {} - - tr46@0.0.3: {} - - tree-kill@1.2.2: {} - - trim-lines@3.0.1: {} - - ts-interface-checker@0.1.13: {} - - ts-mixer@6.0.4: {} - - tslib@2.8.1: {} - - tsscmp@1.0.6: {} - - tsup@8.5.1(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3): - dependencies: - bundle-require: 5.1.0(esbuild@0.27.4) - cac: 6.7.14 - chokidar: 4.0.3 - consola: 3.4.2 - debug: 4.4.3 - esbuild: 0.27.4 - fix-dts-default-cjs-exports: 1.0.1 - joycon: 3.1.1 - picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.5.8)(tsx@4.21.0) - resolve-from: 5.0.0 - rollup: 4.59.0 - source-map: 0.7.6 - sucrase: 3.35.1 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tree-kill: 1.2.2 - optionalDependencies: - postcss: 8.5.8 - typescript: 5.9.3 - transitivePeerDependencies: - - jiti - - supports-color - - tsx - - yaml - - tsx@4.21.0: - dependencies: - esbuild: 0.27.4 - get-tsconfig: 4.13.6 - optionalDependencies: - fsevents: 2.3.3 - - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.2 - - typescript@5.9.3: {} - - ufo@1.6.3: {} - - undici-types@7.18.2: {} - - undici@6.21.3: {} - - undici@6.24.1: {} - - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@5.1.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - unpipe@1.0.0: {} - - util-deprecate@1.0.2: {} - - vary@1.1.2: {} - - vfile-message@4.0.3: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.3 - - vite-node@3.2.4(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.3.1(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vite@7.3.1(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0): - dependencies: - esbuild: 0.27.4 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.8 - rollup: 4.59.0 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 25.5.0 - fsevents: 2.3.3 - lightningcss: 1.32.0 - tsx: 4.21.0 - - vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0): - dependencies: - lightningcss: 1.32.0 - picomatch: 4.0.3 - postcss: 8.5.8 - rolldown: 1.0.0-rc.10 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 25.5.0 - esbuild: 0.27.4 - fsevents: 2.3.3 - tsx: 4.21.0 - - vitest@3.2.4(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0): - dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3 - expect-type: 1.3.0 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0) - vite-node: 3.2.4(@types/node@25.5.0)(lightningcss@1.32.0)(tsx@4.21.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 25.5.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vitest@4.1.0(@types/node@25.5.0)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0)): - dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 - es-module-lexer: 2.0.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 4.0.0 - tinybench: 2.9.0 - tinyexec: 1.0.4 - tinyglobby: 0.2.15 - tinyrainbow: 3.1.0 - vite: 8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(tsx@4.21.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 25.5.0 - transitivePeerDependencies: - - msw - - webidl-conversions@3.0.1: {} - - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - wrappy@1.0.2: {} - - ws@8.20.0: {} - - yoctocolors@2.1.2: {} - - zod@3.25.76: {} - - zwitch@2.0.4: {} + dev: false diff --git a/src/main.ts b/src/main.ts index ee337d1b..5db8ea41 100644 --- a/src/main.ts +++ b/src/main.ts @@ -313,7 +313,6 @@ async function autoRegisterBuiltinPlugins( { name: '@openacp/tunnel', version: '1.0.0', description: 'Expose local services via tunnel' }, { name: '@openacp/api-server', version: '1.0.0', description: 'REST API + SSE streaming server' }, { name: '@openacp/telegram', version: '1.0.0', description: 'Telegram adapter with forum topics' }, - { name: '@openacp/slack', version: '1.0.0', description: 'Slack adapter with channels and threads' }, ] // Try to read legacy config for migration @@ -338,7 +337,6 @@ async function autoRegisterBuiltinPlugins( import('./plugins/tunnel/index.js'), import('./plugins/api-server/index.js'), import('./plugins/telegram/index.js'), - import('./plugins/slack/index.js'), ]) for (const result of pluginModules) { diff --git a/src/plugins/__tests__/plugin-wrappers.test.ts b/src/plugins/__tests__/plugin-wrappers.test.ts index c818afce..82c5aadf 100644 --- a/src/plugins/__tests__/plugin-wrappers.test.ts +++ b/src/plugins/__tests__/plugin-wrappers.test.ts @@ -2,8 +2,8 @@ import { describe, it, expect } from 'vitest' import { builtInPlugins } from '../index.js' describe('Built-in plugin wrappers', () => { - it('exports all 9 built-in plugins', () => { - expect(builtInPlugins).toHaveLength(9) + it('exports all 8 built-in plugins', () => { + expect(builtInPlugins).toHaveLength(8) }) it('all plugins have name, version, setup', () => { @@ -31,12 +31,11 @@ describe('Built-in plugin wrappers', () => { expect(names).toContain('@openacp/tunnel') expect(names).toContain('@openacp/api-server') expect(names).toContain('@openacp/telegram') - expect(names).toContain('@openacp/slack') }) it('adapter plugins depend on security and notifications', () => { const adapters = builtInPlugins.filter(p => - ['@openacp/telegram', '@openacp/slack'].includes(p.name) + ['@openacp/telegram'].includes(p.name) ) for (const adapter of adapters) { expect(adapter.pluginDependencies).toBeDefined() diff --git a/src/plugins/core-plugins.ts b/src/plugins/core-plugins.ts index b7ed5adb..a05682f8 100644 --- a/src/plugins/core-plugins.ts +++ b/src/plugins/core-plugins.ts @@ -11,7 +11,6 @@ import notificationsPlugin from './notifications/index.js' import tunnelPlugin from './tunnel/index.js' import apiServerPlugin from './api-server/index.js' import telegramPlugin from './telegram/index.js' -import slackPlugin from './slack/index.js' export const corePlugins = [ // Service plugins (no adapter dependencies) @@ -25,5 +24,4 @@ export const corePlugins = [ apiServerPlugin, // Adapter plugins (depend on security, notifications, etc.) telegramPlugin, - slackPlugin, ] diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 912f113c..716aaba7 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -6,7 +6,6 @@ import notificationsPlugin from './notifications/index.js' import tunnelPlugin from './tunnel/index.js' import apiServerPlugin from './api-server/index.js' import telegramPlugin from './telegram/index.js' -import slackPlugin from './slack/index.js' export const builtInPlugins = [ securityPlugin, @@ -17,5 +16,4 @@ export const builtInPlugins = [ tunnelPlugin, apiServerPlugin, telegramPlugin, - slackPlugin, ] diff --git a/src/plugins/slack/__tests__/adapter-lifecycle.test.ts b/src/plugins/slack/__tests__/adapter-lifecycle.test.ts deleted file mode 100644 index cc6dc3e3..00000000 --- a/src/plugins/slack/__tests__/adapter-lifecycle.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { describe, it, expect, vi } from "vitest"; -import { SlackTextBuffer } from "../text-buffer.js"; - -function createMockQueue() { - return { enqueue: vi.fn().mockResolvedValue({ ts: "123" }) } as any; -} - -describe("SlackAdapter lifecycle — stop() flush", () => { - it("flushes all active text buffers before stopping", async () => { - const queue = createMockQueue(); - const buf1 = new SlackTextBuffer("C1", "sess-1", queue); - const buf2 = new SlackTextBuffer("C2", "sess-2", queue); - buf1.append("buffered text 1"); - buf2.append("buffered text 2"); - - const textBuffers = new Map(); - textBuffers.set("sess-1", buf1); - textBuffers.set("sess-2", buf2); - - for (const [_sessionId, buf] of textBuffers) { - try { await buf.flush(); } catch { /* swallow */ } - buf.destroy(); - } - textBuffers.clear(); - - expect(queue.enqueue).toHaveBeenCalledTimes(2); - expect(queue.enqueue.mock.calls[0][1].text).toContain("buffered text 1"); - expect(queue.enqueue.mock.calls[1][1].text).toContain("buffered text 2"); - expect(textBuffers.size).toBe(0); - }); - - it("continues flushing remaining buffers even if one flush throws", async () => { - const failQueue = { - enqueue: vi.fn() - .mockRejectedValueOnce(new Error("network error")) - .mockResolvedValue({ ts: "456" }), - } as any; - const successQueue = createMockQueue(); - - const buf1 = new SlackTextBuffer("C1", "sess-1", failQueue); - const buf2 = new SlackTextBuffer("C2", "sess-2", successQueue); - buf1.append("text 1"); - buf2.append("text 2"); - - const textBuffers = new Map(); - textBuffers.set("sess-1", buf1); - textBuffers.set("sess-2", buf2); - - for (const [_sessionId, buf] of textBuffers) { - try { await buf.flush(); } catch { /* swallow */ } - buf.destroy(); - } - textBuffers.clear(); - - expect(successQueue.enqueue).toHaveBeenCalledTimes(1); - expect(successQueue.enqueue.mock.calls[0][1].text).toContain("text 2"); - expect(textBuffers.size).toBe(0); - }); -}); - -describe("SlackAdapter lifecycle — session_end flush error handling", () => { - it("cleans up buffer even when flush throws on session_end", async () => { - const failQueue = { - enqueue: vi.fn().mockRejectedValue(new Error("network error")), - } as any; - const buf = new SlackTextBuffer("C1", "sess-1", failQueue); - buf.append("some pending text"); - - const textBuffers = new Map(); - textBuffers.set("sess-1", buf); - - const sessionId = "sess-1"; - const sessionBuf = textBuffers.get(sessionId); - if (sessionBuf) { - try { - await sessionBuf.flush(); - } catch { - // swallow - } - sessionBuf.destroy(); - textBuffers.delete(sessionId); - } - - expect(textBuffers.has("sess-1")).toBe(false); - }); - - it("successfully flushes buffer on session_end when no error", async () => { - const queue = createMockQueue(); - const buf = new SlackTextBuffer("C1", "sess-1", queue); - buf.append("final response"); - - const textBuffers = new Map(); - textBuffers.set("sess-1", buf); - - const sessionBuf = textBuffers.get("sess-1"); - if (sessionBuf) { - try { - await sessionBuf.flush(); - } catch { /* swallow */ } - sessionBuf.destroy(); - textBuffers.delete("sess-1"); - } - - expect(queue.enqueue).toHaveBeenCalledTimes(1); - expect(queue.enqueue.mock.calls[0][1].text).toContain("final response"); - expect(textBuffers.has("sess-1")).toBe(false); - }); -}); diff --git a/src/plugins/slack/__tests__/channel-manager.test.ts b/src/plugins/slack/__tests__/channel-manager.test.ts deleted file mode 100644 index ecf2a9cf..00000000 --- a/src/plugins/slack/__tests__/channel-manager.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { SlackChannelManager } from "../channel-manager.js"; -import type { ISlackSendQueue } from "../send-queue.js"; -import type { SlackChannelConfig } from "../types.js"; - -function makeConfig(overrides: Partial = {}): SlackChannelConfig { - return { - enabled: true, - botToken: "xoxb-test", - appToken: "xapp-test", - signingSecret: "secret", - allowedUserIds: [], - channelPrefix: "openacp", - autoCreateSession: true, - ...overrides, - } as SlackChannelConfig; -} - -function makeMockQueue(overrides: Partial<{ enqueue: ReturnType }> = {}) { - return { - enqueue: vi.fn().mockResolvedValue({ channel: { id: "C_NEW" } }), - ...overrides, - }; -} - -describe("SlackChannelManager", () => { - it("creates channel and returns meta (happy path)", async () => { - const queue = makeMockQueue(); - const manager = new SlackChannelManager(queue as any, makeConfig()); - - const meta = await manager.createChannel("sess-1", "Fix Auth Bug"); - - expect(queue.enqueue).toHaveBeenCalledWith( - "conversations.create", - expect.objectContaining({ is_private: true }), - ); - expect(meta.channelId).toBe("C_NEW"); - expect(meta.channelSlug).toMatch(/^openacp-/); - }); - - it("retries with new slug on name_taken error", async () => { - const nameTakenError = { data: { error: "name_taken" } }; - const queue = { - enqueue: vi.fn() - .mockRejectedValueOnce(nameTakenError) - .mockResolvedValue({ channel: { id: "C_RETRY" } }), - }; - const manager = new SlackChannelManager(queue as any, makeConfig()); - - const meta = await manager.createChannel("sess-2", "Duplicate Session"); - - expect(queue.enqueue).toHaveBeenCalledTimes(2); - expect(meta.channelId).toBe("C_RETRY"); - }); - - it("throws non-name_taken errors", async () => { - const otherError = new Error("rate_limited"); - const queue = { - enqueue: vi.fn().mockRejectedValue(otherError), - }; - const manager = new SlackChannelManager(queue as any, makeConfig()); - - await expect(manager.createChannel("sess-3", "Some Session")).rejects.toThrow("rate_limited"); - }); - - it("invites allowedUserIds when configured", async () => { - const queue = makeMockQueue(); - const manager = new SlackChannelManager( - queue as any, - makeConfig({ allowedUserIds: ["U1", "U2"] }), - ); - - await manager.createChannel("sess-4", "Restricted Session"); - - expect(queue.enqueue).toHaveBeenCalledWith( - "conversations.invite", - expect.objectContaining({ channel: "C_NEW", users: "U1,U2" }), - ); - }); - - it("skips invite when allowedUserIds is empty", async () => { - const queue = makeMockQueue(); - const manager = new SlackChannelManager(queue as any, makeConfig({ allowedUserIds: [] })); - - await manager.createChannel("sess-5", "Open Session"); - - const inviteCalls = (queue.enqueue.mock.calls as any[]).filter( - (call) => call[0] === "conversations.invite", - ); - expect(inviteCalls).toHaveLength(0); - }); - - it("retries up to 3 times on name_taken, then throws", async () => { - const mockQueue: ISlackSendQueue = { - enqueue: vi.fn() - .mockRejectedValueOnce({ data: { error: "name_taken" } }) - .mockRejectedValueOnce({ data: { error: "name_taken" } }) - .mockRejectedValueOnce({ data: { error: "name_taken" } }), - }; - - const manager = new SlackChannelManager(mockQueue, { channelPrefix: "test" } as any); - - await expect(manager.createChannel("s1", "test")).rejects.toThrow(); - expect(mockQueue.enqueue).toHaveBeenCalledTimes(3); - }); - - it("succeeds on second attempt after name_taken", async () => { - const mockQueue: ISlackSendQueue = { - enqueue: vi.fn() - .mockRejectedValueOnce({ data: { error: "name_taken" } }) - .mockResolvedValueOnce({ channel: { id: "C456" } }) - .mockResolvedValue(undefined), - }; - - const manager = new SlackChannelManager(mockQueue, { - channelPrefix: "test", - allowedUserIds: [], - } as any); - - const result = await manager.createChannel("s1", "test"); - expect(result.channelId).toBe("C456"); - expect(mockQueue.enqueue).toHaveBeenCalledTimes(2); - }); -}); diff --git a/src/plugins/slack/__tests__/conformance.test.ts b/src/plugins/slack/__tests__/conformance.test.ts deleted file mode 100644 index 103c2d2d..00000000 --- a/src/plugins/slack/__tests__/conformance.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { runAdapterConformanceTests } from '../../../core/adapter-primitives/__tests__/adapter-conformance.js' -import { MessagingAdapter } from '../../../core/adapter-primitives/messaging-adapter.js' -import { BaseRenderer } from '../../../core/adapter-primitives/rendering/renderer.js' -import type { AdapterCapabilities } from '../../../core/channel.js' - -class TestAdapter extends MessagingAdapter { - readonly name = 'slack' - readonly renderer = new BaseRenderer() - readonly capabilities: AdapterCapabilities = { - streaming: true, richFormatting: true, threads: true, - reactions: false, fileUpload: false, voice: false, - } - async start() {} - async stop() {} - async createSessionThread() { return 'thread-1' } - async renameSessionThread() {} - async sendPermissionRequest() {} - async sendNotification() {} -} - -runAdapterConformanceTests( - () => new TestAdapter( - { configManager: { get: () => ({}) } }, - { enabled: true, maxMessageLength: 4096 }, - ), -) diff --git a/src/plugins/slack/__tests__/event-router.test.ts b/src/plugins/slack/__tests__/event-router.test.ts deleted file mode 100644 index 0c07d375..00000000 --- a/src/plugins/slack/__tests__/event-router.test.ts +++ /dev/null @@ -1,249 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { SlackEventRouter } from "../event-router.js"; -import type { SlackChannelConfig } from "../types.js"; - -function createMockApp() { - const handlers: Record = {}; - return { - message: vi.fn((handler: Function) => { handlers["message"] = handler; }), - _trigger: async (event: string, payload: any) => { - const handler = handlers[event]; - if (handler) await handler(payload); - }, - }; -} - -function makeConfig(overrides: Partial = {}): SlackChannelConfig { - return { - enabled: true, - botToken: "xoxb-test", - appToken: "xapp-test", - signingSecret: "secret", - allowedUserIds: [], - channelPrefix: "openacp", - autoCreateSession: true, - ...overrides, - } as SlackChannelConfig; -} - -describe("SlackEventRouter", () => { - it("ignores bot messages (message has bot_id field)", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue(undefined); - const router = new SlackEventRouter(sessionLookup, onIncoming, "BOT1", "NOTIF", onNewSession, makeConfig()); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "C123", user: "U1", text: "hello", bot_id: "B1" } }); - - expect(onIncoming).not.toHaveBeenCalled(); - expect(onNewSession).not.toHaveBeenCalled(); - }); - - it("ignores own messages (userId matches botUserId)", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue(undefined); - const router = new SlackEventRouter(sessionLookup, onIncoming, "BOT1", "NOTIF", onNewSession, makeConfig()); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "C123", user: "BOT1", text: "hello" } }); - - expect(onIncoming).not.toHaveBeenCalled(); - expect(onNewSession).not.toHaveBeenCalled(); - }); - - it("ignores messages with subtype (edited, deleted)", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue(undefined); - const router = new SlackEventRouter(sessionLookup, onIncoming, "BOT1", "NOTIF", onNewSession, makeConfig()); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "C123", user: "U1", text: "edited", subtype: "message_changed" } }); - - expect(onIncoming).not.toHaveBeenCalled(); - expect(onNewSession).not.toHaveBeenCalled(); - }); - - it("rejects messages from non-allowed users when allowedUserIds is configured", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue(undefined); - const router = new SlackEventRouter( - sessionLookup, - onIncoming, - "BOT1", - "NOTIF", - onNewSession, - makeConfig({ allowedUserIds: ["U_ALLOWED"] }), - ); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "C123", user: "U_NOT_ALLOWED", text: "hello" } }); - - expect(onIncoming).not.toHaveBeenCalled(); - expect(onNewSession).not.toHaveBeenCalled(); - }); - - it("allows messages when allowedUserIds is empty (open access mode)", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue(undefined); - const router = new SlackEventRouter( - sessionLookup, - onIncoming, - "BOT1", - "NOTIF", - onNewSession, - makeConfig({ allowedUserIds: [] }), - ); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "NOTIF", user: "U_ANYONE", text: "hello" } }); - - expect(onNewSession).toHaveBeenCalledWith("hello", "U_ANYONE"); - }); - - it("routes to onIncoming when sessionLookup returns a match", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue({ channelId: "C123", channelSlug: "openacp-session-abc1" }); - const router = new SlackEventRouter(sessionLookup, onIncoming, "BOT1", "NOTIF", onNewSession, makeConfig()); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "C123", user: "U1", text: "hello" } }); - - expect(onIncoming).toHaveBeenCalledWith("openacp-session-abc1", "hello", "U1", undefined); - expect(onNewSession).not.toHaveBeenCalled(); - }); - - it("routes file_share messages with audio clips", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue({ channelId: "C123", channelSlug: "openacp-session-abc1" }); - const router = new SlackEventRouter(sessionLookup, onIncoming, "BOT1", "NOTIF", onNewSession, makeConfig()); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { - message: { - channel: "C123", - user: "U1", - text: "", - subtype: "file_share", - files: [ - { id: "F1", name: "audio_message_abc.mp4", mimetype: "video/mp4", size: 1024, url_private: "https://files.slack.com/F1" }, - ], - }, - }); - - expect(onIncoming).toHaveBeenCalledWith( - "openacp-session-abc1", - "", - "U1", - [{ id: "F1", name: "audio_message_abc.mp4", mimetype: "video/mp4", size: 1024, url_private: "https://files.slack.com/F1" }], - ); - }); - - it("still blocks edited/deleted subtypes", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue({ channelId: "C123", channelSlug: "openacp-session-abc1" }); - const router = new SlackEventRouter(sessionLookup, onIncoming, "BOT1", "NOTIF", onNewSession, makeConfig()); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "C123", user: "U1", text: "edited", subtype: "message_changed" } }); - expect(onIncoming).not.toHaveBeenCalled(); - - await app._trigger("message", { message: { channel: "C123", user: "U1", text: "deleted", subtype: "message_deleted" } }); - expect(onIncoming).not.toHaveBeenCalled(); - }); - - it("routes to onNewSession when message is in notification channel and no session match", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue(undefined); - const router = new SlackEventRouter(sessionLookup, onIncoming, "BOT1", "NOTIF_CHAN", onNewSession, makeConfig()); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "NOTIF_CHAN", user: "U1", text: "new task" } }); - - expect(onNewSession).toHaveBeenCalledWith("new task", "U1"); - expect(onIncoming).not.toHaveBeenCalled(); - }); - - it("falls back to global allowedUserIds when Slack-specific list is empty", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue(undefined); - const router = new SlackEventRouter( - sessionLookup, - onIncoming, - "BOT1", - "NOTIF", - onNewSession, - makeConfig({ allowedUserIds: [] }), - ["U_GLOBAL_ALLOWED"], - ); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "NOTIF", user: "U_RANDOM", text: "hello" } }); - expect(onNewSession).not.toHaveBeenCalled(); - - await app._trigger("message", { message: { channel: "NOTIF", user: "U_GLOBAL_ALLOWED", text: "hello" } }); - expect(onNewSession).toHaveBeenCalledWith("hello", "U_GLOBAL_ALLOWED"); - }); - - it("prefers Slack-specific list over global list when both are set", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue(undefined); - const router = new SlackEventRouter( - sessionLookup, - onIncoming, - "BOT1", - "NOTIF", - onNewSession, - makeConfig({ allowedUserIds: ["U_SLACK_ONLY"] }), - ["U_GLOBAL_ONLY"], - ); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "NOTIF", user: "U_GLOBAL_ONLY", text: "hello" } }); - expect(onNewSession).not.toHaveBeenCalled(); - - await app._trigger("message", { message: { channel: "NOTIF", user: "U_SLACK_ONLY", text: "hello" } }); - expect(onNewSession).toHaveBeenCalledWith("hello", "U_SLACK_ONLY"); - }); - - it("allows all users when both Slack and global lists are empty", async () => { - const onIncoming = vi.fn(); - const onNewSession = vi.fn(); - const sessionLookup = vi.fn().mockReturnValue(undefined); - const router = new SlackEventRouter( - sessionLookup, - onIncoming, - "BOT1", - "NOTIF", - onNewSession, - makeConfig({ allowedUserIds: [] }), - [], - ); - const app = createMockApp(); - router.register(app as any); - - await app._trigger("message", { message: { channel: "NOTIF", user: "U_ANYONE", text: "hello" } }); - expect(onNewSession).toHaveBeenCalledWith("hello", "U_ANYONE"); - }); -}); diff --git a/src/plugins/slack/__tests__/formatter.test.ts b/src/plugins/slack/__tests__/formatter.test.ts deleted file mode 100644 index ddf434e1..00000000 --- a/src/plugins/slack/__tests__/formatter.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { SlackFormatter, markdownToMrkdwn } from "../formatter.js"; -// Import OutgoingMessage type from core - -const fmt = new SlackFormatter(); - -describe("SlackFormatter.formatOutgoing", () => { - it("text message returns section blocks", () => { - const blocks = fmt.formatOutgoing({ type: "text", text: "Hello" } as any); - expect(blocks).toHaveLength(1); - expect(blocks[0].type).toBe("section"); - }); - - it("thought message returns context block", () => { - const blocks = fmt.formatOutgoing({ type: "thought", text: "thinking..." } as any); - expect(blocks[0].type).toBe("context"); - }); - - it("long text (>3000 chars) is split into multiple sections", () => { - const long = "x".repeat(4000); - const blocks = fmt.formatOutgoing({ type: "text", text: long } as any); - expect(blocks.length).toBeGreaterThan(1); - blocks.forEach(b => expect(b.type).toBe("section")); - }); - - it("unknown type returns empty array", () => { - const blocks = fmt.formatOutgoing({ type: "unknown_xyz" } as any); - expect(blocks).toEqual([]); - }); - - it("session_end returns divider + context", () => { - const blocks = fmt.formatSessionEnd("timeout"); - expect(blocks[0].type).toBe("divider"); - expect(blocks[1].type).toBe("context"); - }); -}); - -describe("markdownToMrkdwn", () => { - it("converts bold without turning it into italic", () => { - expect(markdownToMrkdwn("**bold text**")).toBe("*bold text*"); - }); - - it("converts italic correctly", () => { - expect(markdownToMrkdwn("*italic text*")).toBe("_italic text_"); - }); - - it("bold and italic in same string stay separate", () => { - expect(markdownToMrkdwn("**bold** and *italic*")).toBe("*bold* and _italic_"); - }); - - it("converts headers to bold", () => { - expect(markdownToMrkdwn("## Hello")).toBe("*Hello*"); - }); - - it("converts links", () => { - expect(markdownToMrkdwn("[text](https://example.com)")).toBe(""); - }); - - it("converts strikethrough", () => { - expect(markdownToMrkdwn("~~strike~~")).toBe("~strike~"); - }); - - it("converts list items", () => { - expect(markdownToMrkdwn("- item")).toBe("• item"); - }); -}); - -describe("SlackFormatter.formatPermissionRequest", () => { - it("returns section + actions with correct button values", () => { - const req = { - id: "req1", - description: "Allow tool X?", - options: [ - { id: "allow", label: "Allow", isAllow: true }, - { id: "deny", label: "Deny", isAllow: false }, - ], - } as any; - const blocks = fmt.formatPermissionRequest(req); - expect(blocks[0].type).toBe("section"); - expect(blocks[1].type).toBe("actions"); - const actions = blocks[1] as any; - expect(actions.elements[0].value).toBe("req1:allow"); - expect(actions.elements[1].value).toBe("req1:deny"); - expect(actions.elements[0].style).toBe("primary"); - expect(actions.elements[1].style).toBe("danger"); - }); -}); diff --git a/src/plugins/slack/__tests__/permission-handler.test.ts b/src/plugins/slack/__tests__/permission-handler.test.ts deleted file mode 100644 index f46a2f5b..00000000 --- a/src/plugins/slack/__tests__/permission-handler.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { SlackPermissionHandler } from "../permission-handler.js"; -import type { ISlackSendQueue } from "../send-queue.js"; - -function createMockApp() { - let actionHandler: Function | undefined; - return { - action: vi.fn((_pattern: any, handler: Function) => { actionHandler = handler; }), - _triggerAction: async (payload: any) => { - if (actionHandler) await actionHandler(payload); - }, - }; -} - -function makeMockQueue() { - return { - enqueue: vi.fn().mockResolvedValue({}), - }; -} - -describe("SlackPermissionHandler", () => { - it("calls onResponse with parsed requestId and optionId when button is clicked", async () => { - const onResponse = vi.fn(); - const queue = makeMockQueue(); - const handler = new SlackPermissionHandler(queue as any, onResponse); - const app = createMockApp(); - handler.register(app as any); - - await app._triggerAction({ - ack: vi.fn().mockResolvedValue(undefined), - action: { value: "req-123:allow" }, - body: { - channel: { id: "C123" }, - message: { ts: "1234567890.123456" }, - }, - }); - - expect(onResponse).toHaveBeenCalledWith("req-123", "allow"); - }); - - it("ignores action values without colon separator (malformed value)", async () => { - const onResponse = vi.fn(); - const queue = makeMockQueue(); - const handler = new SlackPermissionHandler(queue as any, onResponse); - const app = createMockApp(); - handler.register(app as any); - - await app._triggerAction({ - ack: vi.fn().mockResolvedValue(undefined), - action: { value: "malformed-no-colon" }, - body: { - channel: { id: "C123" }, - message: { ts: "1234567890.123456" }, - }, - }); - - expect(onResponse).not.toHaveBeenCalled(); - }); - - it("updates the original message after response (calls queue.enqueue with chat.update)", async () => { - const onResponse = vi.fn(); - const queue = makeMockQueue(); - const handler = new SlackPermissionHandler(queue as any, onResponse); - const app = createMockApp(); - handler.register(app as any); - - await app._triggerAction({ - ack: vi.fn().mockResolvedValue(undefined), - action: { value: "req-abc:deny" }, - body: { - channel: { id: "C456" }, - message: { ts: "9876543210.654321" }, - }, - }); - - expect(queue.enqueue).toHaveBeenCalledWith("chat.update", expect.objectContaining({ - channel: "C456", - ts: "9876543210.654321", - })); - }); - - it("cleanupSession edits pending permission messages to remove buttons", async () => { - const mockQueue: ISlackSendQueue = { - enqueue: vi.fn().mockResolvedValue({ ts: "msg-ts-1" }), - }; - const handler = new SlackPermissionHandler(mockQueue, vi.fn()); - - handler.trackPendingMessage("req-1", "C123", "msg-ts-1"); - - await handler.cleanupSession("C123"); - - expect(mockQueue.enqueue).toHaveBeenCalledWith("chat.update", expect.objectContaining({ - channel: "C123", - ts: "msg-ts-1", - blocks: [], - })); - }); - - it("handles missing message in body gracefully (no crash when body.message is undefined)", async () => { - const onResponse = vi.fn(); - const queue = makeMockQueue(); - const handler = new SlackPermissionHandler(queue as any, onResponse); - const app = createMockApp(); - handler.register(app as any); - - await expect(app._triggerAction({ - ack: vi.fn().mockResolvedValue(undefined), - action: { value: "req-xyz:allow" }, - body: { - channel: { id: "C789" }, - message: undefined, - }, - })).resolves.not.toThrow(); - - expect(onResponse).toHaveBeenCalledWith("req-xyz", "allow"); - expect(queue.enqueue).not.toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/slack/__tests__/send-queue.test.ts b/src/plugins/slack/__tests__/send-queue.test.ts deleted file mode 100644 index f97e47f9..00000000 --- a/src/plugins/slack/__tests__/send-queue.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { SlackSendQueue } from "../send-queue.js"; -import type { WebClient } from "@slack/web-api"; - -describe("SlackSendQueue", () => { - it("enqueues and resolves a valid method call", async () => { - const mockClient = { - apiCall: vi.fn().mockResolvedValue({ ok: true, ts: "12345" }), - } as unknown as WebClient; - - const queue = new SlackSendQueue(mockClient); - const result = await queue.enqueue("chat.postMessage", { channel: "C123", text: "hi" }); - - expect(mockClient.apiCall).toHaveBeenCalledWith("chat.postMessage", { channel: "C123", text: "hi" }); - expect((result as any).ok).toBe(true); - }); - - it("throws for unknown method", async () => { - const mockClient = { apiCall: vi.fn() } as unknown as WebClient; - const queue = new SlackSendQueue(mockClient); - await expect(queue.enqueue("unknown.method" as any, {})).rejects.toThrow("Unknown Slack method"); - }); - - it("different methods have independent queues", async () => { - const callOrder: string[] = []; - const mockClient = { - apiCall: vi.fn().mockImplementation(async (method: string) => { - callOrder.push(method); - await new Promise(r => setTimeout(r, 10)); - return { ok: true }; - }), - }; - - const queue = new SlackSendQueue(mockClient as any); - - const p1 = queue.enqueue("chat.postMessage", { channel: "C1", text: "a" }); - const p2 = queue.enqueue("conversations.create", { name: "test" }); - - await Promise.all([p1, p2]); - - expect(mockClient.apiCall).toHaveBeenCalledTimes(2); - }); - - it("same method calls are serialized (FIFO order)", async () => { - const callOrder: number[] = []; - const mockClient = { - apiCall: vi.fn().mockImplementation(async (_method: string, params: any) => { - callOrder.push(params.order); - return { ok: true }; - }), - }; - - const queue = new SlackSendQueue(mockClient as any); - - const promises = [ - queue.enqueue("chat.postMessage", { channel: "C1", text: "1", order: 1 }), - queue.enqueue("chat.postMessage", { channel: "C1", text: "2", order: 2 }), - queue.enqueue("chat.postMessage", { channel: "C1", text: "3", order: 3 }), - ]; - - await Promise.all(promises); - - expect(callOrder).toEqual([1, 2, 3]); - }); -}); diff --git a/src/plugins/slack/__tests__/slack-voice.test.ts b/src/plugins/slack/__tests__/slack-voice.test.ts deleted file mode 100644 index 0922b587..00000000 --- a/src/plugins/slack/__tests__/slack-voice.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { SlackTextBuffer } from "../text-buffer.js"; -import type { ISlackSendQueue } from "../send-queue.js"; -import { isAudioClip } from "../utils.js"; - -function createMockQueue(): ISlackSendQueue { - return { - enqueue: vi.fn().mockResolvedValue({ ok: true, ts: "1234567890.123456" }), - }; -} - -describe("SlackTextBuffer.stripTtsBlock", () => { - it("strips TTS block from unflushed buffer", async () => { - const queue = createMockQueue(); - const buf = new SlackTextBuffer("C1", "s1", queue); - - buf.append("Hello [TTS]speak this[/TTS]"); - await buf.stripTtsBlock(); - await buf.flush(); - - // After strip, "Hello" remains; markdownToMrkdwn may adjust formatting - const call = (queue.enqueue as ReturnType).mock.calls[0]; - expect(call[0]).toBe("chat.postMessage"); - expect((call[1] as any).text).toBe("Hello"); - }); - - it("strips multiline TTS blocks from unflushed buffer", async () => { - const queue = createMockQueue(); - const buf = new SlackTextBuffer("C1", "s1", queue); - - buf.append("[TTS]line1\nline2\nline3[/TTS] end"); - await buf.stripTtsBlock(); - await buf.flush(); - - const call = (queue.enqueue as ReturnType).mock.calls[0]; - expect(call[0]).toBe("chat.postMessage"); - expect((call[1] as any).text).toBe("end"); - }); - - it("is a no-op when no TTS block present", async () => { - const queue = createMockQueue(); - const buf = new SlackTextBuffer("C1", "s1", queue); - - buf.append("Hello world"); - await buf.stripTtsBlock(); - await buf.flush(); - - expect(queue.enqueue).toHaveBeenCalledWith("chat.postMessage", expect.objectContaining({ - text: "Hello world", - })); - }); - - it("edits already-posted message via chat.update when TTS was flushed", async () => { - const queue = createMockQueue(); - const buf = new SlackTextBuffer("C1", "s1", queue); - - buf.append("Hello world [TTS]speak this[/TTS]"); - await buf.flush(); // Flush posts the message with TTS block - - // Now strip should edit via chat.update - await buf.stripTtsBlock(); - - // Find the chat.update call - const updateCall = (queue.enqueue as ReturnType).mock.calls.find( - (c: any[]) => c[0] === "chat.update", - ); - expect(updateCall).toBeDefined(); - expect((updateCall![1] as any).channel).toBe("C1"); - expect((updateCall![1] as any).ts).toBe("1234567890.123456"); - expect((updateCall![1] as any).text).toBe("Hello world"); - }); - - it("does not call chat.update when flushed message has no TTS block", async () => { - const queue = createMockQueue(); - const buf = new SlackTextBuffer("C1", "s1", queue); - - buf.append("Hello world"); - await buf.flush(); - - await buf.stripTtsBlock(); - - // Only the postMessage call, no chat.update - expect(queue.enqueue).toHaveBeenCalledTimes(1); - expect(queue.enqueue).toHaveBeenCalledWith("chat.postMessage", expect.anything()); - }); -}); - -describe("isAudioClip detection", () => { - function makeFile(mimetype: string, name: string) { - return { id: "F1", name, mimetype, size: 0, url_private: "https://files.slack.com/x" }; - } - - it("detects video/mp4 with audio_message filename as audio", () => { - expect(isAudioClip(makeFile("video/mp4", "audio_message_abc.mp4"))).toBe(true); - }); - - it("detects audio/* MIME types as audio", () => { - expect(isAudioClip(makeFile("audio/ogg", "recording.ogg"))).toBe(true); - expect(isAudioClip(makeFile("audio/mp4", "voice.m4a"))).toBe(true); - expect(isAudioClip(makeFile("audio/mpeg", "file.mp3"))).toBe(true); - }); - - it("rejects non-audio video/mp4 files", () => { - expect(isAudioClip(makeFile("video/mp4", "screen_recording.mp4"))).toBe(false); - }); - - it("rejects non-audio files", () => { - expect(isAudioClip(makeFile("image/png", "screenshot.png"))).toBe(false); - expect(isAudioClip(makeFile("application/pdf", "doc.pdf"))).toBe(false); - expect(isAudioClip(makeFile("text/plain", "notes.txt"))).toBe(false); - }); -}); diff --git a/src/plugins/slack/__tests__/slug.test.ts b/src/plugins/slack/__tests__/slug.test.ts deleted file mode 100644 index de2e4c95..00000000 --- a/src/plugins/slack/__tests__/slug.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { toSlug } from "../slug.js"; - -describe("toSlug", () => { - it("lowercases and replaces spaces with dashes", () => { - const result = toSlug("Fix Auth Bug"); - expect(result).toMatch(/^openacp-fix-auth-bug-[a-z0-9]{4}$/); - }); - - it("strips special characters", () => { - const result = toSlug("OAuth 2.0 & JWT"); - expect(result).toMatch(/^openacp-oauth-20-jwt-[a-z0-9]{4}$/); - }); - - it("uses custom prefix", () => { - const result = toSlug("My Session", "myapp"); - expect(result).toMatch(/^myapp-my-session-[a-z0-9]{4}$/); - }); - - it("collapses consecutive dashes", () => { - const result = toSlug("Hello World"); - expect(result).not.toMatch(/--/); - }); - - it("suffix is lowercase alphanumeric only", () => { - // Run many times to probabilistically verify - for (let i = 0; i < 20; i++) { - const result = toSlug("test"); - expect(result).toMatch(/^[a-z0-9-]+$/); - } - }); - - it("result is ≤80 chars", () => { - const long = "A".repeat(200); - expect(toSlug(long).length).toBeLessThanOrEqual(80); - }); -}); diff --git a/src/plugins/slack/__tests__/text-buffer.test.ts b/src/plugins/slack/__tests__/text-buffer.test.ts deleted file mode 100644 index 78af315e..00000000 --- a/src/plugins/slack/__tests__/text-buffer.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { SlackTextBuffer } from "../text-buffer.js"; - -describe("SlackTextBuffer", () => { - it("flushes buffered text as a single message", async () => { - const mockQueue = { - enqueue: vi.fn().mockResolvedValue({}), - } as any; - const buf = new SlackTextBuffer("C123", "sess1", mockQueue); - - buf.append("Hello "); - buf.append("world"); - await buf.flush(); - - expect(mockQueue.enqueue).toHaveBeenCalledTimes(1); - const call = mockQueue.enqueue.mock.calls[0]; - expect(call[1].text).toContain("Hello"); - expect(call[1].text).toContain("world"); - }); - - it("does not post empty content", async () => { - const mockQueue = { enqueue: vi.fn().mockResolvedValue({}) } as any; - const buf = new SlackTextBuffer("C123", "sess1", mockQueue); - await buf.flush(); - expect(mockQueue.enqueue).not.toHaveBeenCalled(); - }); - - it("does not lose content appended during flush", async () => { - const resolvers: Array<() => void> = []; - const mockQueue = { - enqueue: vi.fn().mockImplementation( - () => new Promise(r => { resolvers.push(r); }), - ), - } as any; - const buf = new SlackTextBuffer("C123", "sess1", mockQueue); - - buf.append("first"); - const flushPromise = buf.flush(); // starts flush, blocks on first enqueue - - // Wait for first enqueue to be called - await new Promise(r => setTimeout(r, 10)); - - // Append more content while flush is in progress - buf.append(" second"); - - // Unblock first enqueue — this triggers re-flush in finally block - resolvers[0](); - - // Wait for re-flush to call enqueue again, then unblock it - await new Promise(r => setTimeout(r, 20)); - if (resolvers[1]) resolvers[1](); - - await flushPromise; - await new Promise(r => setTimeout(r, 20)); - - const allText = mockQueue.enqueue.mock.calls - .map((c: any) => c[1].text as string) - .join(" "); - expect(allText).toContain("second"); - }); - - it("concurrent flush() awaits ongoing flush instead of returning immediately", async () => { - let resolveFirst!: () => void; - const firstCallPromise = new Promise(r => { resolveFirst = r; }); - const postResults: string[] = []; - - const mockQueue: { enqueue: ReturnType } = { - enqueue: vi.fn().mockImplementation(async (_method: string, _params: unknown) => { - if (postResults.length === 0) { - postResults.push("first-start"); - await firstCallPromise; - postResults.push("first-end"); - } else { - postResults.push("second"); - } - return { ts: "123" }; - }), - }; - - const buf = new SlackTextBuffer("C123", "sess-1", mockQueue as any); - buf.append("hello "); - - // Start flush1 — this blocks on firstCallPromise - const flush1 = buf.flush(); - - // Append more while flush1 is in flight - buf.append("world"); - - // Call flush2 while flush1 is still in progress - const flush2 = buf.flush(); - - // flush2 should not have resolved yet (first flush is still blocked) - let flush2Resolved = false; - flush2.then(() => { flush2Resolved = true; }); - - // Yield to microtask queue — if flush2 returned immediately (old bug), - // flush2Resolved would already be true here - await Promise.resolve(); - await Promise.resolve(); - - // Under the buggy implementation: flush2 returns immediately when flushing=true - // so flush2Resolved would be true even though "world" hasn't been sent yet. - // Under the fixed implementation: flush2 awaits the ongoing flush promise, - // so flush2Resolved is still false while firstCallPromise is unresolved. - expect(flush2Resolved).toBe(false); - - // Now unblock the first flush - resolveFirst(); - await flush1; - await flush2; - - // After awaiting both, all content must have been sent - expect(mockQueue.enqueue).toHaveBeenCalledTimes(2); - expect(postResults).toContain("second"); - - buf.destroy(); - }); - - it("destroy clears buffer and timer", async () => { - const mockQueue = { enqueue: vi.fn().mockResolvedValue({}) } as any; - const buf = new SlackTextBuffer("C123", "sess1", mockQueue); - buf.append("text"); - buf.destroy(); - // After destroy, flush should not post anything - await buf.flush(); - expect(mockQueue.enqueue).not.toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/slack/adapter.ts b/src/plugins/slack/adapter.ts deleted file mode 100644 index 94a180d2..00000000 --- a/src/plugins/slack/adapter.ts +++ /dev/null @@ -1,564 +0,0 @@ -// src/adapters/slack/adapter.ts -import fs from "node:fs"; -import { App } from "@slack/bolt"; -import { WebClient } from "@slack/web-api"; -import type { OpenACPCore } from "../../core/core.js"; -import type { - OutgoingMessage, - PermissionRequest, - NotificationMessage, - Attachment, -} from "../../core/types.js"; -import type { AdapterCapabilities } from "../../core/channel.js"; -import type { FileServiceInterface } from "../../core/plugin/types.js"; -import { createChildLogger } from "../../core/utils/log.js"; -import { MessagingAdapter, type MessagingAdapterConfig } from "../../core/adapter-primitives/messaging-adapter.js"; -import type { DisplayVerbosity } from "../../core/adapter-primitives/format-types.js"; -import type { IRenderer } from "../../core/adapter-primitives/rendering/renderer.js"; -import { SlackRenderer } from "./renderer.js"; -const log = createChildLogger({ module: "slack" }); - -import type { SlackChannelConfig } from "./types.js"; -import type { SlackSessionMeta, SlackFileInfo } from "./types.js"; -import { SlackSendQueue } from "./send-queue.js"; -import { SlackFormatter } from "./formatter.js"; -import { SlackChannelManager } from "./channel-manager.js"; -import { SlackPermissionHandler } from "./permission-handler.js"; -import { SlackEventRouter } from "./event-router.js"; -import { SlackTextBuffer } from "./text-buffer.js"; -import { toSlug } from "./slug.js"; -import { isAudioClip } from "./utils.js"; - -export class SlackAdapter extends MessagingAdapter { - readonly name = 'slack'; - readonly renderer!: IRenderer; - readonly capabilities: AdapterCapabilities = { - streaming: true, richFormatting: true, threads: true, - reactions: false, fileUpload: true, voice: true, - }; - - private core: OpenACPCore; - private app!: App; - private webClient!: WebClient; - private queue!: SlackSendQueue; - private formatter: SlackFormatter; - private channelManager!: SlackChannelManager; - private permissionHandler!: SlackPermissionHandler; - private eventRouter!: SlackEventRouter; - private sessions = new Map(); - private textBuffers = new Map(); - private botUserId = ""; - private slackConfig: SlackChannelConfig; - private fileService!: FileServiceInterface; - - constructor(core: OpenACPCore, config: SlackChannelConfig) { - super( - { configManager: core.configManager }, - { ...config as Record, maxMessageLength: 3000, enabled: config.enabled ?? true } as MessagingAdapterConfig, - ); - this.core = core; - this.slackConfig = config; - this.formatter = new SlackFormatter(); - (this as { renderer: IRenderer }).renderer = new SlackRenderer(this.formatter); - } - - async start(): Promise { - const { botToken, appToken, signingSecret } = this.slackConfig; - - if (!botToken || !appToken || !signingSecret) { - throw new Error("Slack adapter requires botToken, appToken, and signingSecret"); - } - - this.app = new App({ - token: botToken, - appToken, - signingSecret, - socketMode: true, - }); - - this.webClient = new WebClient(botToken); - this.queue = new SlackSendQueue(this.webClient); - this.fileService = this.core.fileService; - - // Resolve bot user ID — required to filter bot's own messages (prevent infinite loop) - const authResult = await this.webClient.auth.test(); - if (!authResult.user_id) { - throw new Error("Slack auth.test() did not return user_id — verify botToken is valid"); - } - this.botUserId = authResult.user_id as string; - log.info({ botUserId: this.botUserId }, "Slack bot authenticated"); - - this.channelManager = new SlackChannelManager(this.queue, this.slackConfig); - - // Permission handler — resolve permission gate when user clicks a button - this.permissionHandler = new SlackPermissionHandler( - this.queue, - (requestId, optionId) => { - for (const [sessionId, _meta] of this.sessions) { - const session = this.core.sessionManager.getSession(sessionId); - if (session && session.permissionGate.requestId === requestId) { - session.permissionGate.resolve(optionId); - log.info({ sessionId, requestId, optionId }, "Permission resolved"); - return; - } - } - log.warn({ requestId, optionId }, "No matching session found for permission response"); - }, - ); - this.permissionHandler.register(this.app); - - // Event router — dispatch incoming messages from session channels to core - this.eventRouter = new SlackEventRouter( - (slackChannelId) => { - for (const meta of this.sessions.values()) { - if (meta.channelId === slackChannelId) return meta; - } - return undefined; - }, - (sessionChannelSlug, text, userId, files) => { - const processFiles = async (): Promise => { - if (!files?.length) return undefined; - const audioFiles = files.filter((f) => isAudioClip(f)); - if (!audioFiles.length) return undefined; - - const attachments: Attachment[] = []; - for (const file of audioFiles) { - const buffer = await this.downloadSlackFile(file.url_private); - if (!buffer) continue; - const mimeType = file.mimetype === "video/mp4" ? "audio/mp4" : file.mimetype; - const sessionId = this.core.sessionManager.getSessionByThread("slack", sessionChannelSlug)?.id; - if (!sessionId) continue; - const att = await this.fileService.saveFile(sessionId, file.name, buffer, mimeType); - attachments.push(att); - } - return attachments.length > 0 ? attachments : undefined; - }; - - processFiles() - .then((attachments) => { - this.core - .handleMessage({ - channelId: "slack", - threadId: sessionChannelSlug, - userId, - text, - attachments, - }) - .catch((err) => log.error({ err }, "handleMessage error")); - }) - .catch((err) => log.error({ err }, "Failed to process audio files")); - }, - this.botUserId, - this.slackConfig.notificationChannelId, - // onNewSession: reply with guidance when user messages the notification channel - async (_text, _userId) => { - if (this.slackConfig.notificationChannelId) { - await this.queue.enqueue("chat.postMessage", { - channel: this.slackConfig.notificationChannelId, - text: "💬 To start a new session, use the `/openacp-new` slash command in any channel.", - }).catch((err: unknown) => log.warn({ err }, "Failed to send onNewSession reply")); - } - }, - this.slackConfig, - this.core.configManager.get().security.allowedUserIds, - ); - this.eventRouter.register(this.app); - - // Start Bolt (Socket Mode) - await this.app.start(); - log.info("Slack adapter started (Socket Mode)"); - - // Create startup session + channel (configurable — set autoCreateSession: false to skip) - if (this.slackConfig.autoCreateSession !== false) { - await this._createStartupSession(); - } - } - - private async downloadSlackFile(url: string): Promise { - try { - const resp = await fetch(url, { - headers: { Authorization: `Bearer ${this.slackConfig.botToken}` }, - }); - if (!resp.ok) { - log.warn({ status: resp.status }, "Failed to download Slack file"); - return null; - } - // Slack returns 200 with HTML login page if bot lacks files:read scope - const contentType = resp.headers.get("content-type") ?? ""; - if (contentType.includes("text/html")) { - log.warn("Slack file download returned HTML instead of binary — bot likely missing files:read scope. Reinstall the Slack app with files:read scope."); - return null; - } - return Buffer.from(await resp.arrayBuffer()); - } catch (err) { - log.error({ err }, "Error downloading Slack file"); - return null; - } - } - - private async uploadAudioFile(channelId: string, att: Attachment): Promise { - const fileBuffer = await fs.promises.readFile(att.filePath); - await this.webClient.files.uploadV2({ - channel_id: channelId, - file: fileBuffer, - filename: att.fileName, - }); - } - - private async _createStartupSession(): Promise { - try { - let reuseChannelId = this.slackConfig.startupChannelId; - - // Try to reuse existing startup channel (Telegram ensureTopics pattern) - if (reuseChannelId) { - try { - const info = await this.queue.enqueue>( - "conversations.info", { channel: reuseChannelId }, - ); - const channel = (info as Record)?.channel as Record | undefined; - if (!channel || typeof channel.is_archived !== "boolean") { - log.warn({ reuseChannelId }, "Unexpected conversations.info response shape, creating new channel"); - reuseChannelId = undefined; - } else if (channel.is_archived) { - await this.queue.enqueue("conversations.unarchive", { channel: reuseChannelId }); - log.info({ channelId: reuseChannelId }, "Unarchived startup channel for reuse"); - } - } catch { - // Channel deleted or inaccessible — will create new - reuseChannelId = undefined; - } - } - - if (reuseChannelId) { - // Reuse existing channel — create session pointing to it - let hasSession = false; - for (const m of this.sessions.values()) { - if (m.channelId === reuseChannelId) { hasSession = true; break; } - } - if (!hasSession) { - const session = await this.core.handleNewSession("slack", undefined, undefined, { createThread: false }); - const slug = `startup-${session.id.slice(0, 8)}`; - this.sessions.set(session.id, { channelId: reuseChannelId, channelSlug: slug }); - session.threadId = slug; - // Persist slug to session store so session resume after restart can find it - await this.core.sessionManager.patchRecord(session.id, { - platform: { topicId: slug }, - }); - log.info({ sessionId: session.id, channelId: reuseChannelId }, "Reused startup channel"); - } - } else { - // Create new channel + session - const session = await this.core.handleNewSession("slack", undefined, undefined, { createThread: true }); - if (!session.threadId) { - log.error({ sessionId: session.id }, "Startup session created without threadId"); - return; - } - - // Persist channel ID to config for reuse on next restart - const meta = this.sessions.get(session.id); - if (meta) { - await this.core.configManager.save( - { channels: { slack: { startupChannelId: meta.channelId } } }, - ); - log.info({ sessionId: session.id, channelId: meta.channelId }, "Saved startup channel to config"); - } - } - - // Notify - if (this.slackConfig.notificationChannelId) { - const startupMeta = [...this.sessions.values()].find(m => - m.channelId === (reuseChannelId ?? this.slackConfig.startupChannelId) - ); - if (startupMeta) { - await this.queue.enqueue("chat.postMessage", { - channel: this.slackConfig.notificationChannelId, - text: `✅ OpenACP ready — chat with the agent in <#${startupMeta.channelId}>`, - }); - } - } - } catch (err) { - log.error({ err }, "Failed to create/reuse Slack startup session"); - } - } - - async stop(): Promise { - // Flush all active text buffers before stopping to prevent data loss - for (const [sessionId, buf] of this.textBuffers) { - try { - await buf.flush(); - } catch (err) { - log.warn({ err, sessionId }, "Flush failed during stop"); - } - buf.destroy(); - } - this.textBuffers.clear(); - await this.app.stop(); - log.info("Slack adapter stopped"); - } - - // --- MessagingAdapter implementations --- - - async createSessionThread(sessionId: string, name: string): Promise { - const meta = await this.channelManager.createChannel(sessionId, name); - this.sessions.set(sessionId, meta); - log.info({ sessionId, channelId: meta.channelId, slug: meta.channelSlug }, "Session channel created"); - // Return the slug as the threadId so that lookups via getSessionByThread work - return meta.channelSlug; - } - - async renameSessionThread(sessionId: string, newName: string): Promise { - const meta = this.sessions.get(sessionId); - if (!meta) return; - - const newSlug = toSlug(newName, this.slackConfig.channelPrefix ?? "openacp"); - - try { - await this.queue.enqueue("conversations.rename", { - channel: meta.channelId, - name: newSlug, - }); - meta.channelSlug = newSlug; - // Update session.threadId so getSessionByThread() keeps working after rename - const session = this.core.sessionManager.getSession(sessionId); - if (session) session.threadId = newSlug; - const existingRecord = this.core.sessionManager.getSessionRecord(sessionId); - await this.core.sessionManager.patchRecord(sessionId, { - name: newName, - platform: { ...(existingRecord?.platform ?? {}), topicId: newSlug }, - }); - log.info({ sessionId, newSlug }, "Session channel renamed"); - } catch (err) { - log.warn({ err, sessionId }, "Failed to rename Slack channel"); - } - } - - async deleteSessionThread(sessionId: string): Promise { - const meta = this.sessions.get(sessionId); - if (!meta) return; - - try { - await this.permissionHandler.cleanupSession(meta.channelId); - } catch (err) { - log.warn({ err, sessionId }, "Failed to clean up permission buttons"); - } - - try { - await this.channelManager.archiveChannel(meta.channelId); - log.info({ sessionId, channelId: meta.channelId }, "Session channel archived"); - } catch (err) { - log.warn({ err, sessionId }, "Failed to archive Slack channel"); - } - this.sessions.delete(sessionId); - const buf = this.textBuffers.get(sessionId); - if (buf) { buf.destroy(); this.textBuffers.delete(sessionId); } - } - - private _sessionMetas = new Map(); - - private getSessionMeta(sessionId: string): SlackSessionMeta | undefined { - return this._sessionMetas.get(sessionId); - } - - private getTextBuffer(sessionId: string, channelId: string): SlackTextBuffer { - let buf = this.textBuffers.get(sessionId); - if (!buf) { - buf = new SlackTextBuffer(channelId, sessionId, this.queue); - this.textBuffers.set(sessionId, buf); - } - return buf; - } - - async sendMessage(sessionId: string, content: OutgoingMessage): Promise { - const meta = this.sessions.get(sessionId); - if (!meta) { - log.warn({ sessionId }, "No Slack channel for session, skipping message"); - return; - } - - // Store meta per-session so concurrent calls for different sessions don't overwrite each other - this._sessionMetas.set(sessionId, meta); - try { - await super.sendMessage(sessionId, content); - } finally { - this._sessionMetas.delete(sessionId); - } - } - - // --- Handler overrides (dispatched by base class) --- - - protected async handleText(sessionId: string, content: OutgoingMessage): Promise { - const meta = this.getSessionMeta(sessionId); - if (!meta) return; - // Text chunks are buffered and flushed as a single message after idle timeout - const buf = this.getTextBuffer(sessionId, meta.channelId); - buf.append(content.text ?? ""); - } - - protected async handleSessionEnd(sessionId: string, content: OutgoingMessage): Promise { - const meta = this.getSessionMeta(sessionId); - if (!meta) return; - // Flush any pending text first - await this.flushTextBuffer(sessionId); - - const blocks = this.formatter.formatOutgoing(content); - if (blocks.length === 0) return; - - try { - await this.queue.enqueue("chat.postMessage", { - channel: meta.channelId, - text: content.text ?? content.type, - blocks, - }); - } catch (err) { - log.error({ err, sessionId, type: content.type }, "Failed to post Slack message"); - } - } - - protected async handleError(sessionId: string, content: OutgoingMessage): Promise { - const meta = this.getSessionMeta(sessionId); - if (!meta) return; - // Flush any pending text first - await this.flushTextBuffer(sessionId); - - const blocks = this.formatter.formatOutgoing(content); - if (blocks.length === 0) return; - - try { - await this.queue.enqueue("chat.postMessage", { - channel: meta.channelId, - text: content.text ?? content.type, - blocks, - }); - } catch (err) { - log.error({ err, sessionId, type: content.type }, "Failed to post Slack message"); - } - } - - protected async handleAttachment(sessionId: string, content: OutgoingMessage): Promise { - const meta = this.getSessionMeta(sessionId); - if (!meta || !content.attachment) return; - if (content.attachment.type === "audio") { - try { - await this.uploadAudioFile(meta.channelId, content.attachment); - const buf = this.textBuffers.get(sessionId); - if (buf) await buf.stripTtsBlock(); - } catch (err) { - log.error({ err, sessionId }, "Failed to upload audio to Slack"); - } - } - } - - protected async handleThought(sessionId: string, content: OutgoingMessage, _verbosity: DisplayVerbosity): Promise { - await this.postFormattedMessage(sessionId, content); - } - - protected async handleToolCall(sessionId: string, content: OutgoingMessage, _verbosity: DisplayVerbosity): Promise { - await this.postFormattedMessage(sessionId, content); - } - - protected async handleToolUpdate(sessionId: string, content: OutgoingMessage, _verbosity: DisplayVerbosity): Promise { - await this.postFormattedMessage(sessionId, content); - } - - protected async handlePlan(sessionId: string, content: OutgoingMessage, _verbosity: DisplayVerbosity): Promise { - await this.postFormattedMessage(sessionId, content); - } - - protected async handleUsage(sessionId: string, content: OutgoingMessage, _verbosity: DisplayVerbosity): Promise { - await this.postFormattedMessage(sessionId, content); - } - - protected async handleSystem(sessionId: string, content: OutgoingMessage): Promise { - await this.postFormattedMessage(sessionId, content); - } - - // --- Private helpers --- - - private async flushTextBuffer(sessionId: string): Promise { - const buf = this.textBuffers.get(sessionId); - if (buf) { - try { - await buf.flush(); - } catch (err) { - log.warn({ err, sessionId }, "Flush failed on session_end"); - } - buf.destroy(); - this.textBuffers.delete(sessionId); - } - } - - private async postFormattedMessage(sessionId: string, content: OutgoingMessage): Promise { - const meta = this.getSessionMeta(sessionId); - if (!meta) return; - const blocks = this.formatter.formatOutgoing(content); - if (blocks.length === 0) return; - - try { - await this.queue.enqueue("chat.postMessage", { - channel: meta.channelId, - text: content.text ?? content.type, - blocks, - }); - } catch (err) { - log.error({ err, sessionId, type: content.type }, "Failed to post Slack message"); - } - } - - // NOTE: Async flow — different from Telegram adapter. - // Telegram: sendPermissionRequest awaits user response inline. - // Slack: posts interactive buttons and returns immediately. - // Resolution happens asynchronously via the Bolt action handler in - // SlackPermissionHandler, which calls the PermissionResponseCallback - // passed during construction. The callback iterates sessions to find - // the matching permissionGate and resolves it. - async sendPermissionRequest( - sessionId: string, - request: PermissionRequest, - ): Promise { - const meta = this.sessions.get(sessionId); - if (!meta) return; - - log.info({ sessionId, requestId: request.id }, "Sending Slack permission request"); - const blocks = this.formatter.formatPermissionRequest(request); - - try { - const result = await this.queue.enqueue("chat.postMessage", { - channel: meta.channelId, - text: `Permission request: ${request.description}`, - blocks, - }); - const ts = (result as { ts?: string })?.ts; - if (ts) { - this.permissionHandler.trackPendingMessage(request.id, meta.channelId, ts); - } - } catch (err) { - log.error({ err, sessionId }, "Failed to post Slack permission request"); - } - } - - async sendNotification(notification: NotificationMessage): Promise { - if (!this.slackConfig.notificationChannelId) return; - - const emoji: Record = { - completed: "✅", - error: "❌", - permission: "🔐", - input_required: "💬", - }; - const icon = emoji[notification.type] ?? "ℹ️"; - const text = `${icon} *${notification.sessionName ?? "Session"}*\n${notification.summary}`; - const blocks = this.formatter.formatNotification(text); - - try { - await this.queue.enqueue("chat.postMessage", { - channel: this.slackConfig.notificationChannelId, - text, - blocks, - }); - } catch (err) { - log.warn({ err, sessionId: notification.sessionId }, "Failed to send Slack notification"); - } - } -} - -export type { SlackChannelConfig } from "./types.js"; diff --git a/src/plugins/slack/channel-manager.ts b/src/plugins/slack/channel-manager.ts deleted file mode 100644 index 8f0350f9..00000000 --- a/src/plugins/slack/channel-manager.ts +++ /dev/null @@ -1,67 +0,0 @@ -// src/adapters/slack/channel-manager.ts -import type { ISlackSendQueue } from "./send-queue.js"; -import { toSlug } from "./slug.js"; -import type { SlackSessionMeta } from "./types.js"; -import type { SlackChannelConfig } from "./types.js"; - -export interface ISlackChannelManager { - createChannel(sessionId: string, sessionName: string): Promise; - archiveChannel(channelId: string): Promise; - notifyChannel(text: string): Promise; -} - -export class SlackChannelManager implements ISlackChannelManager { - constructor( - private queue: ISlackSendQueue, - private config: SlackChannelConfig, - ) {} - - async createChannel(sessionId: string, sessionName: string): Promise { - let lastError: unknown; - - for (let attempt = 0; attempt < 3; attempt++) { - const finalSlug = toSlug(sessionName, this.config.channelPrefix ?? "openacp"); - - try { - const res = await this.queue.enqueue<{ channel: { id: string } }>( - "conversations.create", - { name: finalSlug, is_private: true }, - ); - const channelId = res.channel.id; - - // Bot is automatically a member of private channels it creates — no join/invite needed. - // Invite configured users so they can access the channel. - const userIds = this.config.allowedUserIds ?? []; - if (userIds.length > 0) { - await this.queue.enqueue("conversations.invite", { - channel: channelId, - users: userIds.join(","), - }); - } - - return { channelId, channelSlug: finalSlug }; - } catch (err: any) { - if (err?.data?.error === "name_taken" && attempt < 2) { - lastError = err; - continue; - } - throw err; - } - } - - throw lastError; - } - - async archiveChannel(channelId: string): Promise { - await this.queue.enqueue("conversations.archive", { channel: channelId }); - } - - async notifyChannel(text: string): Promise { - if (this.config.notificationChannelId) { - await this.queue.enqueue("chat.postMessage", { - channel: this.config.notificationChannelId, - text, - }); - } - } -} diff --git a/src/plugins/slack/event-router.ts b/src/plugins/slack/event-router.ts deleted file mode 100644 index e2e4af48..00000000 --- a/src/plugins/slack/event-router.ts +++ /dev/null @@ -1,105 +0,0 @@ -// src/adapters/slack/event-router.ts -import type { App } from "@slack/bolt"; -import type { SlackSessionMeta, SlackFileInfo } from "./types.js"; -import type { SlackChannelConfig } from "./types.js"; -import { createChildLogger } from "../../core/utils/log.js"; -const log = createChildLogger({ module: "slack-event-router" }); - -/** Subset of Bolt's message event fields used by the router */ -interface SlackMessageEvent { - bot_id?: string; - subtype?: string; - channel: string; - text?: string; - user?: string; - files?: Array<{ - id: string; - name: string; - mimetype: string; - size: number; - url_private: string; - }>; -} - -// Callback to look up which session (if any) owns a Slack channelId -export type SessionLookup = (channelId: string) => SlackSessionMeta | undefined; - -// Callback to dispatch an incoming message to core -export type IncomingMessageCallback = (sessionId: string, text: string, userId: string, files?: SlackFileInfo[]) => void; - -// Callback to create a new session when user messages the notification channel -export type NewSessionCallback = (text: string, userId: string) => void; - -export interface ISlackEventRouter { - register(app: App): void; -} - -export class SlackEventRouter implements ISlackEventRouter { - constructor( - private sessionLookup: SessionLookup, - private onIncoming: IncomingMessageCallback, - private botUserId: string, - private notificationChannelId: string | undefined, - private onNewSession: NewSessionCallback, - private config: SlackChannelConfig, - private globalAllowedUserIds: string[] = [], - ) {} - - private isAllowedUser(userId: string): boolean { - const slackAllowed = this.config.allowedUserIds ?? []; - const allowed = slackAllowed.length > 0 ? slackAllowed : this.globalAllowedUserIds; - if (allowed.length === 0) return true; - return allowed.includes(userId); - } - - register(app: App): void { - app.message(async ({ message }) => { - log.debug({ message }, "Slack raw message event"); - - const msg = message as unknown as SlackMessageEvent; - - if (msg.bot_id) return; - const subtype = msg.subtype; - if (subtype && subtype !== "file_share") return; // edited, deleted, etc. - - const channelId = msg.channel; - const text: string = msg.text ?? ""; - const userId: string = msg.user ?? ""; - - const files: SlackFileInfo[] | undefined = msg.files?.map((f) => ({ - id: f.id, - name: f.name, - mimetype: f.mimetype, - size: f.size, - url_private: f.url_private, - })); - - log.debug({ channelId, userId, text }, "Slack message received"); - - // Ignore messages from the bot itself - if (userId === this.botUserId) return; - - // Enforce allowedUserIds - if (!this.isAllowedUser(userId)) { - log.warn({ userId }, "slack: message from non-allowed user rejected"); - return; - } - - const session = this.sessionLookup(channelId); - if (session) { - // Message to an existing session channel - log.debug({ channelId, sessionSlug: session.channelSlug }, "Routing to session"); - this.onIncoming(session.channelSlug, text, userId, files); - return; - } - - log.debug({ channelId, notificationChannelId: this.notificationChannelId }, "No session found for channel"); - - // Message to the notification channel → create new session - if (this.notificationChannelId && channelId === this.notificationChannelId) { - this.onNewSession(text, userId); - return; - } - }); - } -} diff --git a/src/plugins/slack/formatter.ts b/src/plugins/slack/formatter.ts deleted file mode 100644 index 83f8f493..00000000 --- a/src/plugins/slack/formatter.ts +++ /dev/null @@ -1,133 +0,0 @@ -// src/adapters/slack/formatter.ts -import type { types } from "@slack/bolt"; -import type { OutgoingMessage, PermissionRequest } from "../../core/types.js"; -import { splitSafe } from "./utils.js"; - -type KnownBlock = types.KnownBlock; - -export interface ISlackFormatter { - formatOutgoing(message: OutgoingMessage): KnownBlock[]; - formatPermissionRequest(req: PermissionRequest): KnownBlock[]; - formatNotification(text: string): KnownBlock[]; - formatSessionEnd(reason?: string): KnownBlock[]; -} - -/** - * Convert a markdown string to Slack mrkdwn format. - * Handles the most common patterns from AI responses. - */ -export function markdownToMrkdwn(text: string): string { - return text - // Fenced code blocks — preserve as-is (Slack supports ``` natively) - // Headers: # H1 → placeholder (protected from italic regex) - .replace(/^#{1,6}\s+(.+)$/gm, "\x00BOLD\x00$1\x00BOLD\x00") - // Bold: **text** → placeholder - .replace(/\*\*(.+?)\*\*/g, "\x00BOLD\x00$1\x00BOLD\x00") - // Italic: *text* → _text_ (won't match placeholder tokens) - .replace(/(? - .replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, "<$2|$1>") - // Unordered lists: "- item" or "* item" → "• item" - .replace(/^[ \t]*[-*]\s+/gm, "• ") - // Ordered lists: "1. item" → "1. item" (already fine in mrkdwn) - .trim(); -} - -// Slack mrkdwn text block, max 3000 chars per section -const SECTION_LIMIT = 3000; - -function section(text: string): KnownBlock { - return { type: "section", text: { type: "mrkdwn", text: text.slice(0, SECTION_LIMIT) } }; -} - -function context(text: string): KnownBlock { - return { type: "context", elements: [{ type: "mrkdwn", text }] }; -} - -export class SlackFormatter implements ISlackFormatter { - formatOutgoing(message: OutgoingMessage): KnownBlock[] { - switch (message.type) { - case "text": { - const text = message.text ?? ""; - if (!text.trim()) return []; - const converted = markdownToMrkdwn(text); - return splitSafe(converted).map(chunk => section(chunk)); - } - - case "thought": - return [context(`💭 _${(message.text ?? "").slice(0, 500)}_`)]; - - case "tool_call": { - const name = (message as OutgoingMessage & { metadata?: { name?: string; input?: unknown } }).metadata?.name ?? "tool"; - const input = (message as OutgoingMessage & { metadata?: { input?: unknown } }).metadata?.input; - const inputStr = input ? `\n\`\`\`\n${JSON.stringify(input, null, 2).slice(0, 500)}\n\`\`\`` : ""; - return [context(`🔧 \`${name}\`${inputStr}`)]; - } - - case "tool_update": { - const name = (message as OutgoingMessage & { metadata?: { name?: string; status?: string } }).metadata?.name ?? "tool"; - const status = (message as OutgoingMessage & { metadata?: { status?: string } }).metadata?.status ?? "done"; - const icon = status === "error" ? "❌" : "✅"; - return [context(`${icon} \`${name}\` — ${status}`)]; - } - - case "plan": - return [ - { type: "divider" }, - section(`📋 *Plan*\n${message.text ?? ""}`), - ]; - - case "usage": { - const meta = (message as OutgoingMessage & { metadata?: { input_tokens?: number; output_tokens?: number; cost_usd?: number } }).metadata ?? {}; - const parts = [ - meta.input_tokens != null ? `in: ${meta.input_tokens}` : null, - meta.output_tokens != null ? `out: ${meta.output_tokens}` : null, - meta.cost_usd != null ? `$${Number(meta.cost_usd).toFixed(4)}` : null, - ].filter((p): p is string => p !== null); - return parts.length ? [context(`📊 ${parts.join(" · ")}`)] : []; - } - - case "session_end": - return this.formatSessionEnd(message.text); - - case "error": - return [section(`⚠️ *Error:* ${message.text ?? "Unknown error"}`)]; - - default: - return []; - } - } - - formatPermissionRequest(req: PermissionRequest): KnownBlock[] { - return [ - section(`🔐 *Permission Request*\n${req.description}`), - { - type: "actions", - block_id: `perm_${req.id}`, - elements: req.options.map(opt => ({ - type: "button" as const, - text: { type: "plain_text" as const, text: opt.label, emoji: true }, - value: `${req.id}:${opt.id}`, - action_id: `perm_action_${opt.id}_${req.id}`, - style: (opt.isAllow ? "primary" : "danger") as "primary" | "danger", - })), - } as KnownBlock, - ]; - } - - formatNotification(text: string): KnownBlock[] { - return [section(text)]; - } - - formatSessionEnd(reason?: string): KnownBlock[] { - return [ - { type: "divider" }, - context(`✅ Session ended${reason ? ` — ${reason}` : ""}`), - ]; - } -} diff --git a/src/plugins/slack/index.ts b/src/plugins/slack/index.ts deleted file mode 100644 index 63c567c7..00000000 --- a/src/plugins/slack/index.ts +++ /dev/null @@ -1,147 +0,0 @@ -import type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js' -import type { OpenACPCore } from '../../core/core.js' -import type { SlackChannelConfig } from './types.js' - -function createSlackPlugin(): OpenACPPlugin { - let adapter: { stop(): Promise } | null = null - - return { - name: '@openacp/slack', - version: '1.0.0', - description: 'Slack adapter with channels and threads', - essential: true, - pluginDependencies: { - '@openacp/security': '^1.0.0', - '@openacp/notifications': '^1.0.0', - }, - optionalPluginDependencies: { - '@openacp/speech': '^1.0.0', - }, - permissions: ['services:register', 'kernel:access', 'events:read'], - - async install(ctx: InstallContext) { - const { terminal, settings, legacyConfig } = ctx - - // Migrate from legacy config if present - if (legacyConfig) { - const ch = legacyConfig.channels as Record | undefined - const slackCfg = ch?.slack as Record | undefined - if (slackCfg?.botToken) { - await settings.setAll({ - botToken: slackCfg.botToken, - appToken: slackCfg.appToken, - signingSecret: slackCfg.signingSecret ?? '', - channelId: slackCfg.channelId ?? '', - }) - terminal.log.success('Slack settings migrated from legacy config') - return - } - } - - // Interactive setup via terminal - terminal.note( - '1. Create a Slack App at https://api.slack.com/apps\n' + - '2. Enable Socket Mode and get an App-Level Token\n' + - '3. Add Bot Token Scopes: chat:write, channels:history, groups:history, files:write\n' + - '4. Install app to workspace and copy the Bot User OAuth Token', - 'Slack Setup', - ) - - const botToken = await terminal.text({ - message: 'Bot User OAuth Token (xoxb-...):', - validate: (v) => (!v.trim() ? 'Token cannot be empty' : undefined), - }) - - const appToken = await terminal.text({ - message: 'App-Level Token (xapp-...):', - validate: (v) => (!v.trim() ? 'Token cannot be empty' : undefined), - }) - - const signingSecret = await terminal.text({ - message: 'Signing Secret:', - validate: (v) => (!v.trim() ? 'Signing secret cannot be empty' : undefined), - }) - - const channelId = await terminal.text({ - message: 'Default channel ID (optional):', - }) - - await settings.setAll({ - botToken: botToken.trim(), - appToken: appToken.trim(), - signingSecret: signingSecret.trim(), - channelId: channelId.trim() || '', - }) - terminal.log.success('Slack settings saved') - }, - - async configure(ctx: InstallContext) { - const { terminal, settings } = ctx - - const choice = await terminal.select({ - message: 'What to configure?', - options: [ - { value: 'botToken', label: 'Change bot token' }, - { value: 'appToken', label: 'Change app token' }, - { value: 'channelId', label: 'Change channel ID' }, - { value: 'done', label: 'Done' }, - ], - }) - - if (choice === 'botToken') { - const val = await terminal.text({ - message: 'New bot token:', - validate: (v) => (!v.trim() ? 'Token cannot be empty' : undefined), - }) - await settings.set('botToken', val.trim()) - terminal.log.success('Bot token updated') - } else if (choice === 'appToken') { - const val = await terminal.text({ - message: 'New app token:', - validate: (v) => (!v.trim() ? 'Token cannot be empty' : undefined), - }) - await settings.set('appToken', val.trim()) - terminal.log.success('App token updated') - } else if (choice === 'channelId') { - const val = await terminal.text({ message: 'New channel ID:' }) - await settings.set('channelId', val.trim()) - terminal.log.success('Channel ID updated') - } - }, - - async uninstall(ctx: InstallContext, opts: { purge: boolean }) { - if (opts.purge) { - await ctx.settings.clear() - ctx.terminal.log.success('Slack settings cleared') - } - }, - - async setup(ctx) { - const config = ctx.pluginConfig as Record - if (!config.botToken || !config.appToken) { - ctx.log.info('Slack disabled (missing botToken or appToken)') - return - } - - const { SlackAdapter } = await import('./adapter.js') - // config is a Record from pluginConfig; at runtime it - // contains all SlackChannelConfig fields populated from the migrated config. - adapter = new SlackAdapter(ctx.core as OpenACPCore, { - ...config, - enabled: true, - maxMessageLength: 3000, - } as unknown as SlackChannelConfig) - - ctx.registerService('adapter:slack', adapter) - ctx.log.info('Slack adapter registered') - }, - - async teardown() { - if (adapter) { - await adapter.stop() - } - }, - } -} - -export default createSlackPlugin() diff --git a/src/plugins/slack/permission-handler.ts b/src/plugins/slack/permission-handler.ts deleted file mode 100644 index 23756017..00000000 --- a/src/plugins/slack/permission-handler.ts +++ /dev/null @@ -1,68 +0,0 @@ -import type { App, BlockAction, ButtonAction } from "@slack/bolt"; -import type { ISlackSendQueue } from "./send-queue.js"; - -export type PermissionResponseCallback = (requestId: string, optionId: string) => void; - -export interface ISlackPermissionHandler { - register(app: App): void; - trackPendingMessage(requestId: string, channelId: string, messageTs: string): void; - cleanupSession(channelId: string): Promise; -} - -export class SlackPermissionHandler implements ISlackPermissionHandler { - private pendingMessages = new Map(); - - constructor( - private queue: ISlackSendQueue, - private onResponse: PermissionResponseCallback, - ) {} - - trackPendingMessage(requestId: string, channelId: string, messageTs: string): void { - this.pendingMessages.set(requestId, { channelId, messageTs }); - } - - async cleanupSession(channelId: string): Promise { - for (const [requestId, info] of this.pendingMessages) { - if (info.channelId !== channelId) continue; - await this.queue.enqueue("chat.update", { - channel: info.channelId, - ts: info.messageTs, - blocks: [], - }); - this.pendingMessages.delete(requestId); - } - } - - register(app: App): void { - // Match any action starting with "perm_action_" - app.action>( - /^perm_action_/, - async ({ ack, body, action }) => { - await ack(); - - const value: string = action.value ?? ""; - const colonIdx = value.indexOf(":"); - if (colonIdx === -1) return; - - const requestId = value.slice(0, colonIdx); - const optionId = value.slice(colonIdx + 1); - - this.onResponse(requestId, optionId); - - // Remove from pending tracking since the user has responded - this.pendingMessages.delete(requestId); - - // Update message to remove action buttons and show confirmation - const message = body.message; - if (message) { - await this.queue.enqueue("chat.update", { - channel: body.channel?.id ?? "", - ts: message.ts, - text: `✅ Permission response: *${optionId}*`, - blocks: [], - }); - } - } - ); - } -} diff --git a/src/plugins/slack/renderer.ts b/src/plugins/slack/renderer.ts deleted file mode 100644 index e251761c..00000000 --- a/src/plugins/slack/renderer.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { BaseRenderer } from "../../core/adapter-primitives/rendering/renderer.js"; -import type { - RenderedMessage, - RenderedPermission, -} from "../../core/adapter-primitives/rendering/renderer.js"; -import type { - OutgoingMessage, - PermissionRequest, - NotificationMessage, -} from "../../core/types.js"; -import type { DisplayVerbosity } from "../../core/adapter-primitives/format-types.js"; -import { SlackFormatter, markdownToMrkdwn } from "./formatter.js"; - -/** - * SlackRenderer — renders messages as Slack Block Kit structures. - * Delegates to the existing SlackFormatter for block generation, - * wrapping results into RenderedMessage with format: 'structured'. - */ -export class SlackRenderer extends BaseRenderer { - private formatter: SlackFormatter; - - constructor(formatter?: SlackFormatter) { - super(); - this.formatter = formatter ?? new SlackFormatter(); - } - - renderText(content: OutgoingMessage): RenderedMessage { - const blocks = this.formatter.formatOutgoing(content); - return { - body: content.text ?? "", - format: "structured", - components: blocks, - }; - } - - renderThought( - content: OutgoingMessage, - _verbosity: DisplayVerbosity, - ): RenderedMessage { - const blocks = this.formatter.formatOutgoing(content); - return { - body: content.text ?? "", - format: "structured", - components: blocks, - }; - } - - renderToolCall( - content: OutgoingMessage, - _verbosity: DisplayVerbosity, - ): RenderedMessage { - const blocks = this.formatter.formatOutgoing(content); - return { - body: content.text ?? "", - format: "structured", - components: blocks, - }; - } - - renderToolUpdate( - content: OutgoingMessage, - _verbosity: DisplayVerbosity, - ): RenderedMessage { - const blocks = this.formatter.formatOutgoing(content); - return { - body: content.text ?? "", - format: "structured", - components: blocks, - }; - } - - renderPlan(content: OutgoingMessage): RenderedMessage { - const blocks = this.formatter.formatOutgoing(content); - return { - body: content.text ?? "", - format: "structured", - components: blocks, - }; - } - - renderUsage( - content: OutgoingMessage, - _verbosity: DisplayVerbosity, - ): RenderedMessage { - const blocks = this.formatter.formatOutgoing(content); - return { - body: content.text ?? "", - format: "structured", - components: blocks, - }; - } - - renderError(content: OutgoingMessage): RenderedMessage { - const blocks = this.formatter.formatOutgoing(content); - return { - body: content.text ?? "", - format: "structured", - components: blocks, - }; - } - - renderSessionEnd(content: OutgoingMessage): RenderedMessage { - const blocks = this.formatter.formatSessionEnd(content.text); - return { - body: content.text ?? "Session ended", - format: "structured", - components: blocks, - }; - } - - renderPermission(request: PermissionRequest): RenderedPermission { - const blocks = this.formatter.formatPermissionRequest(request); - return { - body: request.description, - format: "structured", - components: blocks, - actions: request.options.map((o) => ({ - id: o.id, - label: o.label, - isAllow: o.isAllow, - })), - }; - } - - renderNotification(notification: NotificationMessage): RenderedMessage { - const emoji: Record = { - completed: "✅", - error: "❌", - permission: "🔐", - input_required: "💬", - budget_warning: "⚠️", - }; - const icon = emoji[notification.type] || "ℹ️"; - const text = `${icon} *${notification.sessionName ?? "Session"}*\n${notification.summary}`; - const blocks = this.formatter.formatNotification(text); - return { - body: text, - format: "structured", - components: blocks, - }; - } - - renderSystemMessage(content: OutgoingMessage): RenderedMessage { - const mrkdwn = markdownToMrkdwn(content.text ?? ""); - return { - body: mrkdwn, - format: "structured", - components: [ - { - type: "section" as const, - text: { type: "mrkdwn" as const, text: mrkdwn }, - }, - ], - }; - } - - renderModeChange(content: OutgoingMessage): RenderedMessage { - const modeId = (content.metadata as Record)?.modeId ?? ""; - const text = `🔄 *Mode:* ${modeId}`; - return { - body: text, - format: "structured", - components: [ - { - type: "context" as const, - elements: [{ type: "mrkdwn" as const, text }], - }, - ], - }; - } - - renderConfigUpdate(): RenderedMessage { - const text = "⚙️ *Config updated*"; - return { - body: text, - format: "structured", - components: [ - { - type: "context" as const, - elements: [{ type: "mrkdwn" as const, text }], - }, - ], - }; - } - - renderModelUpdate(content: OutgoingMessage): RenderedMessage { - const modelId = - (content.metadata as Record)?.modelId ?? ""; - const text = `🤖 *Model:* ${modelId}`; - return { - body: text, - format: "structured", - components: [ - { - type: "context" as const, - elements: [{ type: "mrkdwn" as const, text }], - }, - ], - }; - } -} diff --git a/src/plugins/slack/send-queue.ts b/src/plugins/slack/send-queue.ts deleted file mode 100644 index d909d40f..00000000 --- a/src/plugins/slack/send-queue.ts +++ /dev/null @@ -1,51 +0,0 @@ -import PQueue from "p-queue"; -import type { WebClient } from "@slack/web-api"; - -export type SlackMethod = - | "chat.postMessage" - | "chat.update" - | "conversations.create" - | "conversations.rename" - | "conversations.archive" - | "conversations.invite" - | "conversations.join" - | "conversations.unarchive" - | "conversations.info"; - -// Requests per minute per method (Slack Tier definitions) -const METHOD_RPM: Record = { - "chat.postMessage": 50, // Tier 3 - "chat.update": 50, // Tier 3 - "conversations.create": 20, // Tier 2 - "conversations.rename": 20, // Tier 2 - "conversations.archive": 20, // Tier 2 - "conversations.invite": 20, // Tier 2 - "conversations.join": 20, // Tier 2 - "conversations.unarchive": 20, // Tier 2 - "conversations.info": 50, // Tier 3 -}; - -export interface ISlackSendQueue { - enqueue(method: SlackMethod, params: Record): Promise; -} - -export class SlackSendQueue implements ISlackSendQueue { - private queues = new Map(); - - constructor(private client: WebClient) { - for (const [method, rpm] of Object.entries(METHOD_RPM) as [SlackMethod, number][]) { - // Spread requests evenly across the minute - this.queues.set(method, new PQueue({ - interval: Math.ceil(60_000 / rpm), - intervalCap: 1, - carryoverConcurrencyCount: true, - })); - } - } - - async enqueue(method: SlackMethod, params: Record): Promise { - const queue = this.queues.get(method); - if (!queue) throw new Error(`Unknown Slack method: ${method}`); - return queue.add(() => this.client.apiCall(method, params) as Promise); - } -} diff --git a/src/plugins/slack/slug.ts b/src/plugins/slack/slug.ts deleted file mode 100644 index cd3820de..00000000 --- a/src/plugins/slack/slug.ts +++ /dev/null @@ -1,26 +0,0 @@ -// src/adapters/slack/slug.ts -import { customAlphabet } from "nanoid"; - -const nanoidAlpha = customAlphabet("abcdefghijklmnopqrstuvwxyz0123456789", 4); - -/** - * Convert a human-readable session name to a valid Slack channel name. - * Rules: lowercase, ≤80 chars, only [a-z0-9-], unique suffix appended. - * - * Examples: - * "Fix authentication bug" → "openacp-fix-authentication-bug-a3k9" - * "New Session" → "openacp-new-session-x7p2" - * "Implement OAuth 2.0 & JWT refresh" → "openacp-implement-oauth-20-jwt-refresh-b8qr" - */ -export function toSlug(name: string, prefix = "openacp"): string { - const base = name - .toLowerCase() - .replace(/[^a-z0-9\s-]/g, "") // strip special chars - .trim() - .replace(/\s+/g, "-") // spaces → dashes - .replace(/-+/g, "-") // collapse consecutive dashes - .slice(0, 60); // leave room for prefix and suffix - - const suffix = nanoidAlpha(); - return `${prefix}-${base}-${suffix}`.replace(/-+/g, "-"); -} diff --git a/src/plugins/slack/text-buffer.ts b/src/plugins/slack/text-buffer.ts deleted file mode 100644 index 82be23aa..00000000 --- a/src/plugins/slack/text-buffer.ts +++ /dev/null @@ -1,102 +0,0 @@ -// src/adapters/slack/text-buffer.ts -// Buffers streamed text chunks per session and flushes as a single Slack message. -// This prevents the "many tiny messages" problem from streaming AI responses. - -import type { ISlackSendQueue } from "./send-queue.js"; -import { markdownToMrkdwn } from "./formatter.js"; -import { splitSafe } from "./utils.js"; -import { createChildLogger } from "../../core/utils/log.js"; - -const log = createChildLogger({ module: "slack-text-buffer" }); - -const FLUSH_IDLE_MS = 2000; // flush after 2s of no new chunks - -export class SlackTextBuffer { - private buffer = ""; - private timer: ReturnType | undefined; - private flushPromise: Promise | undefined; - private lastMessageTs: string | undefined; - private lastPostedText: string | undefined; - - constructor( - private channelId: string, - private sessionId: string, - private queue: ISlackSendQueue, - ) {} - - append(text: string): void { - if (!text) return; - this.buffer += text; - this.resetTimer(); - } - - private resetTimer(): void { - if (this.timer) clearTimeout(this.timer); - this.timer = setTimeout(() => { - this.timer = undefined; - this.flush().catch((err) => log.error({ err, sessionId: this.sessionId }, "Text buffer flush error")); - }, FLUSH_IDLE_MS); - } - - async flush(): Promise { - if (this.flushPromise) return this.flushPromise; - const text = this.buffer.trim(); - if (!text) return; - this.buffer = ""; - if (this.timer) { clearTimeout(this.timer); this.timer = undefined; } - - this.flushPromise = (async () => { - try { - const converted = markdownToMrkdwn(text); - const chunks = splitSafe(converted); - for (const chunk of chunks) { - if (!chunk.trim()) continue; - const result = await this.queue.enqueue("chat.postMessage", { - channel: this.channelId, - text: chunk, - blocks: [{ type: "section", text: { type: "mrkdwn", text: chunk } }], - }); - // Track last posted message for potential TTS block editing - this.lastMessageTs = (result as { ts?: string } | undefined)?.ts; - this.lastPostedText = chunk; - } - } finally { - this.flushPromise = undefined; - // Re-flush if content arrived while we were flushing - if (this.buffer.trim()) { - await this.flush(); - } - } - })(); - - return this.flushPromise; - } - - destroy(): void { - if (this.timer) { clearTimeout(this.timer); this.timer = undefined; } - this.buffer = ""; - } - - /** Remove [TTS]...[/TTS] blocks — from buffer if unflushed, or edit posted message */ - async stripTtsBlock(): Promise { - // Case 1: TTS block still in unflushed buffer - if (/\[TTS\][\s\S]*?\[\/TTS\]/.test(this.buffer)) { - this.buffer = this.buffer.replace(/\[TTS\][\s\S]*?\[\/TTS\]/g, "").replace(/\s{2,}/g, " ").trim(); - return; - } - - // Case 2: Already flushed — edit the posted message via chat.update - if (this.lastMessageTs && this.lastPostedText && /\[TTS\][\s\S]*?\[\/TTS\]/.test(this.lastPostedText)) { - const cleaned = this.lastPostedText.replace(/\[TTS\][\s\S]*?\[\/TTS\]/g, "").replace(/\s{2,}/g, " ").trim(); - if (cleaned) { - await this.queue.enqueue("chat.update", { - channel: this.channelId, - ts: this.lastMessageTs, - text: cleaned, - blocks: [{ type: "section", text: { type: "mrkdwn", text: cleaned } }], - }); - } - this.lastPostedText = cleaned; - } - } -} diff --git a/src/plugins/slack/types.ts b/src/plugins/slack/types.ts deleted file mode 100644 index 1ba18de8..00000000 --- a/src/plugins/slack/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -// src/adapters/slack/types.ts -export type { SlackChannelConfig } from "../../core/config/config.js"; - -// Per-session metadata stored in SessionRecord.platform -export interface SlackSessionMeta { - channelId: string; // Slack channel ID for this session (C...) - channelSlug: string; // e.g. "openacp-fix-auth-bug-a3k9" -} - -/** Minimal file metadata extracted from Slack message events (subtype: file_share) */ -export interface SlackFileInfo { - id: string; - name: string; - mimetype: string; - size: number; - url_private: string; -} diff --git a/src/plugins/slack/utils.ts b/src/plugins/slack/utils.ts deleted file mode 100644 index 57fe95ff..00000000 --- a/src/plugins/slack/utils.ts +++ /dev/null @@ -1,33 +0,0 @@ -// src/adapters/slack/utils.ts -// Shared utilities for Slack adapter modules. - -import type { SlackFileInfo } from "./types.js"; - -/** Detect Slack audio clips — MIME type or filename pattern */ -export function isAudioClip(file: SlackFileInfo): boolean { - return (file.mimetype === "video/mp4" && file.name?.startsWith("audio_message")) || - file.mimetype?.startsWith("audio/"); -} - -const SECTION_LIMIT = 3000; - -/** - * Split text at nearest newline boundary before `limit`. - * Does NOT track code fence state — a triple-backtick block straddling - * the boundary will be split mid-block. - * Used by SlackFormatter and SlackTextBuffer to avoid exceeding Slack's - * 3000-char section limit. - */ -export function splitSafe(text: string, limit = SECTION_LIMIT): string[] { - if (text.length <= limit) return [text]; - const chunks: string[] = []; - let remaining = text; - while (remaining.length > 0) { - if (remaining.length <= limit) { chunks.push(remaining); break; } - let cut = remaining.lastIndexOf("\n", limit); - if (cut <= 0) cut = limit; - chunks.push(remaining.slice(0, cut)); - remaining = remaining.slice(cut).trimStart(); - } - return chunks; -} From 8be211036cf723c03d219355c850e3800b1a84f5 Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 16:42:35 +0700 Subject: [PATCH 04/10] feat(wizard): discover community adapter plugins from registry The setup wizard now fetches verified adapter plugins from the plugin registry and shows them alongside built-in Telegram/Discord options. When a community adapter is selected, it auto-installs from npm and runs the plugin's install() hook. Gracefully falls back to built-in options only when the registry is unreachable. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/core/setup/setup-channels.ts | 45 +++++----- src/core/setup/types.ts | 7 ++ src/core/setup/wizard.ts | 146 +++++++++++++++++++------------ 3 files changed, 119 insertions(+), 79 deletions(-) diff --git a/src/core/setup/setup-channels.ts b/src/core/setup/setup-channels.ts index ae5ddb15..968b1cec 100644 --- a/src/core/setup/setup-channels.ts +++ b/src/core/setup/setup-channels.ts @@ -57,33 +57,32 @@ async function promptConfiguredAction(label: string): Promise { - const pluginImports: Record Promise> = { - telegram: () => import('../../plugins/telegram/index.js'), - discord: async () => { - const pkg = '@openacp/adapter-discord'; - try { - return await import(/* webpackIgnore: true */ pkg); - } catch { - throw new Error( - `${pkg} is not installed. Run: openacp plugin add ${pkg}`, - ); - } - }, +async function configureViaPlugin(channelId: string): Promise { + const pluginMap: Record = { + telegram: { importPath: '../../plugins/telegram/index.js', name: '@openacp/telegram' }, }; - const importer = pluginImports[channelId]; - if (!importer) return; - - const { SettingsManager } = await import('../plugin/settings-manager.js'); - const { createInstallContext } = await import('../plugin/install-context.js'); - const basePath = path.join(os.homedir(), '.openacp', 'plugins'); - const settingsManager = new SettingsManager(basePath); - - const pluginModule = await importer(); - const plugin = pluginModule.default; + const pluginInfo = pluginMap[channelId]; + + let plugin: any; + if (pluginInfo) { + const pluginModule = await import(pluginInfo.importPath); + plugin = pluginModule.default; + } else { + // Try dynamic import for community plugins (npm package name) + try { + const pluginModule = await import(channelId); + plugin = pluginModule.default; + } catch { + return; + } + } if (plugin?.configure) { + const { SettingsManager } = await import('../plugin/settings-manager.js'); + const { createInstallContext } = await import('../plugin/install-context.js'); + const basePath = path.join(os.homedir(), '.openacp', 'plugins'); + const settingsManager = new SettingsManager(basePath); const ctx = createInstallContext({ pluginName: plugin.name, settingsManager, diff --git a/src/core/setup/types.ts b/src/core/setup/types.ts index 3dc03775..b4130e01 100644 --- a/src/core/setup/types.ts +++ b/src/core/setup/types.ts @@ -34,3 +34,10 @@ export const CHANNEL_META: Record = { discord: { label: "Discord", method: "Bot API" }, }; +export interface CommunityAdapterOption { + name: string // npm package name, e.g. "@openacp/adapter-slack" + displayName: string // e.g. "Slack Adapter" + icon: string // e.g. "💬" + verified: boolean +} + diff --git a/src/core/setup/wizard.ts b/src/core/setup/wizard.ts index d4156ab4..11cee178 100644 --- a/src/core/setup/wizard.ts +++ b/src/core/setup/wizard.ts @@ -1,17 +1,40 @@ +import * as os from "node:os"; +import * as path from "node:path"; import * as clack from "@clack/prompts"; import type { Config, ConfigManager } from "../config/config.js"; import type { ChannelId } from "./types.js"; import type { OnboardSection } from "./types.js"; import { ONBOARD_SECTION_OPTIONS } from "./types.js"; +import type { CommunityAdapterOption } from "./types.js"; import { guardCancel, ok, fail, printStartBanner, summarizeConfig } from "./helpers.js"; import { setupAgents } from "./setup-agents.js"; import { setupWorkspace } from "./setup-workspace.js"; import { setupRunMode } from "./setup-run-mode.js"; import { setupIntegrations } from "./setup-integrations.js"; import { configureChannels } from "./setup-channels.js"; +import { RegistryClient } from "../plugin/registry-client.js"; import type { SettingsManager } from "../plugin/settings-manager.js"; import type { PluginRegistry } from "../plugin/plugin-registry.js"; +// ─── Registry discovery ─── + +async function fetchCommunityAdapters(): Promise { + try { + const client = new RegistryClient() + const registry = await client.getRegistry() + return registry.plugins + .filter(p => p.category === 'adapter' && p.verified) + .map(p => ({ + name: p.npm, + displayName: p.displayName ?? p.name, + icon: p.icon, + verified: p.verified, + })) + } catch { + return [] + } +} + // ─── First-run setup ─── export async function runSetup( @@ -29,18 +52,31 @@ export async function runSetup( return false; } + const communityAdapters = await fetchCommunityAdapters() + + const builtInOptions = [ + { label: 'Telegram', value: 'telegram' }, + ] + + const communityOptions = communityAdapters.map(a => ({ + label: `${a.icon} ${a.displayName}${a.verified ? ' (verified)' : ''}`, + value: `community:${a.name}`, + })) + // Ask user which channels to set up const channelChoices = guardCancel( await clack.multiselect({ message: 'Which channels do you want to set up?', options: [ - { value: 'telegram' as const, label: 'Telegram', hint: 'built-in' }, - { value: 'discord' as const, label: 'Discord', hint: 'will install @openacp/adapter-discord' }, + ...builtInOptions.map(o => ({ value: o.value, label: o.label, hint: 'built-in' })), + ...(communityOptions.length > 0 + ? communityOptions.map(o => ({ value: o.value, label: o.label, hint: 'from plugin registry' })) + : []), ], required: true, initialValues: ['telegram' as const], }), - ) as ChannelId[]; + ) as string[]; // Calculate total steps dynamically: channel(s) + workspace + run mode const channelSteps = channelChoices.length; @@ -71,8 +107,57 @@ export async function runSetup( }); } - if (channelId === 'discord') { - await installAndSetupDiscord(settingsManager, pluginRegistry); + // Handle community plugin selections + if (channelId.startsWith('community:')) { + const npmPackage = channelId.slice('community:'.length); + const { execSync } = await import('node:child_process'); + const pluginsDir = path.join(os.homedir(), '.openacp', 'plugins'); + const nodeModulesDir = path.join(pluginsDir, 'node_modules'); + + // Install from npm + try { + execSync(`npm install ${npmPackage} --prefix "${pluginsDir}" --save`, { + stdio: 'inherit', + timeout: 60000, + }); + } catch { + console.log(fail(`Failed to install ${npmPackage}.`)); + return false; + } + + // Load and run install hook + try { + const { readFileSync } = await import('node:fs'); + const installedPkgPath = path.join(nodeModulesDir, npmPackage, 'package.json'); + const installedPkg = JSON.parse(readFileSync(installedPkgPath, 'utf-8')); + const pluginModule = await import(path.join(nodeModulesDir, npmPackage, installedPkg.main ?? 'dist/index.js')); + const plugin = pluginModule.default; + + if (plugin?.install) { + const installCtx = createInstallContext({ + pluginName: plugin.name ?? npmPackage, + settingsManager, + basePath: settingsManager.getBasePath(), + }); + await plugin.install(installCtx); + } + + pluginRegistry.register(plugin?.name ?? npmPackage, { + version: installedPkg.version, + source: 'npm', + enabled: true, + settingsPath: settingsManager.getSettingsPath(plugin?.name ?? npmPackage), + description: plugin?.description ?? installedPkg.description, + }); + } catch { + // Plugin installed via npm but failed to load — still register + pluginRegistry.register(npmPackage, { + version: 'unknown', + source: 'npm', + enabled: true, + settingsPath: settingsManager.getSettingsPath(npmPackage), + }); + } } } @@ -173,57 +258,6 @@ export async function runSetup( } } -/** - * Install @openacp/adapter-discord from npm if needed, then run its install() hook. - */ -async function installAndSetupDiscord( - settingsManager: SettingsManager, - pluginRegistry: PluginRegistry, -): Promise { - const packageName = '@openacp/adapter-discord'; - - // Try to import first — if not installed, install it - let discordPlugin: any; - const pluginsDir = settingsManager.getBasePath(); - try { - const { importFromDir } = await import('../plugin/plugin-installer.js'); - const mod = await importFromDir(packageName, pluginsDir); - discordPlugin = mod.default; - } catch { - const spinner = clack.spinner(); - spinner.start(`Installing ${packageName}...`); - try { - const { installNpmPlugin } = await import('../plugin/plugin-installer.js'); - const mod = await installNpmPlugin(packageName, pluginsDir); - discordPlugin = mod.default; - spinner.stop(ok(`${packageName} installed`)); - } catch (installErr) { - spinner.stop(fail(`Failed to install ${packageName}: ${(installErr as Error).message}`)); - console.log(fail('You can install it later with: openacp plugin add @openacp/adapter-discord')); - return; - } - } - - const { createInstallContext } = await import('../plugin/install-context.js'); - const ctx = createInstallContext({ - pluginName: discordPlugin.name, - settingsManager, - basePath: settingsManager.getBasePath(), - }); - - if (discordPlugin.install) { - await discordPlugin.install(ctx); - } - - pluginRegistry.register(discordPlugin.name, { - version: discordPlugin.version, - source: 'npm', - enabled: true, - settingsPath: settingsManager.getSettingsPath(discordPlugin.name), - description: discordPlugin.description, - }); -} - /** * Register all built-in plugins that haven't been registered yet. * Called after first-run setup to populate the registry with defaults. From af94b052e288bb32990b76dca5ec9209c1b88ce4 Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 17:02:29 +0700 Subject: [PATCH 05/10] fix(config): remove strict Slack schema to allow old configs with channels.slack Old configs with `channels.slack` crashed on startup because the SlackChannelConfigSchema used `z.literal("slack")` for the adapter field. Since Slack was extracted as a plugin, remove the dedicated schema and let `channels.slack` fall through to BaseChannelSchema (which uses passthrough), ensuring backward compatibility with existing user configs. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/core/config/config.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/core/config/config.ts b/src/core/config/config.ts index 8505a2f3..03a279ad 100644 --- a/src/core/config/config.ts +++ b/src/core/config/config.ts @@ -64,20 +64,6 @@ const TunnelSchema = z export type TunnelConfig = z.infer; -const SlackChannelConfigSchema = z.object({ - enabled: z.boolean().default(false), - adapter: z.literal("slack").optional(), - botToken: z.string().optional(), // xoxb-... - appToken: z.string().optional(), // xapp-... (Socket Mode) - signingSecret: z.string().optional(), - notificationChannelId: z.string().optional(), - allowedUserIds: z.array(z.string()).default([]), - channelPrefix: z.string().default("openacp"), - autoCreateSession: z.boolean().default(true), - startupChannelId: z.string().optional(), -}); - -export type SlackChannelConfig = z.infer; const UsageSchema = z .object({ @@ -118,9 +104,7 @@ const SpeechSchema = z export const ConfigSchema = z.object({ channels: z - .object({ - slack: SlackChannelConfigSchema.optional(), - }) + .object({}) .catchall(BaseChannelSchema), agents: z.record(z.string(), AgentSchema).optional().default({}), defaultAgent: z.string(), From 8c85d5b2d1b95b9d199ea02170c30bc1d0408353 Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 17:09:18 +0700 Subject: [PATCH 06/10] feat: load community plugins from registry on startup Community plugins installed via `plugin add` are now dynamically loaded and booted after built-in plugins. Supports both npm and local sources, with per-plugin error handling so a failing community plugin won't crash the server. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/main.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/main.ts b/src/main.ts index 5db8ea41..a89a514e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -127,6 +127,52 @@ export async function startServer(opts?: StartServerOptions) { // Boot all built-in plugins in dependency order await core.lifecycleManager.boot(corePlugins) + // Load community plugins from registry (npm and local sources) + try { + const communityPlugins: import('./core/plugin/types.js').OpenACPPlugin[] = [] + const npmPlugins = pluginRegistry.listBySource('npm') + const localPlugins = pluginRegistry.listBySource('local') + const allCommunityEntries = new Map([...npmPlugins, ...localPlugins]) + + for (const [name, entry] of allCommunityEntries) { + if (!entry.enabled) continue + + try { + let modulePath: string + + if (entry.source === 'local') { + // Local plugin: name is the filesystem path or resolves directly + modulePath = name.startsWith('/') || name.startsWith('.') + ? path.resolve(name) + : path.join(OPENACP_DIR, 'plugins', 'node_modules', name) + } else { + // npm plugin: installed under ~/.openacp/plugins/node_modules/ + modulePath = path.join(OPENACP_DIR, 'plugins', 'node_modules', name) + } + + log.debug({ plugin: name, modulePath }, 'Loading community plugin') + const mod = await import(modulePath) + const plugin = mod.default + + if (!plugin || !plugin.name || !plugin.setup) { + log.warn({ plugin: name }, 'Community plugin has invalid exports (missing name or setup), skipping') + continue + } + + communityPlugins.push(plugin) + } catch (err) { + log.warn({ err, plugin: name }, 'Failed to load community plugin, skipping') + } + } + + if (communityPlugins.length > 0) { + log.debug({ plugins: communityPlugins.map(p => p.name) }, 'Booting community plugins') + await core.lifecycleManager.boot(communityPlugins) + } + } catch (err) { + log.warn({ err }, 'Community plugin loading failed') + } + // Load dev plugin if running in dev mode if (opts?.devPluginPath) { const { DevPluginLoader } = await import('./core/plugin/dev-loader.js') From 68ca2e4c783bfbd2f2c8c01126ee2daae90f81eb Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 18:16:27 +0700 Subject: [PATCH 07/10] fix: community plugin loading, settings path, and onboard init - Load community plugins from registry on startup (main.ts) - Fix path resolution for local and npm plugins (absolute paths, ESM entry point) - Scan node_modules when plugin name differs from npm package name - Align settings basePath to ~/.openacp/plugins/data/ across all CLI commands - Initialize plugin system in onboard command - Add debug logging for settings resolution in lifecycle-manager Co-Authored-By: Claude Opus 4.6 (1M context) --- src/cli/commands/onboard.ts | 10 ++++++ src/cli/commands/plugins.ts | 12 ++++--- src/core/plugin/lifecycle-manager.ts | 3 ++ src/main.ts | 49 ++++++++++++++++++++++++---- 4 files changed, 62 insertions(+), 12 deletions(-) diff --git a/src/cli/commands/onboard.ts b/src/cli/commands/onboard.ts index 704ab13c..7432f1ec 100644 --- a/src/cli/commands/onboard.ts +++ b/src/cli/commands/onboard.ts @@ -6,8 +6,18 @@ const PLUGINS_DATA_DIR = path.join(OPENACP_DIR, 'plugins', 'data') const REGISTRY_PATH = path.join(OPENACP_DIR, 'plugins.json') export async function cmdOnboard(): Promise { + const os = await import('node:os') + const path = await import('node:path') const { ConfigManager } = await import('../../core/config/config.js') + const { SettingsManager } = await import('../../core/plugin/settings-manager.js') + const { PluginRegistry } = await import('../../core/plugin/plugin-registry.js') + const cm = new ConfigManager() + const pluginsDataDir = path.join(os.homedir(), '.openacp', 'plugins', 'data') + const registryPath = path.join(os.homedir(), '.openacp', 'plugins.json') + const settingsManager = new SettingsManager(pluginsDataDir) + const pluginRegistry = new PluginRegistry(registryPath) + await pluginRegistry.load() if (await cm.exists()) { const { runReconfigure } = await import('../../core/setup/index.js') diff --git a/src/cli/commands/plugins.ts b/src/cli/commands/plugins.ts index 8748c067..ed7fdab9 100644 --- a/src/cli/commands/plugins.ts +++ b/src/cli/commands/plugins.ts @@ -184,7 +184,7 @@ async function configurePlugin(name: string): Promise { process.exit(1) } - const basePath = path.join(os.homedir(), '.openacp', 'plugins') + const basePath = path.join(os.homedir(), '.openacp', 'plugins', 'data') const settingsManager = new SettingsManager(basePath) const ctx = createInstallContext({ pluginName: name, settingsManager, basePath }) @@ -261,7 +261,7 @@ async function installPlugin(input: string): Promise { const { corePlugins } = await import('../../plugins/core-plugins.js') const builtinPlugin = corePlugins.find(p => p.name === pkgName) - const basePath = path.join(os.homedir(), '.openacp', 'plugins') + const basePath = path.join(os.homedir(), '.openacp', 'plugins', 'data') const settingsManager = new SettingsManager(basePath) const registryPath = path.join(os.homedir(), '.openacp', 'plugins.json') const pluginRegistry = new PluginRegistry(registryPath) @@ -302,8 +302,10 @@ async function installPlugin(input: string): Promise { // Read installed plugin's package.json for compatibility check const cliVersion = getCurrentVersion() + const isLocalPath = pkgName.startsWith('/') || pkgName.startsWith('.') try { - const installedPkgPath = path.join(nodeModulesDir, pkgName, 'package.json') + const pluginRoot = isLocalPath ? path.resolve(pkgName) : path.join(nodeModulesDir, pkgName) + const installedPkgPath = path.join(pluginRoot, 'package.json') const { readFileSync } = await import('node:fs') const installedPkg = JSON.parse(readFileSync(installedPkgPath, 'utf-8')) @@ -318,7 +320,7 @@ async function installPlugin(input: string): Promise { } // Try to load and run install hook - const pluginModule = await import(path.join(nodeModulesDir, pkgName, installedPkg.main ?? 'dist/index.js')) + const pluginModule = await import(path.join(pluginRoot, installedPkg.main ?? 'dist/index.js')) const plugin = pluginModule.default if (plugin?.install) { @@ -377,7 +379,7 @@ async function uninstallPlugin(name: string, purge: boolean): Promise { if (plugin?.uninstall) { const { SettingsManager } = await import('../../core/plugin/settings-manager.js') const { createInstallContext } = await import('../../core/plugin/install-context.js') - const basePath = path.join(os.homedir(), '.openacp', 'plugins') + const basePath = path.join(os.homedir(), '.openacp', 'plugins', 'data') const settingsManager = new SettingsManager(basePath) const ctx = createInstallContext({ pluginName: name, settingsManager, basePath }) await plugin.uninstall(ctx, { purge }) diff --git a/src/core/plugin/lifecycle-manager.ts b/src/core/plugin/lifecycle-manager.ts index c5e3fffa..8b003bea 100644 --- a/src/core/plugin/lifecycle-manager.ts +++ b/src/core/plugin/lifecycle-manager.ts @@ -205,11 +205,14 @@ export class LifecycleManager { let pluginConfig: Record if (this.settingsManager) { pluginConfig = await this.settingsManager.loadSettings(plugin.name) + const settingsPath = this.settingsManager.getSettingsPath(plugin.name) + this.getPluginLogger(plugin.name).debug(`Settings loaded from ${settingsPath}: ${Object.keys(pluginConfig).length} keys`) if (Object.keys(pluginConfig).length === 0) { pluginConfig = resolvePluginConfig(plugin.name, this.config) } } else { pluginConfig = resolvePluginConfig(plugin.name, this.config) + this.getPluginLogger(plugin.name).debug('No settingsManager, using legacy config') } // Create context for this plugin diff --git a/src/main.ts b/src/main.ts index a89a514e..8cfc07ac 100644 --- a/src/main.ts +++ b/src/main.ts @@ -140,14 +140,49 @@ export async function startServer(opts?: StartServerOptions) { try { let modulePath: string - if (entry.source === 'local') { - // Local plugin: name is the filesystem path or resolves directly - modulePath = name.startsWith('/') || name.startsWith('.') - ? path.resolve(name) - : path.join(OPENACP_DIR, 'plugins', 'node_modules', name) + if (name.startsWith('/') || name.startsWith('.')) { + // Absolute or relative path (local install via `plugin add /path/to/plugin`) + const resolved = path.resolve(name) + const pkgPath = path.join(resolved, 'package.json') + const pkg = JSON.parse(await fs.promises.readFile(pkgPath, 'utf-8')) + modulePath = path.join(resolved, pkg.main || 'dist/index.js') } else { - // npm plugin: installed under ~/.openacp/plugins/node_modules/ - modulePath = path.join(OPENACP_DIR, 'plugins', 'node_modules', name) + // npm package: try direct name first, then scan node_modules for matching plugin name + const nodeModulesDir = path.join(OPENACP_DIR, 'plugins', 'node_modules') + let pkgDir = path.join(nodeModulesDir, name) + + if (!fs.existsSync(path.join(pkgDir, 'package.json'))) { + // Plugin name doesn't match npm package name — scan installed packages + let found = false + const scopes = fs.readdirSync(nodeModulesDir).filter(d => d.startsWith('@')) + for (const scope of scopes) { + const scopeDir = path.join(nodeModulesDir, scope) + const pkgs = fs.readdirSync(scopeDir) + for (const pkg of pkgs) { + const candidateDir = path.join(scopeDir, pkg) + const candidatePkgPath = path.join(candidateDir, 'package.json') + if (fs.existsSync(candidatePkgPath)) { + try { + const candidatePkg = JSON.parse(fs.readFileSync(candidatePkgPath, 'utf-8')) + const mainPath = path.join(candidateDir, candidatePkg.main || 'dist/index.js') + if (fs.existsSync(mainPath)) { + const testMod = await import(mainPath) + if (testMod.default?.name === name) { + pkgDir = candidateDir + found = true + break + } + } + } catch { /* skip */ } + } + } + if (found) break + } + } + + const pkgJsonPath = path.join(pkgDir, 'package.json') + const pkg = JSON.parse(await fs.promises.readFile(pkgJsonPath, 'utf-8')) + modulePath = path.join(pkgDir, pkg.main || 'dist/index.js') } log.debug({ plugin: name, modulePath }, 'Loading community plugin') From 6698661812218cc9719f50f06bde26bf9cf610f9 Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 19:55:16 +0700 Subject: [PATCH 08/10] fix: address code review feedback (security, consistency, cleanup) - Replace execSync with execFileSync to prevent command injection - Filter plugin scan by keywords/scope before importing to avoid executing arbitrary code - Fix basePath inconsistency in setup-channels.ts (use plugins/data) - Remove dangling Slack references from admin menu and ADAPTER_SCOPES - Log warning instead of silently swallowing errors in configureViaPlugin - Remove duplicate imports in onboard.ts Co-Authored-By: Claude Opus 4.6 (1M context) --- src/cli/commands/onboard.ts | 14 ++------------ src/cli/commands/plugins.ts | 4 ++-- src/core/command-registry.ts | 4 ++-- src/core/commands/admin.ts | 1 - src/core/setup/setup-channels.ts | 5 +++-- src/core/setup/wizard.ts | 5 +++-- src/main.ts | 6 ++++++ 7 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/cli/commands/onboard.ts b/src/cli/commands/onboard.ts index 7432f1ec..096ed6d0 100644 --- a/src/cli/commands/onboard.ts +++ b/src/cli/commands/onboard.ts @@ -6,29 +6,19 @@ const PLUGINS_DATA_DIR = path.join(OPENACP_DIR, 'plugins', 'data') const REGISTRY_PATH = path.join(OPENACP_DIR, 'plugins.json') export async function cmdOnboard(): Promise { - const os = await import('node:os') - const path = await import('node:path') const { ConfigManager } = await import('../../core/config/config.js') const { SettingsManager } = await import('../../core/plugin/settings-manager.js') const { PluginRegistry } = await import('../../core/plugin/plugin-registry.js') const cm = new ConfigManager() - const pluginsDataDir = path.join(os.homedir(), '.openacp', 'plugins', 'data') - const registryPath = path.join(os.homedir(), '.openacp', 'plugins.json') - const settingsManager = new SettingsManager(pluginsDataDir) - const pluginRegistry = new PluginRegistry(registryPath) + const settingsManager = new SettingsManager(PLUGINS_DATA_DIR) + const pluginRegistry = new PluginRegistry(REGISTRY_PATH) await pluginRegistry.load() if (await cm.exists()) { const { runReconfigure } = await import('../../core/setup/index.js') await runReconfigure(cm) } else { - const { SettingsManager } = await import('../../core/plugin/settings-manager.js') - const { PluginRegistry } = await import('../../core/plugin/plugin-registry.js') - const settingsManager = new SettingsManager(PLUGINS_DATA_DIR) - const pluginRegistry = new PluginRegistry(REGISTRY_PATH) - await pluginRegistry.load() - const { runSetup } = await import('../../core/setup/index.js') await runSetup(cm, { skipRunMode: true, settingsManager, pluginRegistry }) } diff --git a/src/cli/commands/plugins.ts b/src/cli/commands/plugins.ts index ed7fdab9..e57cf620 100644 --- a/src/cli/commands/plugins.ts +++ b/src/cli/commands/plugins.ts @@ -200,7 +200,7 @@ async function configurePlugin(name: string): Promise { async function installPlugin(input: string): Promise { const os = await import('node:os') const path = await import('node:path') - const { execSync } = await import('node:child_process') + const { execFileSync } = await import('node:child_process') const { getCurrentVersion } = await import('../version.js') const { SettingsManager } = await import('../../core/plugin/settings-manager.js') const { createInstallContext } = await import('../../core/plugin/install-context.js') @@ -291,7 +291,7 @@ async function installPlugin(input: string): Promise { const nodeModulesDir = path.join(pluginsDir, 'node_modules') try { - execSync(`npm install ${installSpec} --prefix "${pluginsDir}" --save`, { + execFileSync('npm', ['install', installSpec, '--prefix', pluginsDir, '--save'], { stdio: 'inherit', timeout: 60000, }) diff --git a/src/core/command-registry.ts b/src/core/command-registry.ts index 2b37a233..fbd12095 100644 --- a/src/core/command-registry.ts +++ b/src/core/command-registry.ts @@ -12,7 +12,7 @@ interface RegisteredCommand extends CommandDef { * - System commands always own the short name. * - Among plugins, the first to register wins the short name. * - Every plugin command also gets a qualified name: `scope:name`. - * - Adapter plugins (@openacp/telegram, @openacp/discord, @openacp/slack) + * - Adapter plugins (@openacp/telegram, @openacp/discord) * registering a command that already exists → stored as an override * keyed by `channelId:commandName`, used when channelId matches. */ @@ -23,7 +23,7 @@ export class CommandRegistry { /** Adapter-specific overrides: `channelId:commandName` → RegisteredCommand */ private overrides = new Map() - private static ADAPTER_SCOPES = new Set(['telegram', 'discord', 'slack']) + private static ADAPTER_SCOPES = new Set(['telegram', 'discord']) /** * Register a command definition. diff --git a/src/core/commands/admin.ts b/src/core/commands/admin.ts index d345dad9..76c9f803 100644 --- a/src/core/commands/admin.ts +++ b/src/core/commands/admin.ts @@ -43,7 +43,6 @@ export function registerAdminCommands(registry: CommandRegistry, _core: unknown) options: [ { label: 'Telegram', command: '/integrate telegram' }, { label: 'Discord', command: '/integrate discord' }, - { label: 'Slack', command: '/integrate slack' }, ], } satisfies CommandResponse } diff --git a/src/core/setup/setup-channels.ts b/src/core/setup/setup-channels.ts index 968b1cec..97130775 100644 --- a/src/core/setup/setup-channels.ts +++ b/src/core/setup/setup-channels.ts @@ -73,7 +73,8 @@ async function configureViaPlugin(channelId: string): Promise { try { const pluginModule = await import(channelId); plugin = pluginModule.default; - } catch { + } catch (err) { + console.log(`Could not load plugin "${channelId}": ${(err as Error).message}`); return; } } @@ -81,7 +82,7 @@ async function configureViaPlugin(channelId: string): Promise { if (plugin?.configure) { const { SettingsManager } = await import('../plugin/settings-manager.js'); const { createInstallContext } = await import('../plugin/install-context.js'); - const basePath = path.join(os.homedir(), '.openacp', 'plugins'); + const basePath = path.join(os.homedir(), '.openacp', 'plugins', 'data'); const settingsManager = new SettingsManager(basePath); const ctx = createInstallContext({ pluginName: plugin.name, diff --git a/src/core/setup/wizard.ts b/src/core/setup/wizard.ts index 11cee178..07e1ae86 100644 --- a/src/core/setup/wizard.ts +++ b/src/core/setup/wizard.ts @@ -107,16 +107,17 @@ export async function runSetup( }); } + // Handle community plugin selections if (channelId.startsWith('community:')) { const npmPackage = channelId.slice('community:'.length); - const { execSync } = await import('node:child_process'); + const { execFileSync } = await import('node:child_process'); const pluginsDir = path.join(os.homedir(), '.openacp', 'plugins'); const nodeModulesDir = path.join(pluginsDir, 'node_modules'); // Install from npm try { - execSync(`npm install ${npmPackage} --prefix "${pluginsDir}" --save`, { + execFileSync('npm', ['install', npmPackage, '--prefix', pluginsDir, '--save'], { stdio: 'inherit', timeout: 60000, }); diff --git a/src/main.ts b/src/main.ts index 8cfc07ac..b9e55405 100644 --- a/src/main.ts +++ b/src/main.ts @@ -153,6 +153,7 @@ export async function startServer(opts?: StartServerOptions) { if (!fs.existsSync(path.join(pkgDir, 'package.json'))) { // Plugin name doesn't match npm package name — scan installed packages + // Only consider packages that are actual OpenACP plugins (by keywords or scope) let found = false const scopes = fs.readdirSync(nodeModulesDir).filter(d => d.startsWith('@')) for (const scope of scopes) { @@ -164,6 +165,11 @@ export async function startServer(opts?: StartServerOptions) { if (fs.existsSync(candidatePkgPath)) { try { const candidatePkg = JSON.parse(fs.readFileSync(candidatePkgPath, 'utf-8')) + const npmName = candidatePkg.name as string | undefined + const keywords = Array.isArray(candidatePkg.keywords) ? candidatePkg.keywords as string[] : [] + const isOpenACPPlugin = npmName?.startsWith('@openacp/') || keywords.includes('openacp-plugin') + if (!isOpenACPPlugin) continue + const mainPath = path.join(candidateDir, candidatePkg.main || 'dist/index.js') if (fs.existsSync(mainPath)) { const testMod = await import(mainPath) From e560f3a5c8172cca2502f6bcdd4a1212a07c7845 Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 19:55:50 +0700 Subject: [PATCH 09/10] chore: remove plan file from PR (working artifact with local paths) Co-Authored-By: Claude Opus 4.6 (1M context) --- ...026-03-27-extract-slack-plugin-redesign.md | 872 ------------------ 1 file changed, 872 deletions(-) delete mode 100644 docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md diff --git a/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md b/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md deleted file mode 100644 index 3be28058..00000000 --- a/docs/superpowers/plans/2026-03-27-extract-slack-plugin-redesign.md +++ /dev/null @@ -1,872 +0,0 @@ -# Extract Slack Plugin from Redesign Branch — Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Extract the Slack adapter from OpenACP's built-in plugins into a standalone `@openacp/adapter-slack` package in the `slack-plugin` repo, update the Plugin SDK exports, add registry discovery to the setup wizard, and update the plugin-registry manifest. - -**Architecture:** The Slack plugin source code lives at `src/plugins/slack/` on the `redesign/microkernel-plugin-architecture` branch as a built-in plugin. We copy it to the `slack-plugin` repo, rewrite all `../../core/...` imports to use `@openacp/plugin-sdk`, remove the built-in plugin from core, and wire the wizard to discover community adapters from the registry. - -**Tech Stack:** TypeScript, ESM, Vitest, `@openacp/plugin-sdk`, `@slack/bolt`, `@slack/web-api`, `@clack/prompts`, `p-queue`, `nanoid`, `zod` - -**Repos involved (all under `/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/`):** - -| Repo | Path | Branch | Tasks | -|------|------|--------|-------| -| `OpenACP` | `openacp-group/OpenACP` | `feat/extract-slack-plugin` (from `redesign/microkernel-plugin-architecture`) | 1, 5, 6 | -| `slack-plugin` | `openacp-group/slack-plugin` | `feat/redesign-plugin-architecture` (from `main`) | 2, 3, 4 | -| `plugin-registry` | `openacp-group/plugin-registry` | `feat/update-slack-manifest-redesign` (from `main`) | 7 | -| All repos | — | — | 8 | - -**Task execution order:** Task 1 → Task 2 → Task 3 → Task 4 → Task 5 → Task 6 → Task 7 → Task 8 - -Tasks 1 must complete before Tasks 3-4 (SDK exports needed for plugin imports). -Tasks 2-4 (slack-plugin) and Task 5 (OpenACP remove) can run in parallel. -Task 7 (plugin-registry) is independent. - ---- - -### Task 1: Update Plugin SDK Exports - -> **REPO: `OpenACP`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP`) -> **BRANCH: `feat/extract-slack-plugin`** (created from `redesign/microkernel-plugin-architecture`) - -**Files:** -- Modify: `OpenACP/src/packages/plugin-sdk/src/index.ts` -- Modify: `OpenACP/src/index.ts` (if types aren't already exported from the main package) - -The SDK currently exports adapter base classes (`MessagingAdapter`, `BaseRenderer`, `SendQueue`) but is missing several types that adapter plugins need. These must be added before the Slack plugin can compile. - -- [ ] **Step 1: Checkout redesign branch in OpenACP** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP -git stash -git checkout redesign/microkernel-plugin-architecture -git checkout -b feat/extract-slack-plugin -``` - -- [ ] **Step 2: Check which types are already exported from `src/index.ts`** - -Run: -```bash -grep -n "DisplayVerbosity\|AdapterCapabilities\|Attachment\|MessagingAdapterConfig\|IRenderer\|RenderedMessage\|RenderedPermission" src/index.ts -``` - -This tells us which types are already in the main package's public API (the SDK re-exports from `@openacp/cli`). - -- [ ] **Step 3: Add missing type exports to `src/index.ts`** - -Add the following exports to `src/index.ts` if not already present. These are the types adapter plugins need: - -```typescript -// Adapter types for external plugins -export type { DisplayVerbosity } from './core/adapter-primitives/format-types.js' -export type { AdapterCapabilities } from './core/channel.js' -export type { Attachment } from './core/types.js' -export type { MessagingAdapterConfig } from './core/adapter-primitives/messaging-adapter.js' -export type { IRenderer, RenderedMessage, RenderedPermission } from './core/adapter-primitives/rendering/renderer.js' -``` - -- [ ] **Step 4: Update Plugin SDK to re-export new types** - -In `src/packages/plugin-sdk/src/index.ts`, add re-exports for the newly exported types: - -```typescript -// Add to existing re-exports from '@openacp/cli' -export type { - // ... existing exports ... - DisplayVerbosity, - AdapterCapabilities, - Attachment, - MessagingAdapterConfig, - IRenderer, - RenderedMessage, - RenderedPermission, -} from '@openacp/cli' -``` - -- [ ] **Step 5: Verify build** - -```bash -pnpm build -``` - -Expected: Build succeeds with no errors. - -- [ ] **Step 6: Commit** - -```bash -git add src/index.ts src/packages/plugin-sdk/src/index.ts -git commit -m "feat(plugin-sdk): export adapter types for external plugins" -``` - ---- - -### Task 2: Reset slack-plugin Repo - -> **REPO: `slack-plugin`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin`) -> **BRANCH: `feat/redesign-plugin-architecture`** (created from `main`) - -**Files:** -- Modify: `slack-plugin/package.json` -- Modify: `slack-plugin/tsconfig.json` -- Modify: `slack-plugin/vitest.config.ts` -- Delete: all existing `src/` files - -- [ ] **Step 1: Create new branch in slack-plugin** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin -git checkout main -git checkout -b feat/redesign-plugin-architecture -``` - -- [ ] **Step 2: Delete all existing source files** - -```bash -rm -rf src/ dist/ -``` - -- [ ] **Step 3: Create new `src/` directory** - -```bash -mkdir -p src/__tests__ -``` - -- [ ] **Step 4: Update `package.json`** - -Replace the entire `package.json` content: - -```json -{ - "name": "@openacp/adapter-slack", - "version": "0.1.0", - "description": "Slack messaging platform adapter plugin for OpenACP", - "type": "module", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": ["dist"], - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "vitest run", - "test:watch": "vitest", - "prepublishOnly": "npm run build" - }, - "keywords": ["openacp", "openacp-plugin", "slack", "adapter"], - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/Open-ACP/slack-adapter" - }, - "peerDependencies": { - "@openacp/plugin-sdk": "^1.0.0" - }, - "dependencies": { - "@clack/prompts": "^1.1.0", - "@slack/bolt": "^4.6.0", - "@slack/web-api": "^7.15.0", - "nanoid": "^5.0.0", - "p-queue": "^9.1.0", - "zod": "^3.25.0" - }, - "devDependencies": { - "@openacp/plugin-sdk": "file:../OpenACP/src/packages/plugin-sdk", - "typescript": "^5.4.0", - "vitest": "^3.0.0" - } -} -``` - -Note: `devDependencies` uses `file:` link to the local SDK for development. For publishing, change to `"^1.0.0"`. - -- [ ] **Step 5: Update `tsconfig.json`** - -```json -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "strict": true, - "esModuleInterop": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "skipLibCheck": true, - "outDir": "dist", - "rootDir": "src", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["src/__tests__"] -} -``` - -- [ ] **Step 6: Update `vitest.config.ts`** - -```typescript -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - include: ['src/**/*.test.ts'], - }, -}) -``` - -- [ ] **Step 7: Install dependencies** - -```bash -npm install -``` - -- [ ] **Step 8: Commit** - -```bash -git add -A -git commit -m "chore: reset repo for redesign plugin architecture" -``` - ---- - -### Task 3: Copy and Rewrite Slack Plugin Source Files - -> **REPO: `slack-plugin`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin`) -> **BRANCH: `feat/redesign-plugin-architecture`** (same as Task 2) -> **SOURCE: `OpenACP/src/plugins/slack/`** (on `redesign/microkernel-plugin-architecture` branch) - -**Files:** -- Create: `slack-plugin/src/index.ts` -- Create: `slack-plugin/src/adapter.ts` -- Create: `slack-plugin/src/event-router.ts` -- Create: `slack-plugin/src/channel-manager.ts` -- Create: `slack-plugin/src/formatter.ts` -- Create: `slack-plugin/src/renderer.ts` -- Create: `slack-plugin/src/text-buffer.ts` -- Create: `slack-plugin/src/send-queue.ts` -- Create: `slack-plugin/src/permission-handler.ts` -- Create: `slack-plugin/src/slug.ts` -- Create: `slack-plugin/src/types.ts` -- Create: `slack-plugin/src/utils.ts` - -**Source:** Copy from `OpenACP/src/plugins/slack/` on `redesign/microkernel-plugin-architecture` branch. - -**Import rewrite rules (apply to ALL files):** - -| Old import path | New import | Notes | -|---|---|---| -| `from '../../core/plugin/types.js'` | `from '@openacp/plugin-sdk'` | All plugin types | -| `from '../../core/types.js'` | `from '@openacp/plugin-sdk'` | OutgoingMessage, PermissionRequest, etc. | -| `from '../../core/channel.js'` | `from '@openacp/plugin-sdk'` | AdapterCapabilities | -| `from '../../core/adapter-primitives/messaging-adapter.js'` | `from '@openacp/plugin-sdk'` | MessagingAdapter, MessagingAdapterConfig | -| `from '../../core/adapter-primitives/format-types.js'` | `from '@openacp/plugin-sdk'` | DisplayVerbosity | -| `from '../../core/adapter-primitives/rendering/renderer.js'` | `from '@openacp/plugin-sdk'` | BaseRenderer, IRenderer, etc. | -| `from '../../core/core.js'` | Remove — use `ctx.kernel` | OpenACPCore | -| `from '../../core/utils/log.js'` | Remove — use `ctx.log` | createChildLogger | -| `from '../../core/config/config.js'` | Remove — define locally | SlackChannelConfig | - -- [ ] **Step 1: Copy all source files from redesign branch** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group - -# Copy source files (not tests) -for file in index.ts adapter.ts event-router.ts channel-manager.ts formatter.ts renderer.ts text-buffer.ts send-queue.ts permission-handler.ts slug.ts types.ts utils.ts; do - cp OpenACP/src/plugins/slack/$file slack-plugin/src/$file -done -``` - -Note: The OpenACP repo must be on the `redesign/microkernel-plugin-architecture` branch (or `feat/extract-slack-plugin` which was branched from it). - -- [ ] **Step 2: Rewrite `types.ts`** - -This file currently re-exports `SlackChannelConfig` from `../../core/config/config.js`. Instead, define the Zod schema locally. Read the current content of `OpenACP/src/core/config/config.ts` to find the `SlackChannelConfig` schema, then write it directly in `types.ts`: - -```typescript -import { z } from 'zod' - -export const SlackChannelConfigSchema = z.object({ - enabled: z.boolean().default(true), - botToken: z.string(), - appToken: z.string(), - signingSecret: z.string().optional(), - channelPrefix: z.string().default('openacp'), - notificationChannelId: z.string().optional(), - allowedUserIds: z.array(z.string()).default([]), - autoCreateSession: z.boolean().default(true), - startupChannelId: z.string().optional(), -}) - -export type SlackChannelConfig = z.infer - -export interface SlackSessionMeta { - channelId: string - channelSlug: string -} - -export interface SlackFileInfo { - id: string - name: string - mimetype: string - size: number - url_private: string -} -``` - -Verify: Check `OpenACP/src/core/config/config.ts` for the exact `SlackChannelConfig` schema fields. The fields above are from the research but must match exactly. - -- [ ] **Step 3: Rewrite imports in `index.ts`** - -Change: -```typescript -import type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js' -import type { OpenACPCore } from '../../core/core.js' -``` -To: -```typescript -import type { OpenACPPlugin, InstallContext, PluginContext } from '@openacp/plugin-sdk' -``` - -Remove any direct reference to `OpenACPCore`. In the `setup()` hook, access core via `ctx.kernel` instead of importing `OpenACPCore` type. The adapter constructor that takes `core` should receive it from `ctx.kernel`. - -Also update `essential: true` to `essential: false` (external plugins should not be marked essential). - -Remove `pluginDependencies` on `@openacp/security` and `@openacp/notifications` if they are only needed at runtime via `ctx.getService()` — external plugins should use optional lookups rather than hard dependencies on built-in plugins. - -- [ ] **Step 4: Rewrite imports in `adapter.ts`** - -Change all `../../core/...` imports to `@openacp/plugin-sdk`: - -```typescript -import type { OutgoingMessage, PermissionRequest, NotificationMessage, Attachment } from '@openacp/plugin-sdk' -import type { AdapterCapabilities } from '@openacp/plugin-sdk' -import type { FileServiceInterface } from '@openacp/plugin-sdk' -import { MessagingAdapter, type MessagingAdapterConfig } from '@openacp/plugin-sdk' -import type { DisplayVerbosity } from '@openacp/plugin-sdk' -import type { IRenderer } from '@openacp/plugin-sdk' -``` - -Consolidate into a single import: -```typescript -import { - MessagingAdapter, - type MessagingAdapterConfig, - type OutgoingMessage, - type PermissionRequest, - type NotificationMessage, - type Attachment, - type AdapterCapabilities, - type FileServiceInterface, - type DisplayVerbosity, - type IRenderer, -} from '@openacp/plugin-sdk' -``` - -Remove `import { createChildLogger } from '../../core/utils/log.js'`. The adapter receives a logger from the plugin context. Update the constructor to accept a logger parameter, or use a module-level logger approach. The `index.ts` setup() hook should pass `ctx.log` to the adapter constructor. - -Remove `import type { OpenACPCore } from '../../core/core.js'`. The adapter constructor currently takes `OpenACPCore` — change the type to `any` or create a minimal interface for what the adapter actually uses from core (typically `handleNewSession`, `handleIncomingMessage`, `findSession`, `handlePermissionResponse`). - -- [ ] **Step 5: Rewrite imports in `renderer.ts`** - -Change: -```typescript -import { BaseRenderer, type RenderedMessage, type RenderedPermission } from '../../core/adapter-primitives/rendering/renderer.js' -import type { OutgoingMessage, PermissionRequest, NotificationMessage } from '../../core/types.js' -import type { DisplayVerbosity } from '../../core/adapter-primitives/format-types.js' -``` -To: -```typescript -import { - BaseRenderer, - type RenderedMessage, - type RenderedPermission, - type OutgoingMessage, - type PermissionRequest, - type NotificationMessage, - type DisplayVerbosity, -} from '@openacp/plugin-sdk' -``` - -- [ ] **Step 6: Rewrite imports in `formatter.ts`** - -Change: -```typescript -import type { OutgoingMessage, PermissionRequest } from '../../core/types.js' -``` -To: -```typescript -import type { OutgoingMessage, PermissionRequest } from '@openacp/plugin-sdk' -``` - -- [ ] **Step 7: Rewrite imports in `event-router.ts`** - -Remove `import { createChildLogger } from '../../core/utils/log.js'`. The event router should accept a logger via constructor parameter instead. - -- [ ] **Step 8: Rewrite imports in `text-buffer.ts`** - -Remove `import { createChildLogger } from '../../core/utils/log.js'`. Accept logger via constructor parameter. - -- [ ] **Step 9: Verify no remaining core imports** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin -grep -rn "from '../../core" src/ --include="*.ts" | grep -v __tests__ -``` - -Expected: No matches. - -- [ ] **Step 10: Build** - -```bash -npm run build -``` - -Expected: Build succeeds. If there are type errors, fix them by checking what the SDK actually exports and adjusting imports. - -- [ ] **Step 11: Commit** - -```bash -git add src/ -git commit -m "feat: add Slack plugin source with @openacp/plugin-sdk imports" -``` - ---- - -### Task 4: Copy and Rewrite Slack Plugin Tests - -> **REPO: `slack-plugin`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin`) -> **BRANCH: `feat/redesign-plugin-architecture`** (same as Task 2) -> **SOURCE: `OpenACP/src/plugins/slack/__tests__/`** (on `redesign/microkernel-plugin-architecture` branch) - -**Files:** -- Create: `slack-plugin/src/__tests__/adapter-lifecycle.test.ts` -- Create: `slack-plugin/src/__tests__/channel-manager.test.ts` -- Create: `slack-plugin/src/__tests__/conformance.test.ts` -- Create: `slack-plugin/src/__tests__/event-router.test.ts` -- Create: `slack-plugin/src/__tests__/formatter.test.ts` -- Create: `slack-plugin/src/__tests__/permission-handler.test.ts` -- Create: `slack-plugin/src/__tests__/send-queue.test.ts` -- Create: `slack-plugin/src/__tests__/slack-voice.test.ts` -- Create: `slack-plugin/src/__tests__/slug.test.ts` -- Create: `slack-plugin/src/__tests__/text-buffer.test.ts` - -- [ ] **Step 1: Copy all test files** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group -cp OpenACP/src/plugins/slack/__tests__/*.test.ts slack-plugin/src/__tests__/ -``` - -- [ ] **Step 2: Rewrite test imports** - -In each test file, change any `../../core/...` imports to `@openacp/plugin-sdk` or `@openacp/plugin-sdk/testing`: - -```bash -cd slack-plugin -# Find all core imports in test files -grep -rn "from '.*core" src/__tests__/ --include="*.ts" -``` - -For each match, apply the same import rewrite rules as Task 3. Additionally: -- `createTestContext` → `from '@openacp/plugin-sdk/testing'` -- `createTestInstallContext` → `from '@openacp/plugin-sdk/testing'` -- `mockServices` → `from '@openacp/plugin-sdk/testing'` - -Change relative imports to source modules (e.g., `from '../../plugins/slack/adapter.js'`) to local relative paths (e.g., `from '../adapter.js'`). - -- [ ] **Step 3: Update conformance test** - -The `conformance.test.ts` imports `runAdapterConformanceTests` from core. Check if this is exported by the SDK. If not, either: -- Add it to SDK exports, or -- Skip this test for now and note it as a follow-up - -- [ ] **Step 4: Run tests** - -```bash -npm test -``` - -Expected: All tests pass. Fix any import errors or type mismatches. - -- [ ] **Step 5: Commit** - -```bash -git add src/__tests__/ -git commit -m "test: add Slack plugin tests with SDK imports" -``` - ---- - -### Task 5: Remove Slack Built-in Plugin from OpenACP - -> **REPO: `OpenACP`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP`) -> **BRANCH: `feat/extract-slack-plugin`** (same as Task 1) - -**Files:** -- Delete: `OpenACP/src/plugins/slack/` (entire directory) -- Modify: `OpenACP/src/plugins/index.ts` -- Modify: `OpenACP/src/plugins/core-plugins.ts` -- Modify: `OpenACP/package.json` (remove Slack deps if unused elsewhere) - -- [ ] **Step 1: Delete Slack plugin directory** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP -rm -rf src/plugins/slack/ -``` - -- [ ] **Step 2: Update `src/plugins/index.ts`** - -Remove the Slack import and entry from the `builtInPlugins` array. The file imports all 10 plugins — remove the Slack one so only 9 remain. - -Remove: -```typescript -import { createSlackPlugin } from './slack/index.js' -``` -And remove the `createSlackPlugin()` entry from the `builtInPlugins` array. - -- [ ] **Step 3: Update `src/plugins/core-plugins.ts`** - -Same change — remove Slack from the `corePlugins` array. - -- [ ] **Step 4: Check if Slack dependencies are used elsewhere** - -```bash -grep -rn "@slack/bolt\|@slack/web-api" src/ --include="*.ts" | grep -v plugins/slack -``` - -If no results: remove `@slack/bolt` and `@slack/web-api` from `package.json` dependencies. - -```bash -pnpm remove @slack/bolt @slack/web-api -``` - -If other files still use them: leave them. - -- [ ] **Step 5: Build and test** - -```bash -pnpm build -pnpm test -``` - -Expected: Build succeeds, all tests pass (no tests should reference Slack since the tests were in `src/plugins/slack/__tests__/` which was deleted). - -- [ ] **Step 6: Commit** - -```bash -git add -A -git commit -m "feat: remove Slack built-in plugin (extracted to @openacp/adapter-slack)" -``` - ---- - -### Task 6: Add Registry Discovery to Setup Wizard - -> **REPO: `OpenACP`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP`) -> **BRANCH: `feat/extract-slack-plugin`** (same as Task 1) - -**Files:** -- Modify: `OpenACP/src/core/setup/wizard.ts` -- Modify: `OpenACP/src/core/setup/setup-channels.ts` -- Modify: `OpenACP/src/core/setup/types.ts` - -- [ ] **Step 1: Update `src/core/setup/types.ts`** - -The `ChannelId` type is currently `"telegram" | "discord"`. For community plugins, we need a dynamic approach. Add a type for community channel options: - -```typescript -export interface CommunityAdapterOption { - name: string // npm package name, e.g. "@openacp/adapter-slack" - displayName: string // e.g. "Slack Adapter" - icon: string // e.g. "💬" - verified: boolean -} -``` - -- [ ] **Step 2: Update `wizard.ts` — fetch community adapters** - -Add a helper function that queries the registry for verified adapter plugins: - -```typescript -import { RegistryClient } from '../plugin/registry-client.js' - -async function fetchCommunityAdapters(): Promise { - try { - const client = new RegistryClient() - const registry = await client.getRegistry() - return registry.plugins - .filter(p => p.category === 'adapter' && p.verified) - .map(p => ({ - name: p.npm, - displayName: p.displayName ?? p.name, - icon: p.icon, - verified: p.verified, - })) - } catch { - // Offline or registry unavailable — graceful fallback - return [] - } -} -``` - -- [ ] **Step 3: Update `wizard.ts` — merge community adapters into channel selection** - -In `runSetup()`, replace the hardcoded `clack.select` options with dynamic options that include community adapters: - -```typescript -const communityAdapters = await fetchCommunityAdapters() - -const builtInOptions = [ - { label: 'Telegram', value: 'telegram' }, - { label: 'Discord', value: 'discord' }, - { label: 'Both (Telegram + Discord)', value: 'both' }, -] - -const communityOptions = communityAdapters.map(a => ({ - label: `${a.icon} ${a.displayName}${a.verified ? ' (verified)' : ''}`, - value: `community:${a.name}`, // prefix to distinguish from built-in -})) - -const channelChoice = guardCancel( - await clack.select({ - message: 'Which messaging platform do you want to use?', - options: [ - ...builtInOptions, - ...(communityOptions.length > 0 - ? [{ label: '── Community Adapters ──', value: '__separator__', disabled: true }, ...communityOptions] - : []), - ], - }), -) as string -``` - -- [ ] **Step 4: Update `wizard.ts` — handle community adapter selection** - -Add handling for `community:` prefixed selections: - -```typescript -if (typeof channelChoice === 'string' && channelChoice.startsWith('community:')) { - const npmPackage = channelChoice.replace('community:', '') - currentStep++ - - if (settingsManager && pluginRegistry) { - // Auto-install the plugin from npm - clack.spinner() - const s = clack.spinner() - s.start(`Installing ${npmPackage}...`) - - try { - const { exec } = await import('node:child_process') - const { promisify } = await import('node:util') - const execAsync = promisify(exec) - const pluginsDir = path.join(os.homedir(), '.openacp', 'plugins') - await execAsync(`npm install ${npmPackage}`, { cwd: pluginsDir }) - s.stop(ok(`${npmPackage} installed`)) - } catch (err) { - s.stop(fail(`Failed to install ${npmPackage}: ${(err as Error).message}`)) - return false - } - - // Load and run the plugin's install() hook - const pluginModule = await import(npmPackage) - const plugin = pluginModule.default - if (plugin?.install) { - const { createInstallContext } = await import('../plugin/install-context.js') - const ctx = createInstallContext({ - pluginName: plugin.name, - settingsManager, - basePath: settingsManager.getBasePath(), - }) - await plugin.install(ctx) - pluginRegistry.register(plugin.name, { - version: plugin.version, - source: 'npm', - enabled: true, - settingsPath: settingsManager.getSettingsPath(plugin.name), - description: plugin.description, - }) - } - } -} -``` - -Note: The npm install approach should match how `openacp plugin install` works in `src/cli.ts`. Check the existing install command implementation and reuse the same logic. - -- [ ] **Step 5: Update `setup-channels.ts` — show installed community adapters** - -In `configureChannels()`, add installed community plugins to the channel list. After the built-in channels, query `PluginRegistry` for installed npm plugins with `adapter` in the name: - -Update `configureViaPlugin()` to handle community plugins by dynamically importing them from `node_modules`: - -```typescript -async function configureViaPlugin(channelId: string): Promise { - // Built-in plugins - const pluginMap: Record = { - telegram: { importPath: '../../plugins/telegram/index.js', name: '@openacp/telegram' }, - discord: { importPath: '../../plugins/discord/index.js', name: '@openacp/discord' }, - } - - const pluginInfo = pluginMap[channelId] - - let plugin: any - if (pluginInfo) { - const pluginModule = await import(pluginInfo.importPath) - plugin = pluginModule.default - } else { - // Community plugin — channelId is the npm package name - try { - const pluginModule = await import(channelId) - plugin = pluginModule.default - } catch { - return - } - } - - if (plugin?.configure) { - const { SettingsManager } = await import('../plugin/settings-manager.js') - const { createInstallContext } = await import('../plugin/install-context.js') - const basePath = path.join(os.homedir(), '.openacp', 'plugins') - const settingsManager = new SettingsManager(basePath) - const ctx = createInstallContext({ - pluginName: plugin.name, - settingsManager, - basePath, - }) - await plugin.configure(ctx) - } -} -``` - -- [ ] **Step 6: Build and test** - -```bash -pnpm build -pnpm test -``` - -Expected: Build succeeds, all tests pass. - -- [ ] **Step 7: Commit** - -```bash -git add src/core/setup/ -git commit -m "feat(wizard): discover community adapter plugins from registry" -``` - ---- - -### Task 7: Update Plugin Registry Manifest - -> **REPO: `plugin-registry`** (`/Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/plugin-registry`) -> **BRANCH: `feat/update-slack-manifest-redesign`** (created from `main`) - -**Files:** -- Modify: `plugin-registry/plugins/openacp--adapter-slack.json` - -- [ ] **Step 1: Create new branch** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/plugin-registry -git checkout main -git checkout -b feat/update-slack-manifest-redesign -``` - -- [ ] **Step 2: Update manifest** - -Edit `plugins/openacp--adapter-slack.json`: - -```json -{ - "name": "adapter-slack", - "displayName": "Slack Adapter", - "description": "Slack messaging platform adapter for OpenACP", - "npm": "@openacp/adapter-slack", - "repository": "https://github.com/Open-ACP/slack-adapter", - "author": { - "name": "OpenACP", - "github": "Open-ACP" - }, - "version": "0.1.0", - "minCliVersion": "2026.0327.1", - "category": "adapter", - "tags": ["slack", "messaging", "adapter"], - "icon": "💬", - "license": "MIT", - "verified": true, - "createdAt": "2026-03-27T00:00:00Z" -} -``` - -Changes from old manifest: -- `repository` → updated to correct URL -- `minCliVersion` → updated to `2026.0327.1` (redesign branch version) - -- [ ] **Step 3: Rebuild registry** - -```bash -npm run build -``` - -This regenerates `registry.json` from the manifests. - -- [ ] **Step 4: Commit** - -```bash -git add plugins/openacp--adapter-slack.json registry.json -git commit -m "feat: update Slack adapter manifest for redesign architecture" -``` - ---- - -### Task 8: Final Verification - -> **ALL REPOS** — cross-repo verification - -- [ ] **Step 1: Verify OpenACP builds and tests pass** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP -pnpm build -pnpm test -``` - -Expected: All pass. No references to Slack in core. - -- [ ] **Step 2: Verify slack-plugin builds and tests pass** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/slack-plugin -npm run build -npm test -``` - -Expected: All 10 test files pass. - -- [ ] **Step 3: Verify no remaining core imports in slack-plugin** - -```bash -grep -rn "from '.*core\|from '.*plugin/types\|from '.*config/config" slack-plugin/src/ --include="*.ts" -``` - -Expected: No matches. - -- [ ] **Step 4: Verify SDK exports are complete** - -```bash -cd /Users/hieu/Documents/Companies/Lab3/opensource/openacp-group/OpenACP -grep -c "DisplayVerbosity\|AdapterCapabilities\|Attachment\|MessagingAdapterConfig\|IRenderer\|RenderedMessage\|RenderedPermission" src/packages/plugin-sdk/src/index.ts -``` - -Expected: At least 7 matches (one per type). - -- [ ] **Step 5: Create PRs** - -Create PRs for each repo: - -1. **OpenACP**: `feat/extract-slack-plugin` → `redesign/microkernel-plugin-architecture` -2. **slack-plugin**: `feat/redesign-plugin-architecture` → `main` -3. **plugin-registry**: `feat/update-slack-manifest-redesign` → `main` - -PR descriptions should reference each other as related PRs. From a048dd637df34a365a040cea7ea0101147ccb459 Mon Sep 17 00:00:00 2001 From: EthanMiller0x Date: Fri, 27 Mar 2026 20:53:16 +0700 Subject: [PATCH 10/10] fix: register failed community plugins as disabled instead of enabled Co-Authored-By: Claude Opus 4.6 (1M context) --- src/core/setup/wizard.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/setup/wizard.ts b/src/core/setup/wizard.ts index 07e1ae86..da732584 100644 --- a/src/core/setup/wizard.ts +++ b/src/core/setup/wizard.ts @@ -150,12 +150,13 @@ export async function runSetup( settingsPath: settingsManager.getSettingsPath(plugin?.name ?? npmPackage), description: plugin?.description ?? installedPkg.description, }); - } catch { - // Plugin installed via npm but failed to load — still register + } catch (err) { + // Plugin installed via npm but failed to load — register as disabled + console.log(fail(`Failed to load ${npmPackage}: ${(err as Error).message}`)); pluginRegistry.register(npmPackage, { version: 'unknown', source: 'npm', - enabled: true, + enabled: false, settingsPath: settingsManager.getSettingsPath(npmPackage), }); }