Skip to content

Conversation

@TalexDreamSoul
Copy link
Contributor

@TalexDreamSoul TalexDreamSoul commented Jan 26, 2026

🎯 Overview

This PR adds the ability for users to manually add any file to the launcher, solving the problem of missing applications in automatic scans.

✨ Features

Core Functionality

  • Custom App Management: Full CRUD operations for user-added applications
  • File Type Support: JAR files, scripts (sh, bash, py, js), executables, and more
  • Smart Search: Integrated into the search engine with pinyin support for Chinese names
  • Usage Tracking: Records launch count and last used time for better recommendations

Implementation Details

  • Database: New customApps table with comprehensive metadata
  • Backend: CustomAppProvider for app management and launching
  • IPC: Secure channels for renderer-main communication
  • Search: CustomAppSearchProvider integrated into search engine
  • UI Ready: File selection dialogs for apps and icons

🔧 Technical Changes

New Files

  • custom-app-provider.ts: Core business logic
  • custom-app-ipc.ts: IPC channel definitions and handlers
  • custom-app-module.ts: Module initialization
  • custom-app-integration.ts: Search integration
  • custom-app-search-provider.ts: Search provider implementation

Modified Files

  • schema.ts: Added customApps table
  • index.ts: Registered custom app module
  • search-core.ts: Registered custom app search provider

🎨 User Experience

Users can now:

  1. Add any file to the launcher via file picker
  2. Customize display name and icon
  3. Set launch commands and environment variables
  4. Search for custom apps using name or pinyin
  5. Track usage statistics

🧪 Testing

  • ✅ Database schema validated
  • ✅ IPC channels defined
  • ✅ Search provider registered
  • ✅ Module initialization integrated

📝 Notes

  • UI implementation is pending (backend is ready)
  • Supports all major file types
  • Includes proper error handling and logging
  • Compatible with existing app scanning system

🔗 Related Issues

Closes #[issue-number] (if applicable)

Summary by CodeRabbit

  • New Features
    • Custom app management: Add, edit, and delete custom applications with configurable launch commands and environment variables
    • Search integration: Custom apps now appear in search results with intelligent scoring based on usage frequency and recency
    • Multi-format support: Handles various file types including executables, scripts, and archives
    • Usage tracking: Monitors app launch statistics and last used timestamps

✏️ Tip: You can customize this high-level summary in your review settings.

- Add customApps database table for storing user-added applications
- Implement CustomAppProvider for managing custom apps (CRUD operations)
- Add IPC channels for custom app management and file selection
- Create CustomAppIntegration for search functionality
- Register CustomAppSearchProvider in search engine
- Support JAR files, scripts, executables, and other file types
- Include pinyin keyword generation for Chinese app names
- Track usage statistics (launch count, last used time)

This feature allows users to manually add any file to the launcher,
solving the problem of missing applications in automatic scans.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

📝 Walkthrough

Walkthrough

Introduces a custom applications management feature by adding a database schema for custom apps, a provider for CRUD operations, Electron IPC handlers for main/renderer communication, search integration with caching and scoring, and module lifecycle management. Enables users to add, manage, search, and launch custom applications.

Changes

Cohort / File(s) Summary
Database Schema
apps/core-app/src/main/db/schema.ts
New customApps SQLite table with fields for path, displayName, iconPath, launchCommand, workingDirectory, environmentVars, fileType, enabled status, timestamps (createdAt, updatedAt, lastUsedAt), useCount, and notes. Includes indexes on path, enabled, and lastUsedAt for performance.
Core Provider Logic
apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-provider.ts
Implements CustomAppProvider with CRUD operations (add, update, delete, get), app launching with file type detection, keyword generation for search, environment variable handling, and database row mapping. Exports CustomApp and CustomAppInput interfaces.
IPC Communication
apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-ipc.ts
Defines 10 IPC channels (GET_ALL, GET_ENABLED, GET_ONE, ADD, UPDATE, DELETE, TOGGLE_ENABLED, LAUNCH, SELECT_FILE, SELECT_ICON) and handler registration/unregistration functions for main/renderer process communication.
Module Management
apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-module.ts
Implements IModule with onLoad and onUnload lifecycle methods that register and unregister IPC handlers respectively.
Search Integration
apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts, custom-app-search-provider.ts
CustomAppIntegration provides search functionality with TTL-based caching, multi-criteria scoring (exact/prefix/contains matches, usage frequency, recency), and error handling. CustomAppSearchProvider implements ISearchProvider interface for integration with search engine.
Application Integration
apps/core-app/src/main/index.ts, apps/core-app/src/main/modules/box-tool/search-engine/search-core.ts
Adds customAppModule to startup module sequence and registers customAppSearchProvider with SearchEngineCore.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Renderer as Renderer Process
    participant IPC as IPC Handler
    participant Provider as CustomAppProvider
    participant DB as Database
    
    User->>Renderer: Click "Add App"
    Renderer->>IPC: SELECT_FILE dialog
    IPC->>DB: (file selected)
    IPC->>Renderer: Return file path
    
    Renderer->>IPC: ADD (CustomAppInput)
    IPC->>Provider: addCustomApp()
    Provider->>Provider: Validate file existence
    Provider->>Provider: detectFileType()
    Provider->>DB: Insert new record
    DB-->>Provider: Return CustomApp
    Provider-->>IPC: CustomApp result
    IPC-->>Renderer: Success response
    Renderer->>User: App added
Loading
sequenceDiagram
    participant User as User
    participant Search as SearchEngine
    participant Provider as CustomAppSearchProvider
    participant Integration as CustomAppIntegration
    participant Cache as TTL Cache
    participant DB as Database
    
    User->>Search: Enter search query
    Search->>Provider: provide(query)
    Provider->>Integration: search(query)
    Integration->>Cache: Check cached apps
    
    alt Cache expired
        Cache->>DB: Fetch enabled apps
        DB-->>Cache: Return CustomApps
        Cache->>Cache: Store with TTL
    end
    
    Integration->>Integration: calculateScore() for each app
    Integration->>Integration: Filter positive scores
    Integration->>Integration: Sort by score
    Integration-->>Provider: TuffSearchResult[]
    Provider-->>Search: Results with logging
    Search->>User: Display results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Whiskers twitched with joy today,
Custom apps are here to stay!
Search and launch with hop-skip-bound,
New features everywhere around! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat: Add custom app launcher functionality' clearly and concisely summarizes the main change—adding custom app launcher support—which aligns with the changeset's core objective of enabling users to manually add arbitrary files to the launcher.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c2dc7e987a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +82 to +85
const now = Date.now()
const updateData: any = {
...updates,
updatedAt: now

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recompute derived fields when updating path

这里直接把 updates 写回数据库,如果用户在编辑时修改了 path,不会重新校验新路径是否存在,也不会更新 fileType/workingDirectory。这样一来旧的文件类型和工作目录会继续被用于启动,导致“路径已改但仍按旧类型启动/旧 cwd 启动”的错误场景。只要 UI 允许修改路径,就会触发此问题,建议在 updates.path 存在时重新检测文件类型并更新工作目录。

Useful? React with 👍 / 👎.

Comment on lines +186 to +190
case 'jar':
// JAR 文件使用 java -jar 启动
await execAsync(`java -jar "${appPath}"`, {
cwd: workingDirectory
})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass environment variables for non-command launches

当没有 launchCommand 时,JAR/脚本分支的 execAsync 调用没有传入 env,因此为自定义应用配置的环境变量会在这些默认启动路径中被忽略。用户只要依赖 environmentVars 且未设置 launchCommand,实际启动环境就与配置不一致;建议在这些分支中同样传入合并后的 env

Useful? React with 👍 / 👎.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In
`@apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts`:
- Around line 80-131: In calculateScore (the function that uses
customAppProvider.generateKeywords on app.displayName and scores
exact/prefix/contains/file/path/notes/useCount/lastUsedAt), add a short
clarifying comment near the keyword matching block stating that scoring is
cumulative across criteria (e.g., note that an exact match may also pass
prefix/contains checks and points are intentionally summed), so future
maintainers understand the design intent and that multiple matches reinforce
relevance.
- Around line 136-173: appToSearchResult currently instantiates
TuffSearchResultBuilder incorrectly and calls item-level methods on it; instead,
build a TuffItem using TuffItemBuilder (setId, setTitle, setSubtitle, setIcon,
setAction, setMetadata, setTags) to produce a TuffItem, then construct a
TuffSearchResult by creating new TuffSearchResultBuilder(query) (provide a
TuffQuery instance consistent with other providers), call
setItems([theTuffItem]) and any needed setDuration()/setActivate()/setSources(),
then call build(); ensure the action uses
customAppProvider.launchCustomApp(app.id) as before and that
TuffSearchResultBuilder is given the required query parameter.

In `@apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-ipc.ts`:
- Around line 33-42: The file currently uses ipcMain.handle for IPC handlers
(e.g., in registerCustomAppIpc and the handler for CustomAppIpcChannels.GET_ALL
that calls customAppProvider.getAllCustomApps); replace each ipcMain.handle
usage with the project's standard regChannel(ChannelType.MAIN, ...) pattern
(import regChannel and ChannelType/ChannelCore as needed) so all handlers are
registered via the centralized channel infrastructure; ensure each handler's
signature and error handling are preserved when migrating (use
regChannel(ChannelType.MAIN, CustomAppIpcChannels.GET_ALL, async () => { ... }))
and remove the direct ipcMain.handle calls.

In `@apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-provider.ts`:
- Around line 168-216: The launch method currently uses execAsync with
interpolated strings (in launch, cases for 'jar','sh','bash','py' and the
launchCommand path) which allows command injection; change these to use
child_process.spawn (or execFile) with a command and args array instead of
concatenated strings, e.g., spawn('java', ['-jar', appPath], { cwd, env }) for
the 'jar' branch, spawn('bash', [appPath], { cwd }) for shell scripts,
spawn('python3', [appPath], { cwd }) for 'py', and when a user-supplied
launchCommand is provided, parse it into a command and args (or require separate
command + args fields) and call spawn(command, args, { cwd, env }) rather than
passing the raw string to execAsync; remove usage of execAsync for these flows
and keep shell.openPath(appPath) only for cases that truly require opening with
the OS.
- Around line 242-247: The current logic treats displayName as a file path by
calling path.basename on it; replace that by pulling the actual file path from
the CustomApp object (e.g., use the CustomApp.path field) and only compute const
fileName = path.basename(customApp.path, path.extname(customApp.path)) when
customApp.path is defined/truthy, then push fileName.toLowerCase() into
keywords; remove the fileName !== displayName comparison and guard against
missing path to avoid calling path.* on a non-path string.
- Around line 152-159: The update currently reads app.useCount and writes
app.useCount + 1, causing a race condition; change the db update for customApps
(the db.update(...).set({...}).where(eq(customApps.id, id)) call) to perform a
database-side increment for useCount (use an SQL increment expression or the
query-builder's increment helper) while still setting lastUsedAt to Date.now(),
so the update is atomic and avoids lost concurrent increments.
🧹 Nitpick comments (8)
apps/core-app/src/main/db/schema.ts (2)

767-770: Timestamp columns lack SQL defaults unlike other tables in this schema.

Other tables in this schema (e.g., embeddings, itemUsageStats, intelligenceQuotas) use sql\(strftime('%s', 'now'))`for automatic timestamp defaults. HerecreatedAtandupdatedAt` require manual population, which could lead to null/missing timestamps if the application code forgets to set them.

♻️ Consider adding SQL defaults for consistency
     // 创建时间
-    createdAt: integer('created_at').notNull(),
+    createdAt: integer('created_at').notNull().default(sql`(strftime('%s', 'now'))`),
     
     // 最后修改时间
-    updatedAt: integer('updated_at').notNull(),
+    updatedAt: integer('updated_at').notNull().default(sql`(strftime('%s', 'now'))`),

781-785: Redundant index on path column.

The path column already has a .unique() constraint (line 743), which implicitly creates an index in SQLite. The explicit pathIdx index is redundant and adds unnecessary overhead during writes.

♻️ Remove redundant index
   (table) => ({
-    pathIdx: index('idx_custom_apps_path').on(table.path),
     enabledIdx: index('idx_custom_apps_enabled').on(table.enabled),
     lastUsedIdx: index('idx_custom_apps_last_used').on(table.lastUsedAt)
   })
apps/core-app/src/main/modules/box-tool/search-engine/search-core.ts (1)

73-88: Consider adding custom apps to the @app filter alias.

The custom-app-provider isn't included in PROVIDER_ALIASES, so users searching with @app won't see custom apps. If custom apps should be discoverable via the @app filter, add the provider ID to the alias list.

♻️ Add custom-app-provider to app alias
 const PROVIDER_ALIASES: Record<string, string[]> = {
   file: [
     'file-provider',
     'file-index',
     'files',
     'fs',
     'document',
     'everything-provider',
     'everything'
   ],
-  app: ['app-provider', 'applications', 'apps'],
+  app: ['app-provider', 'applications', 'apps', 'custom-app-provider'],
   plugin: ['plugin-features', 'plugins', 'extension', 'extensions'],
   system: ['system-provider', 'sys'],
   url: ['url-provider', 'link', 'links'],
   preview: ['preview-provider']
 }
apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-ipc.ts (2)

55-62: Add input validation for IPC parameters.

IPC handlers receive data from the renderer process, which should be treated as untrusted input. The id parameter should be validated (positive integer check), and in the ADD/UPDATE handlers, the CustomAppInput should be sanitized.

Example validation
   ipcMain.handle(CustomAppIpcChannels.GET_ONE, async (_event, id: number) => {
     try {
+      if (typeof id !== 'number' || !Number.isInteger(id) || id <= 0) {
+        throw new Error('Invalid app id')
+      }
       return await customAppProvider.getCustomApp(id)
     } catch (error) {

126-148: Consider specifying a parent window for the dialog.

The dialog may appear behind the main window on some platforms when no parent is specified. Using BrowserWindow.getFocusedWindow() as the parent can improve the user experience.

Suggested improvement
+import { ipcMain, dialog, BrowserWindow } from 'electron'
...
   ipcMain.handle(CustomAppIpcChannels.SELECT_FILE, async () => {
     try {
+      const parentWindow = BrowserWindow.getFocusedWindow()
       const result = await dialog.showOpenDialog({
+        ...(parentWindow ? { parentWindow } : {}),
         title: 'Select Application or File',
apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-provider.ts (3)

44-76: Add validation that the path is a regular file.

fs.existsSync only checks existence, not whether the path is a regular file. Users could potentially add directories or symbolic links. Consider using fs.statSync to verify it's a file.

Suggested improvement
   async addCustomApp(input: CustomAppInput): Promise<CustomApp> {
-    if (!fs.existsSync(input.path)) {
-      throw new Error(`File not found: ${input.path}`)
+    try {
+      const stat = fs.statSync(input.path)
+      if (!stat.isFile()) {
+        throw new Error(`Path is not a file: ${input.path}`)
+      }
+    } catch (error) {
+      if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
+        throw new Error(`File not found: ${input.path}`)
+      }
+      throw error
     }

81-103: Avoid any type and validate path updates.

The any type on line 83 bypasses TypeScript safety. Also, if updates.path is provided, the new path should be validated for existence before updating.

Suggested improvement
   async updateCustomApp(id: number, updates: Partial<CustomAppInput>): Promise<CustomApp> {
+    if (updates.path) {
+      if (!fs.existsSync(updates.path)) {
+        throw new Error(`File not found: ${updates.path}`)
+      }
+    }
+
     const now = Date.now()
-    const updateData: any = {
+    const updateData: Partial<typeof customApps.$inferInsert> & { updatedAt: number } = {
       ...updates,
       updatedAt: now
     }

276-293: Add error handling for JSON.parse.

If environmentVars contains malformed JSON (e.g., due to data corruption), JSON.parse will throw and crash the operation. Wrap in try/catch for resilience.

Suggested fix
   private mapDbToCustomApp(row: any): CustomApp {
+    let environmentVars: Record<string, string> | undefined
+    if (row.environmentVars) {
+      try {
+        environmentVars = JSON.parse(row.environmentVars)
+      } catch {
+        environmentVars = undefined
+      }
+    }
+
     return {
       id: row.id,
       path: row.path,
       displayName: row.displayName,
       // ...
-      environmentVars: row.environmentVars ? JSON.parse(row.environmentVars) : undefined,
+      environmentVars,
       // ...
     }
   }

Comment on lines +80 to +131
private calculateScore(app: CustomApp, query: string): number {
let score = 0

// 生成搜索关键词
const keywords = customAppProvider.generateKeywords(app.displayName)

// 完全匹配
if (keywords.some((kw) => kw === query)) {
score += 100
}

// 前缀匹配
if (keywords.some((kw) => kw.startsWith(query))) {
score += 80
}

// 包含匹配
if (keywords.some((kw) => kw.includes(query))) {
score += 60
}

// 文件名匹配
const fileName = path.basename(app.path).toLowerCase()
if (fileName.includes(query)) {
score += 40
}

// 路径匹配
if (app.path.toLowerCase().includes(query)) {
score += 20
}

// 备注匹配
if (app.notes && app.notes.toLowerCase().includes(query)) {
score += 10
}

// 使用频率加权
if (app.useCount > 0) {
score += Math.min(app.useCount, 20)
}

// 最近使用加权
if (app.lastUsedAt) {
const daysSinceLastUse = (Date.now() - app.lastUsedAt) / (1000 * 60 * 60 * 24)
if (daysSinceLastUse < 7) {
score += 10
}
}

return score
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check for other scoring implementations in the box-tool addon modules
rg -n 'calculateScore|score \+=' --type=ts apps/core-app/src/main/modules/box-tool/addon -A 3 -B 1

Repository: talex-touch/tuff

Length of output: 5076


🏁 Script executed:

# Also check if there are any other search providers with similar ranking logic
fd -t f '\.(ts|tsx)$' apps/core-app/src/main/modules/box-tool/addon -exec rg -l 'score|ranking|weight' {} \;

Repository: talex-touch/tuff

Length of output: 272


🏁 Script executed:

# Look at the actual structure to see if this is the pattern elsewhere
rg -n 'if.*\.some|keyword.*match' --type=ts apps/core-app/src/main/modules/box-tool/addon -A 2 -B 1

Repository: talex-touch/tuff

Length of output: 4426


🏁 Script executed:

# Check if there are any tests that might clarify scoring intent
fd -t f 'custom-app.*test\|test.*custom-app' apps/core-app/ --type f 2>/dev/null | head -20

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

# Look at search-processing-service.ts to see if it has different ranking approach
cat -n apps/core-app/src/main/modules/box-tool/addon/apps/search-processing-service.ts | sed -n '90,120p'

Repository: talex-touch/tuff

Length of output: 1303


Add clarifying comment about intentional cumulative scoring in calculateScore().

The scoring logic accumulates points across multiple criteria—an exact keyword match will also pass prefix and contains checks, yielding up to 240 points from keywords alone. This appears to be an intentional design to heavily boost relevance when an app matches multiple scoring tiers. Consider adding a brief comment (e.g., "// Cumulative: matches across multiple criteria reinforce relevance") to make this behavior explicit for future maintainers.

🤖 Prompt for AI Agents
In `@apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts`
around lines 80 - 131, In calculateScore (the function that uses
customAppProvider.generateKeywords on app.displayName and scores
exact/prefix/contains/file/path/notes/useCount/lastUsedAt), add a short
clarifying comment near the keyword matching block stating that scoring is
cumulative across criteria (e.g., note that an exact match may also pass
prefix/contains checks and points are intentionally summed), so future
maintainers understand the design intent and that multiple matches reinforce
relevance.

Comment on lines +136 to +173
private appToSearchResult(app: CustomApp): TuffSearchResult {
const builder = new TuffSearchResultBuilder()

// 基本信息
builder
.setId(`custom-app:${app.id}`)
.setTitle(app.displayName)
.setSubtitle(app.path)
.setIcon(app.iconPath || this.getDefaultIcon(app.fileType))

// 设置执行动作
builder.setAction(async () => {
try {
await customAppProvider.launchCustomApp(app.id)
return true
} catch (error) {
logger.error(`Failed to launch custom app ${app.id}:`, error)
return false
}
})

// 添加元数据
builder.setMetadata({
type: 'custom-app',
customAppId: app.id,
fileType: app.fileType,
path: app.path
})

// 添加标签
const tags: string[] = ['Custom App']
if (app.fileType) {
tags.push(app.fileType.toUpperCase())
}
builder.setTags(tags)

return builder.build()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find TuffSearchResultBuilder definition
fd -t f "\.ts" apps/core-app/src | xargs rg -l "class TuffSearchResultBuilder"

Repository: talex-touch/tuff

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Find and examine TuffSearchResultBuilder class
rg -A 50 "class TuffSearchResultBuilder" --type=ts

Repository: talex-touch/tuff

Length of output: 3508


🏁 Script executed:

#!/bin/bash
# Check search-core.ts for source property requirements
fd -t f "search-core.ts" apps/core-app/src

Repository: talex-touch/tuff

Length of output: 128


🏁 Script executed:

#!/bin/bash
# Look for other TuffSearchResultBuilder usage in box-tool addon
rg "TuffSearchResultBuilder" --type=ts apps/core-app/src/main/modules/box-tool/addon -B 2 -A 15

Repository: talex-touch/tuff

Length of output: 50372


🏁 Script executed:

#!/bin/bash
# Find TuffItemBuilder class definition
rg -A 30 "class TuffItemBuilder" --type=ts packages/utils

Repository: talex-touch/tuff

Length of output: 2422


🏁 Script executed:

#!/bin/bash
# Look at custom-app-integration.ts to understand the full context
fd -t f "custom-app-integration.ts" apps/core-app/src

Repository: talex-touch/tuff

Length of output: 136


🏁 Script executed:

#!/bin/bash
# Check the actual file around the method
cat -n apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts | sed -n '130,180p'

Repository: talex-touch/tuff

Length of output: 1542


🏁 Script executed:

#!/bin/bash
# Check if TuffItemBuilder has setSource, setAction, setMetadata, setTags methods
rg -A 100 "class TuffItemBuilder" --type=ts packages/utils/core-box/builder/tuff-builder.ts | grep -E "(setSource|setAction|setMetadata|setTags)"

Repository: talex-touch/tuff

Length of output: 72


🏁 Script executed:

#!/bin/bash
# Look for usage of appToSearchResult to understand the expected return type and how it's called
rg -B 5 -A 5 "appToSearchResult" apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts

Repository: talex-touch/tuff

Length of output: 879


🏁 Script executed:

#!/bin/bash
# Check how the method should properly use both builders based on context
rg -B 10 "appToSearchResult\(" apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts | head -30

Repository: talex-touch/tuff

Length of output: 660


🏁 Script executed:

#!/bin/bash
# Check full TuffSearchResultBuilder API to see what methods it actually has
rg -A 150 "class TuffSearchResultBuilder" packages/utils/core-box/builder/tuff-builder.ts | head -100

Repository: talex-touch/tuff

Length of output: 2050


🏁 Script executed:

#!/bin/bash
# Check TuffItemBuilder full API with all its methods
rg -A 200 "class TuffItemBuilder" packages/utils/core-box/builder/tuff-builder.ts | grep -E "^\s+(public |private ).*\(" | head -30

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check what TuffItemBuilder methods are available
rg -A 200 "class TuffItemBuilder" packages/utils/core-box/builder/tuff-builder.ts | grep "public.*(" | head -40

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look at how other providers actually build results - check app-provider.ts more carefully
rg -B 5 -A 15 "private.*toSearchResult|appToSearchResult" apps/core-app/src/main/modules/box-tool/addon/apps/app-provider.ts | head -60

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Get the full TuffItemBuilder class definition without grep filtering
rg "class TuffItemBuilder" -A 300 packages/utils/core-box/builder/tuff-builder.ts | head -200

Repository: talex-touch/tuff

Length of output: 4013


🏁 Script executed:

#!/bin/bash
# Look at app-provider.ts to see how it properly converts apps to items
cat apps/core-app/src/main/modules/box-tool/addon/apps/app-provider.ts | grep -A 50 "mapFileToTuffItem\|toTuffItem" | head -70

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search to see if appToSearchResult is using the wrong method pattern - maybe look at how search results are built in other integrations
fd -t f ".*-integration.ts" apps/core-app/src/main/modules/box-tool/addon/apps

Repository: talex-touch/tuff

Length of output: 136


🏁 Script executed:

#!/bin/bash
# Let's check if there's a similar integration to compare the pattern
cat apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts | sed -n '1,50p'

Repository: talex-touch/tuff

Length of output: 1399


🏁 Script executed:

#!/bin/bash
# Check what TuffSearchResult type definition looks like
rg "type TuffSearchResult|interface TuffSearchResult" -A 10 packages/utils

Repository: talex-touch/tuff

Length of output: 1394


🏁 Script executed:

#!/bin/bash
# Check how the search method is being called - does it have access to query?
cat apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts | sed -n '10,80p'

Repository: talex-touch/tuff

Length of output: 1734


🏁 Script executed:

#!/bin/bash
# Let me verify what methods are actually available on TuffSearchResultBuilder - looking for setId, setTitle etc
rg "setId|setTitle|setSubtitle|setIcon|setAction|setMetadata" packages/utils/core-box/builder/tuff-builder.ts

Repository: talex-touch/tuff

Length of output: 812


🏁 Script executed:

#!/bin/bash
# Verify the search method signature that's calling appToSearchResult
rg "async search" apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts -A 20 | head -30

Repository: talex-touch/tuff

Length of output: 631


🏁 Script executed:

#!/bin/bash
# Check if TuffQuery is available in the context to build properly
rg "TuffQuery" apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Let's see how the search method signature uses query - is it TuffQuery or just string?
cat apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts | head -150 | tail -50

Repository: talex-touch/tuff

Length of output: 1180


🏁 Script executed:

#!/bin/bash
# Check if there's a TuffQuery import or if it's just a string
rg "import.*TuffQuery\|type TuffQuery" apps/core-app/src/main/modules/box-tool/addon/apps/

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Verify the exact type of what's being passed around - look at search method more carefully
rg -B 5 "appToSearchResult\(app\)" apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts

Repository: talex-touch/tuff

Length of output: 422


Fix critical bugs in appToSearchResult(): use wrong builder class and missing required constructor parameter.

The method has multiple critical issues:

  1. Missing required parameter: TuffSearchResultBuilder constructor requires a query: TuffQuery parameter, but the code calls new TuffSearchResultBuilder() with no arguments.

  2. Wrong builder class: The code attempts to call setId(), setTitle(), setSubtitle(), setIcon(), setAction(), setMetadata(), and setTags() on TuffSearchResultBuilder, but these methods don't exist on that class. These are methods on TuffItemBuilder. TuffSearchResultBuilder only has: setItems(), setDuration(), setActivate(), setSources(), and setSortStats().

  3. Incorrect architecture: The method should build individual TuffItem objects using TuffItemBuilder, then pass them to TuffSearchResultBuilder.setItems() to create the result container.

The correct pattern (based on other providers) is:

  • Build TuffItem with TuffItemBuilder
  • Create TuffSearchResult with TuffSearchResultBuilder(query).setItems([item]).build()
  • Each result must include a query object, not just a string
🤖 Prompt for AI Agents
In `@apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-integration.ts`
around lines 136 - 173, appToSearchResult currently instantiates
TuffSearchResultBuilder incorrectly and calls item-level methods on it; instead,
build a TuffItem using TuffItemBuilder (setId, setTitle, setSubtitle, setIcon,
setAction, setMetadata, setTags) to produce a TuffItem, then construct a
TuffSearchResult by creating new TuffSearchResultBuilder(query) (provide a
TuffQuery instance consistent with other providers), call
setItems([theTuffItem]) and any needed setDuration()/setActivate()/setSources(),
then call build(); ensure the action uses
customAppProvider.launchCustomApp(app.id) as before and that
TuffSearchResultBuilder is given the required query parameter.

Comment on lines +33 to +42
export function registerCustomAppIpc(): void {
// 获取所有自定义应用
ipcMain.handle(CustomAppIpcChannels.GET_ALL, async () => {
try {
return await customAppProvider.getAllCustomApps()
} catch (error) {
logger.error('Failed to get all custom apps:', error)
throw error
}
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for regChannel usage in the codebase
rg -n "regChannel" --type=ts -B2 -A2 apps/core-app/src/main/

Repository: talex-touch/tuff

Length of output: 2943


🏁 Script executed:

#!/bin/bash
# Check the full content of the custom-app-ipc.ts file to understand structure
wc -l "apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-ipc.ts"

Repository: talex-touch/tuff

Length of output: 132


🏁 Script executed:

#!/bin/bash
# Search for ipcMain.handle usage in main process files to see if pattern is used elsewhere
rg -n "ipcMain\.handle" --type=ts apps/core-app/src/main/ | head -20

Repository: talex-touch/tuff

Length of output: 1464


🏁 Script executed:

#!/bin/bash
# Check what's exported from utils package related to regChannel
find . -name "*utils*" -o -name "*channel*" | grep -E "(src|lib)" | head -20

Repository: talex-touch/tuff

Length of output: 1070


🏁 Script executed:

#!/bin/bash
# Read the custom-app-ipc.ts file to see imports and structure
head -40 "apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-ipc.ts"

Repository: talex-touch/tuff

Length of output: 1075


🏁 Script executed:

#!/bin/bash
# Check how other addon IPC files are structured
find "apps/core-app/src/main/modules/box-tool/addon" -name "*ipc*.ts" -o -name "*channel*.ts" | head -10

Repository: talex-touch/tuff

Length of output: 128


🏁 Script executed:

#!/bin/bash
# Check how ChannelCore is instantiated and made available in modules
rg -n "ChannelCore\|channelBridge\|channelCore" --type=ts "apps/core-app/src/main/modules/box-tool" -B2 -A2 | head -40

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check how the addon module is initialized and if it has access to channel infrastructure
rg -n "registerCustomAppIpc\|custom-app-ipc" --type=ts "apps/core-app/src/main" -B3 -A1

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if addon modules have a base structure or initialization pattern
ls -la "apps/core-app/src/main/modules/box-tool/addon/apps/" | head -20

Repository: talex-touch/tuff

Length of output: 1200


🏁 Script executed:

#!/bin/bash
# Check the module entry points and structure
head -50 "apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-provider.ts" 2>/dev/null || echo "File not found, checking directory..."

Repository: talex-touch/tuff

Length of output: 1201


🏁 Script executed:

#!/bin/bash
# Check the custom-app-module.ts to see how the addon is structured
cat -n "apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-module.ts"

Repository: talex-touch/tuff

Length of output: 1244


🏁 Script executed:

#!/bin/bash
# Search for where registerCustomAppIpc is called
rg -n "registerCustomAppIpc\|unregisterCustomAppIpc" --type=ts "apps/core-app/src/main"

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if other addon or non-module files use ipcMain.handle vs regChannel
rg -n "ipcMain\.handle" --type=ts "apps/core-app/src/main" | wc -l

Repository: talex-touch/tuff

Length of output: 62


🏁 Script executed:

#!/bin/bash
# Check how modules typically access ChannelCore or channel infrastructure
rg -n "import.*ChannelCore\|import.*channelBridge" --type=ts "apps/core-app/src/main/modules" -B1 | head -30

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if there's a global or singleton ChannelCore instance that should be imported
rg -n "export.*channelCore\|export.*channelBridge" --type=ts "apps/core-app/src/main" -B2 -A1

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check how BaseModule or IModule types provide access to channel functionality
rg -n "interface IModule\|class BaseModule" --type=ts "apps/core-app/src/main" -A15 | head -50

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look at how modules access services - check module initialization patterns
head -100 "apps/core-app/src/main/modules/box-tool/core-box/window.ts" | grep -A20 "regChannel"

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for files that successfully use regChannel to understand the pattern
rg -l "regChannel" --type=ts "apps/core-app/src/main/modules" | head -5

Repository: talex-touch/tuff

Length of output: 221


🏁 Script executed:

#!/bin/bash
# Check a file that uses regChannel to see how it imports/uses it
rg -n "regChannel" --type=ts "apps/core-app/src/main/modules/ai/agents/agent-channels.ts" -B10 -A5

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check how regChannel is used in plugin.ts which showed regChannel usage
rg -n "regChannel" --type=ts "apps/core-app/src/main/modules/plugin/plugin.ts" -B5 -A5 | head -40

Repository: talex-touch/tuff

Length of output: 582


🏁 Script executed:

#!/bin/bash
# Look for imports of channelBridge or access patterns to the channel system in plugin module
head -50 "apps/core-app/src/main/modules/plugin/plugin.ts" | grep -E "import|channel|Channel"

Repository: talex-touch/tuff

Length of output: 1378


🏁 Script executed:

#!/bin/bash
# Check the module manager to understand how modules are initialized and what they have access to
rg -n "class.*ModuleManager\|interface.*ModuleManager" --type=ts "apps/core-app/src/main" -A30 | head -60

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if there's a service injection or context passed to modules
rg -n "class ModuleManager\|onLoad.*context\|onLoad.*channel" --type=ts "apps/core-app/src/main" -A10 | head -50

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look at how the addon is loaded - check the parent module that loads CustomAppModule
rg -n "customAppModule\|CustomAppModule" --type=ts "apps/core-app/src/main" -B5 -A3

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if there are any other addon IPC files and how they handle registration
find "apps/core-app/src/main/modules/box-tool/addon" -type f -name "*.ts" -exec grep -l "ipcMain\|regChannel" {} \;

Repository: talex-touch/tuff

Length of output: 128


🏁 Script executed:

#!/bin/bash
# Search for how main-renderer IPC is handled in the codebase - look for ChannelCore instantiation
rg -n "new ChannelCore\|instance.*ChannelCore" --type=ts "apps/core-app/src/main" -B3 -A3

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if there's a different pattern for addon modules vs regular modules
find "apps/core-app/src/main/modules/box-tool/addon" -name "*.ts" -exec grep -l "class\|export" {} \; | xargs grep -l "Module\|IPC"

Repository: talex-touch/tuff

Length of output: 343


🏁 Script executed:

#!/bin/bash
# Look for where IPC handlers are typically registered in the main modules
rg -n "ipcMain\.on\|ipcMain\.handle" --type=ts "apps/core-app/src/main/modules/box-tool" -B2 -A1 | head -30

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check the coding guidelines more carefully - verify the exact requirements
cat -n "coding_guidelines" 2>/dev/null || rg -n "Use regChannel\|ChannelType.MAIN" --type=ts --type=md "apps/core-app" | head -20

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look for how the ChannelCore or channel infrastructure is accessed in module context
rg -n "channelBridge\|ChannelCore" --type=ts "apps/core-app/src/main/modules/box-tool/addon" -B3 -A3

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if there's a way to access the centralized channel system from the addon
find "apps/core-app/src/main" -name "*.ts" -path "*/core/*" | xargs grep -l "ChannelCore\|channelBridge" | head -5

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Check if there's documentation or comments about why addon modules use direct ipcMain
grep -r "addon\|ipcMain.handle" "apps/core-app/src/main/modules/box-tool/addon/apps" --include="*.ts" | grep -i "comment\|reason\|why\|note\|TODO" | head -10

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look at the structure of channel-core.ts to understand what regChannel does
sed -n '376,410p' "apps/core-app/src/main/core/channel-core.ts"

Repository: talex-touch/tuff

Length of output: 795


🏁 Script executed:

#!/bin/bash
# Check if ipcMain usage is consistent within box-tool addon - is this a pattern?
for file in apps/core-app/src/main/modules/box-tool/addon/*/; do
  echo "=== Directory: $file ==="
  find "$file" -name "*.ts" -exec grep -l "ipcMain\|regChannel" {} \;
done

Repository: talex-touch/tuff

Length of output: 488


🏁 Script executed:

#!/bin/bash
# Check if ChannelCore is accessible/exported from a central location that addon could import
rg -n "export.*ChannelCore\|export.*channelCore" --type=ts "apps/core-app/src/main" -B1

Repository: talex-touch/tuff

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look at how the main module or core initializes ChannelCore
grep -r "ChannelCore\|channelBridge" "apps/core-app/src/main/core" --include="*.ts" | grep -E "export|instance" | head -10

Repository: talex-touch/tuff

Length of output: 42


Replace ipcMain.handle() with regChannel() per coding guidelines.

The coding guidelines require using regChannel(ChannelType.MAIN, ...) for main-renderer IPC communication in files under apps/core-app/src/main/**/*.ts. This file uses ipcMain.handle() directly (9 handlers), which bypasses the centralized channel management provided by ChannelCore. Import ChannelCore or access the channel infrastructure and refactor all handlers to use the standard pattern.

🤖 Prompt for AI Agents
In `@apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-ipc.ts` around
lines 33 - 42, The file currently uses ipcMain.handle for IPC handlers (e.g., in
registerCustomAppIpc and the handler for CustomAppIpcChannels.GET_ALL that calls
customAppProvider.getAllCustomApps); replace each ipcMain.handle usage with the
project's standard regChannel(ChannelType.MAIN, ...) pattern (import regChannel
and ChannelType/ChannelCore as needed) so all handlers are registered via the
centralized channel infrastructure; ensure each handler's signature and error
handling are preserved when migrating (use regChannel(ChannelType.MAIN,
CustomAppIpcChannels.GET_ALL, async () => { ... })) and remove the direct
ipcMain.handle calls.

Comment on lines +152 to +159
// 更新使用统计
await db
.update(customApps)
.set({
lastUsedAt: Date.now(),
useCount: app.useCount + 1
})
.where(eq(customApps.id, id))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential race condition in useCount increment.

The read-then-increment pattern (app.useCount + 1) can cause lost updates if the app is launched multiple times concurrently. Use a SQL increment expression instead.

Suggested fix using SQL increment
+import { eq, sql } from 'drizzle-orm'
...
     await db
       .update(customApps)
       .set({
         lastUsedAt: Date.now(),
-        useCount: app.useCount + 1
+        useCount: sql`${customApps.useCount} + 1`
       })
       .where(eq(customApps.id, id))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 更新使用统计
await db
.update(customApps)
.set({
lastUsedAt: Date.now(),
useCount: app.useCount + 1
})
.where(eq(customApps.id, id))
// 更新使用统计
await db
.update(customApps)
.set({
lastUsedAt: Date.now(),
useCount: sql`${customApps.useCount} + 1`
})
.where(eq(customApps.id, id))
🤖 Prompt for AI Agents
In `@apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-provider.ts`
around lines 152 - 159, The update currently reads app.useCount and writes
app.useCount + 1, causing a race condition; change the db update for customApps
(the db.update(...).set({...}).where(eq(customApps.id, id)) call) to perform a
database-side increment for useCount (use an SQL increment expression or the
query-builder's increment helper) while still setting lastUsedAt to Date.now(),
so the update is atomic and avoids lost concurrent increments.

Comment on lines +168 to +216
private async launch(app: CustomApp): Promise<void> {
const { path: appPath, launchCommand, workingDirectory, environmentVars } = app

// 如果指定了启动命令,使用命令启动
if (launchCommand) {
const env = environmentVars
? { ...process.env, ...environmentVars }
: process.env

await execAsync(launchCommand, {
cwd: workingDirectory,
env
})
return
}

// 否则根据文件类型决定启动方式
switch (app.fileType) {
case 'jar':
// JAR 文件使用 java -jar 启动
await execAsync(`java -jar "${appPath}"`, {
cwd: workingDirectory
})
break

case 'sh':
case 'bash':
// Shell 脚本
await execAsync(`bash "${appPath}"`, {
cwd: workingDirectory
})
break

case 'py':
// Python 脚本
await execAsync(`python3 "${appPath}"`, {
cwd: workingDirectory
})
break

case 'app':
case 'exe':
case 'other':
default:
// 其他文件使用系统默认程序打开
await shell.openPath(appPath)
break
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Command injection vulnerability in launch method.

Using execAsync with string interpolation allows command injection. A malicious file path like test"; rm -rf /; echo " could execute arbitrary commands. The launchCommand field is also directly executed without sanitization.

Use child_process.spawn with arguments array instead of string concatenation:

🔒 Proposed fix using spawn
-import { exec } from 'node:child_process'
-import { promisify } from 'node:util'
-
-const execAsync = promisify(exec)
+import { spawn } from 'node:child_process'
...
   private async launch(app: CustomApp): Promise<void> {
     const { path: appPath, launchCommand, workingDirectory, environmentVars } = app
 
     if (launchCommand) {
-      const env = environmentVars
-        ? { ...process.env, ...environmentVars }
-        : process.env
-
-      await execAsync(launchCommand, {
-        cwd: workingDirectory,
-        env
-      })
+      // For custom commands, use shell but warn users about security
+      const env = environmentVars ? { ...process.env, ...environmentVars } : process.env
+      const child = spawn(launchCommand, [], { 
+        cwd: workingDirectory, 
+        env, 
+        shell: true,
+        detached: true,
+        stdio: 'ignore'
+      })
+      child.unref()
       return
     }
 
     switch (app.fileType) {
       case 'jar':
-        await execAsync(`java -jar "${appPath}"`, { cwd: workingDirectory })
+        spawn('java', ['-jar', appPath], { cwd: workingDirectory, detached: true, stdio: 'ignore' }).unref()
         break
       case 'sh':
       case 'bash':
-        await execAsync(`bash "${appPath}"`, { cwd: workingDirectory })
+        spawn('bash', [appPath], { cwd: workingDirectory, detached: true, stdio: 'ignore' }).unref()
         break
       case 'py':
-        await execAsync(`python3 "${appPath}"`, { cwd: workingDirectory })
+        spawn('python3', [appPath], { cwd: workingDirectory, detached: true, stdio: 'ignore' }).unref()
         break
🤖 Prompt for AI Agents
In `@apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-provider.ts`
around lines 168 - 216, The launch method currently uses execAsync with
interpolated strings (in launch, cases for 'jar','sh','bash','py' and the
launchCommand path) which allows command injection; change these to use
child_process.spawn (or execFile) with a command and args array instead of
concatenated strings, e.g., spawn('java', ['-jar', appPath], { cwd, env }) for
the 'jar' branch, spawn('bash', [appPath], { cwd }) for shell scripts,
spawn('python3', [appPath], { cwd }) for 'py', and when a user-supplied
launchCommand is provided, parse it into a command and args (or require separate
command + args fields) and call spawn(command, args, { cwd, env }) rather than
passing the raw string to execAsync; remove usage of execAsync for these flows
and keep shell.openPath(appPath) only for cases that truly require opening with
the OS.

Comment on lines +242 to +247
// 添加文件名(不含扩展名)
const fileName = path.basename(displayName, path.extname(displayName))
if (fileName !== displayName) {
keywords.push(fileName.toLowerCase())
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Logic issue: Treating displayName as a file path.

Lines 243-246 use path.basename on displayName, but displayName is a user-provided label (e.g., "My Custom App"), not a file path. If the intent is to include the actual filename in keywords, use the path field from the CustomApp instead.

Suggested fix
-  generateKeywords(displayName: string): string[] {
+  generateKeywords(displayName: string, appPath?: string): string[] {
     const keywords: string[] = []
     keywords.push(displayName.toLowerCase())
     // ... pinyin logic ...
 
-    const fileName = path.basename(displayName, path.extname(displayName))
-    if (fileName !== displayName) {
-      keywords.push(fileName.toLowerCase())
+    if (appPath) {
+      const fileName = path.basename(appPath, path.extname(appPath))
+      keywords.push(fileName.toLowerCase())
     }
🤖 Prompt for AI Agents
In `@apps/core-app/src/main/modules/box-tool/addon/apps/custom-app-provider.ts`
around lines 242 - 247, The current logic treats displayName as a file path by
calling path.basename on it; replace that by pulling the actual file path from
the CustomApp object (e.g., use the CustomApp.path field) and only compute const
fileName = path.basename(customApp.path, path.extname(customApp.path)) when
customApp.path is defined/truthy, then push fileName.toLowerCase() into
keywords; remove the fileName !== displayName comparison and guard against
missing path to avoid calling path.* on a non-path string.

@cloudflare-workers-and-pages
Copy link

Deploying tuff with  Cloudflare Pages  Cloudflare Pages

Latest commit: c2dc7e9
Status:🚫  Build failed.

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants