@muyichengshayu/promptx 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -0
- package/apps/server/src/appPaths.js +102 -0
- package/apps/server/src/codex.js +585 -0
- package/apps/server/src/codexRunRuntime.js +212 -0
- package/apps/server/src/codexRuns.js +525 -0
- package/apps/server/src/codexSessions.js +149 -0
- package/apps/server/src/db.js +389 -0
- package/apps/server/src/gitDiff.js +1425 -0
- package/apps/server/src/index.js +909 -0
- package/apps/server/src/pdf.js +383 -0
- package/apps/server/src/repository.js +484 -0
- package/apps/server/src/sseHub.js +69 -0
- package/apps/server/src/upload.js +22 -0
- package/apps/server/src/workspaceFiles.js +662 -0
- package/apps/web/dist/assets/CodexSessionManagerDialog-c35LrKjV.js +6 -0
- package/apps/web/dist/assets/TaskDiffReviewDialog-BYcla0q4.js +12 -0
- package/apps/web/dist/assets/WorkbenchSettingsDialog-C0uQRStP.js +1 -0
- package/apps/web/dist/assets/WorkbenchView-Cp_qHNdX.js +216 -0
- package/apps/web/dist/assets/index-D9ui_gwj.js +25 -0
- package/apps/web/dist/assets/index-DDNrspNi.css +1 -0
- package/apps/web/dist/index.html +16 -0
- package/bin/promptx.js +60 -0
- package/package.json +58 -0
- package/packages/shared/src/index.js +121 -0
- package/scripts/doctor.mjs +251 -0
- package/scripts/service.mjs +308 -0
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# PromptX
|
|
2
|
+
|
|
3
|
+
PromptX 是一个面向本机 AI 协作的轻量工作台。
|
|
4
|
+
|
|
5
|
+
它适合先整理需求、截图、文本、PDF、禅道 Bug 等上下文,再持续发送给本机 Codex,在同一页里查看执行过程和多轮结果。
|
|
6
|
+
|
|
7
|
+
## 核心能力
|
|
8
|
+
|
|
9
|
+
- 左侧管理任务,中间查看项目执行过程,右侧整理输入内容
|
|
10
|
+
- 支持文本、图片、`md`、`txt`、`pdf`
|
|
11
|
+
- 支持为任务绑定本机项目,并持续复用同一个 Codex 线程
|
|
12
|
+
- 支持查看执行过程、代码变更和最终回复
|
|
13
|
+
- 支持公开页与 Raw 导出
|
|
14
|
+
- 内置禅道 Chrome 扩展,可一键把 Bug 内容带入工作台
|
|
15
|
+
|
|
16
|
+
## 运行前提
|
|
17
|
+
|
|
18
|
+
- 已安装 Node,支持 `20`、`22`、`24`,推荐 `22`
|
|
19
|
+
- 本机可以正常运行 `codex --version`
|
|
20
|
+
- Codex 已开启高权限,并使用满血模式
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @muyichengshayu/promptx
|
|
26
|
+
promptx doctor
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 启动
|
|
30
|
+
|
|
31
|
+
默认地址:
|
|
32
|
+
|
|
33
|
+
- `http://127.0.0.1:3000`
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
promptx start
|
|
37
|
+
promptx status
|
|
38
|
+
promptx stop
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
promptx doctor
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 使用方式
|
|
46
|
+
|
|
47
|
+
1. 打开工作台,新建或选择一个任务
|
|
48
|
+
2. 在右侧整理文本、图片、文件等上下文
|
|
49
|
+
3. 在中间选择一个 PromptX 项目
|
|
50
|
+
4. 点击发送,把当前内容交给 Codex
|
|
51
|
+
5. 在中间继续查看执行过程,并按需多轮发送
|
|
52
|
+
|
|
53
|
+
## 禅道扩展
|
|
54
|
+
|
|
55
|
+
仓库内置了禅道 Chrome 扩展:`apps/zentao-extension`
|
|
56
|
+
|
|
57
|
+
1. 打开 `chrome://extensions`
|
|
58
|
+
2. 开启开发者模式
|
|
59
|
+
3. 点击“加载已解压的扩展程序”
|
|
60
|
+
4. 选择 `apps/zentao-extension`
|
|
61
|
+
|
|
62
|
+
使用时保持 PromptX 已启动,然后在禅道 Bug 详情页点击右下角 `AI修复` 即可。
|
|
63
|
+
|
|
64
|
+
## 注意事项
|
|
65
|
+
|
|
66
|
+
- 当前只支持 Codex,不支持其他模型后端
|
|
67
|
+
- 当前以本机单用户使用为主,不包含账号体系和团队权限
|
|
68
|
+
- 默认仅监听本机地址;如需跨设备访问,建议通过 Tailscale
|
|
69
|
+
- 如果 Codex 运行在受限权限下,文件读写和自动修改能力会明显受限
|
|
70
|
+
|
|
71
|
+
## 本地数据目录
|
|
72
|
+
|
|
73
|
+
运行数据默认保存在 `~/.promptx/`,包含:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
data/
|
|
77
|
+
uploads/
|
|
78
|
+
tmp/
|
|
79
|
+
run/
|
|
80
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
const serverRootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
7
|
+
const defaultPromptxHomeDir = path.join(os.homedir(), '.promptx')
|
|
8
|
+
|
|
9
|
+
let preparedStorageKey = ''
|
|
10
|
+
|
|
11
|
+
function directoryHasEntries(targetPath = '') {
|
|
12
|
+
try {
|
|
13
|
+
return fs.readdirSync(targetPath).length > 0
|
|
14
|
+
} catch {
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function moveDirectoryContents(sourceDir, targetDir) {
|
|
20
|
+
if (!fs.existsSync(sourceDir) || !directoryHasEntries(sourceDir)) {
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (fs.existsSync(targetDir) && directoryHasEntries(targetDir)) {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fs.mkdirSync(path.dirname(targetDir), { recursive: true })
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
fs.renameSync(sourceDir, targetDir)
|
|
32
|
+
return true
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (error?.code !== 'EXDEV') {
|
|
35
|
+
throw error
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fs.mkdirSync(targetDir, { recursive: true })
|
|
40
|
+
fs.cpSync(sourceDir, targetDir, { recursive: true })
|
|
41
|
+
fs.rmSync(sourceDir, { recursive: true, force: true })
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolvePromptxPaths() {
|
|
46
|
+
const promptxHomeDir = process.env.PROMPTX_HOME
|
|
47
|
+
? path.resolve(process.env.PROMPTX_HOME)
|
|
48
|
+
: defaultPromptxHomeDir
|
|
49
|
+
|
|
50
|
+
const dataDir = process.env.PROMPTX_DATA_DIR
|
|
51
|
+
? path.resolve(process.env.PROMPTX_DATA_DIR)
|
|
52
|
+
: path.join(promptxHomeDir, 'data')
|
|
53
|
+
|
|
54
|
+
const uploadsDir = process.env.PROMPTX_UPLOADS_DIR
|
|
55
|
+
? path.resolve(process.env.PROMPTX_UPLOADS_DIR)
|
|
56
|
+
: path.join(promptxHomeDir, 'uploads')
|
|
57
|
+
|
|
58
|
+
const tmpDir = process.env.PROMPTX_TMP_DIR
|
|
59
|
+
? path.resolve(process.env.PROMPTX_TMP_DIR)
|
|
60
|
+
: path.join(promptxHomeDir, 'tmp')
|
|
61
|
+
|
|
62
|
+
return { promptxHomeDir, dataDir, uploadsDir, tmpDir }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function ensurePromptxStorageReady() {
|
|
66
|
+
const resolved = resolvePromptxPaths()
|
|
67
|
+
const storageKey = JSON.stringify(resolved)
|
|
68
|
+
|
|
69
|
+
if (preparedStorageKey === storageKey) {
|
|
70
|
+
return resolved
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fs.mkdirSync(resolved.promptxHomeDir, { recursive: true })
|
|
74
|
+
|
|
75
|
+
const migrationPlans = [
|
|
76
|
+
process.env.PROMPTX_DATA_DIR
|
|
77
|
+
? null
|
|
78
|
+
: { source: path.join(serverRootDir, 'data'), target: resolved.dataDir },
|
|
79
|
+
process.env.PROMPTX_UPLOADS_DIR
|
|
80
|
+
? null
|
|
81
|
+
: { source: path.join(serverRootDir, 'uploads'), target: resolved.uploadsDir },
|
|
82
|
+
process.env.PROMPTX_TMP_DIR
|
|
83
|
+
? null
|
|
84
|
+
: { source: path.join(serverRootDir, 'tmp'), target: resolved.tmpDir },
|
|
85
|
+
].filter(Boolean)
|
|
86
|
+
|
|
87
|
+
migrationPlans.forEach(({ source, target }) => {
|
|
88
|
+
moveDirectoryContents(source, target)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
fs.mkdirSync(resolved.dataDir, { recursive: true })
|
|
92
|
+
fs.mkdirSync(resolved.uploadsDir, { recursive: true })
|
|
93
|
+
fs.mkdirSync(resolved.tmpDir, { recursive: true })
|
|
94
|
+
|
|
95
|
+
preparedStorageKey = storageKey
|
|
96
|
+
return resolved
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
serverRootDir,
|
|
101
|
+
resolvePromptxPaths,
|
|
102
|
+
}
|