@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 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
+ }