@muyichengshayu/promptx 0.1.4 → 0.1.6
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/CHANGELOG.md +37 -0
- package/README.md +21 -0
- package/apps/server/src/codexRuns.js +51 -4
- package/apps/server/src/db.js +6 -0
- package/apps/server/src/index.js +51 -13
- package/apps/server/src/relayClient.js +431 -0
- package/apps/server/src/relayConfig.js +84 -0
- package/apps/server/src/relayProtocol.js +143 -0
- package/apps/server/src/relayServer.js +823 -0
- package/apps/server/src/relayTenants.js +228 -0
- package/apps/web/dist/assets/CodexSessionManagerDialog-E16PIzLB.js +1 -0
- package/apps/web/dist/assets/{TaskDiffReviewDialog-Twev0IGz.js → TaskDiffReviewDialog-MsxH_AKt.js} +1 -1
- package/apps/web/dist/assets/WorkbenchSettingsDialog-CEYbW3cO.js +21 -0
- package/apps/web/dist/assets/WorkbenchView-iQBWLLDq.js +216 -0
- package/apps/web/dist/assets/index-C2HSUDwd.css +1 -0
- package/apps/web/dist/assets/index-NZ5CT31R.js +25 -0
- package/apps/web/dist/assets/info-C7kzOdmJ.js +6 -0
- package/apps/web/dist/index.html +2 -2
- package/bin/promptx.js +24 -1
- package/docs/relay-quickstart.md +241 -0
- package/package.json +10 -3
- package/scripts/relay-service.mjs +301 -0
- package/scripts/relay-tenant.mjs +133 -0
- package/scripts/relay.mjs +12 -0
- package/apps/web/dist/assets/CodexSessionManagerDialog-BpDNRA83.js +0 -6
- package/apps/web/dist/assets/WorkbenchSettingsDialog-r5bqLsmQ.js +0 -1
- package/apps/web/dist/assets/WorkbenchView-361Ud4iT.js +0 -216
- package/apps/web/dist/assets/index-CJNRF_I8.js +0 -25
- package/apps/web/dist/assets/index-DDNrspNi.css +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.6
|
|
4
|
+
|
|
5
|
+
- 新增多租户 Relay 子域名接入能力,一个 Relay 进程即可同时服务多个同事的远程访问。
|
|
6
|
+
- 新增 `promptx relay tenant add/list/remove` 与 `promptx relay start/stop/restart/status`,补齐 Relay 的租户管理和后台运维命令。
|
|
7
|
+
- 完善 Relay 转发稳定性与诊断信息,修复远程请求体转发问题,并增加更清晰的 host、tenant 与拒绝原因日志。
|
|
8
|
+
- README 精简为入口说明,详细 Relay 部署与使用流程迁移到 `docs/relay-quickstart.md`。
|
|
9
|
+
- 修复 Windows 开发环境下 `pnpm dev` / `pnpm dev:tailscale` 可能报 `spawn EINVAL` 的问题。
|
|
10
|
+
|
|
11
|
+
## 0.1.5
|
|
12
|
+
|
|
13
|
+
- 本轮提示词支持按新数据结构展示图片块,新的图文输入在会话区可直接看到附图预览。
|
|
14
|
+
- 修复旧库升级时 `prompt_blocks_json` 字段缺失导致服务启动报错的问题,补齐增量迁移。
|
|
15
|
+
- 优化项目管理交互:运行中也可打开“管理项目”,管理弹窗优先选中当前项目,编辑表单能正确回填。
|
|
16
|
+
- 简化项目选择器与项目列表状态展示,减少无意义抖动和过重高亮;新建任务按钮不再因列表加载短暂禁用。
|
|
17
|
+
|
|
18
|
+
## 0.1.4
|
|
19
|
+
|
|
20
|
+
- 修复 Windows 下通过 npm 安装后的正式版在启动服务、发送请求和执行辅助命令时频繁弹出黑色控制台窗口的问题。
|
|
21
|
+
- 为 PromptX、Codex、Git 与发布检查相关的 Windows 子进程统一补充隐藏窗口选项,减少系统级弹窗干扰。
|
|
22
|
+
|
|
23
|
+
## 0.1.3
|
|
24
|
+
|
|
25
|
+
- 修复工作台输入区在输入法候选、实时刷新和快速发送场景下的尾字丢失问题。
|
|
26
|
+
- 修复设置面板首次打开时版本信息不加载、一直停留在“读取中...”的问题。
|
|
27
|
+
|
|
28
|
+
## 0.1.2
|
|
29
|
+
|
|
30
|
+
- 设置面板增加版本信息展示,并在读取失败时显示明确状态。
|
|
31
|
+
- CLI 新增 `version`、`--version`、`-v` 版本查询,同时兼容 `-versioin`。
|
|
32
|
+
- README 补充说明:禅道扩展目前需要下载或克隆仓库源码后手动加载。
|
|
33
|
+
|
|
34
|
+
## 0.1.1
|
|
35
|
+
|
|
36
|
+
- 修复工作台中“代码变更”入口、执行过程“查看”按钮和任务卡文件数徽标的闪烁问题。
|
|
37
|
+
- 优化任务列表刷新时的代码变更摘要复用逻辑,减少界面抖动。
|
package/README.md
CHANGED
|
@@ -36,12 +36,18 @@ promptx doctor
|
|
|
36
36
|
promptx start
|
|
37
37
|
promptx status
|
|
38
38
|
promptx stop
|
|
39
|
+
promptx relay start
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
```bash
|
|
42
43
|
promptx doctor
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
其中:
|
|
47
|
+
|
|
48
|
+
- `promptx start`:启动本机 PromptX 工作台
|
|
49
|
+
- `promptx relay start`:启动公网中转服务,适合部署到你自己的云服务器
|
|
50
|
+
|
|
45
51
|
## 使用方式
|
|
46
52
|
|
|
47
53
|
1. 打开工作台,新建或选择一个任务
|
|
@@ -50,6 +56,21 @@ promptx doctor
|
|
|
50
56
|
4. 点击发送,把当前内容交给 Codex
|
|
51
57
|
5. 在中间继续查看执行过程,并按需多轮发送
|
|
52
58
|
|
|
59
|
+
## 远程访问 Relay(预览)
|
|
60
|
+
|
|
61
|
+
如果你希望在手机上远程访问自己电脑上的 PromptX,或想在云端部署多租户 Relay,请直接查看:
|
|
62
|
+
|
|
63
|
+
- `docs/relay-quickstart.md`
|
|
64
|
+
|
|
65
|
+
文档里已经整理好这些内容:
|
|
66
|
+
|
|
67
|
+
- 本地 PromptX 接入 Relay
|
|
68
|
+
- 云端 Relay 启动与后台管理
|
|
69
|
+
- 多租户子域名接入
|
|
70
|
+
- `promptx relay tenant add/list/remove`
|
|
71
|
+
- `promptx relay start/stop/restart/status`
|
|
72
|
+
- Nginx、DNS、健康检查与常见排查
|
|
73
|
+
|
|
53
74
|
## 禅道扩展
|
|
54
75
|
|
|
55
76
|
仓库内置了禅道 Chrome 扩展:`apps/zentao-extension`
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid'
|
|
2
|
+
import { BLOCK_TYPES, clampText } from '../../../packages/shared/src/index.js'
|
|
2
3
|
import { all, get, run, transaction } from './db.js'
|
|
3
4
|
import { getPromptxCodexSessionById } from './codexSessions.js'
|
|
4
5
|
import { captureRunGitBaseline, captureRunGitFinalSnapshot, captureTaskGitBaseline } from './gitDiff.js'
|
|
@@ -18,6 +19,48 @@ function parseEventPayload(rawValue = '{}') {
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
function parsePromptBlocks(rawValue = '[]') {
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(rawValue || '[]')
|
|
25
|
+
return Array.isArray(parsed) ? parsed.filter(Boolean) : []
|
|
26
|
+
} catch {
|
|
27
|
+
return []
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function normalizePromptBlock(block = {}) {
|
|
32
|
+
const type =
|
|
33
|
+
block.type === BLOCK_TYPES.IMAGE
|
|
34
|
+
? BLOCK_TYPES.IMAGE
|
|
35
|
+
: block.type === BLOCK_TYPES.IMPORTED_TEXT
|
|
36
|
+
? BLOCK_TYPES.IMPORTED_TEXT
|
|
37
|
+
: BLOCK_TYPES.TEXT
|
|
38
|
+
|
|
39
|
+
const content = clampText(
|
|
40
|
+
String(block.content || ''),
|
|
41
|
+
type === BLOCK_TYPES.IMAGE ? 1000 : 50000
|
|
42
|
+
)
|
|
43
|
+
const meta =
|
|
44
|
+
type === BLOCK_TYPES.IMPORTED_TEXT
|
|
45
|
+
? {
|
|
46
|
+
fileName: clampText(block.meta?.fileName || '', 180),
|
|
47
|
+
collapsed: Boolean(block.meta?.collapsed),
|
|
48
|
+
}
|
|
49
|
+
: {}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
type,
|
|
53
|
+
content,
|
|
54
|
+
meta,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizePromptBlocks(blocks = []) {
|
|
59
|
+
return Array.isArray(blocks)
|
|
60
|
+
? blocks.map((block) => normalizePromptBlock(block)).filter(Boolean)
|
|
61
|
+
: []
|
|
62
|
+
}
|
|
63
|
+
|
|
21
64
|
function toCodexRunEvent(row) {
|
|
22
65
|
return {
|
|
23
66
|
id: Number(row.id),
|
|
@@ -38,6 +81,7 @@ function toCodexRun(row, events = []) {
|
|
|
38
81
|
taskSlug: row.task_slug,
|
|
39
82
|
sessionId: row.session_id,
|
|
40
83
|
prompt: row.prompt || '',
|
|
84
|
+
promptBlocks: parsePromptBlocks(row.prompt_blocks_json),
|
|
41
85
|
status: row.status || 'running',
|
|
42
86
|
responseMessage: row.response_message || '',
|
|
43
87
|
errorMessage: row.error_message || '',
|
|
@@ -100,7 +144,7 @@ function getRunRowById(runId) {
|
|
|
100
144
|
}
|
|
101
145
|
|
|
102
146
|
return get(
|
|
103
|
-
`SELECT id, task_slug, session_id, prompt, status, response_message, error_message, created_at, updated_at, started_at, finished_at
|
|
147
|
+
`SELECT id, task_slug, session_id, prompt, prompt_blocks_json, status, response_message, error_message, created_at, updated_at, started_at, finished_at
|
|
104
148
|
FROM codex_runs
|
|
105
149
|
WHERE id = ?`,
|
|
106
150
|
[targetId]
|
|
@@ -208,6 +252,7 @@ export function listTaskCodexRunsWithOptions(taskSlug, options = {}) {
|
|
|
208
252
|
runs.task_slug,
|
|
209
253
|
runs.session_id,
|
|
210
254
|
runs.prompt,
|
|
255
|
+
runs.prompt_blocks_json,
|
|
211
256
|
runs.status,
|
|
212
257
|
runs.response_message,
|
|
213
258
|
runs.error_message,
|
|
@@ -227,6 +272,7 @@ export function listTaskCodexRunsWithOptions(taskSlug, options = {}) {
|
|
|
227
272
|
runs.task_slug,
|
|
228
273
|
runs.session_id,
|
|
229
274
|
runs.prompt,
|
|
275
|
+
runs.prompt_blocks_json,
|
|
230
276
|
runs.status,
|
|
231
277
|
runs.response_message,
|
|
232
278
|
runs.error_message,
|
|
@@ -271,6 +317,7 @@ export function createCodexRun(input = {}) {
|
|
|
271
317
|
const taskSlug = String(input.taskSlug || '').trim()
|
|
272
318
|
const sessionId = String(input.sessionId || '').trim()
|
|
273
319
|
const prompt = String(input.prompt || '').trim()
|
|
320
|
+
const promptBlocks = normalizePromptBlocks(input.promptBlocks)
|
|
274
321
|
|
|
275
322
|
if (!taskSlug) {
|
|
276
323
|
throw new Error('缺少任务。')
|
|
@@ -298,11 +345,11 @@ export function createCodexRun(input = {}) {
|
|
|
298
345
|
transaction(() => {
|
|
299
346
|
run(
|
|
300
347
|
`INSERT INTO codex_runs (
|
|
301
|
-
id, task_slug, session_id, prompt, status,
|
|
348
|
+
id, task_slug, session_id, prompt, prompt_blocks_json, status,
|
|
302
349
|
response_message, error_message, created_at, updated_at, started_at, finished_at
|
|
303
350
|
)
|
|
304
|
-
VALUES (?, ?, ?, ?, 'running', '', '', ?, ?, ?, NULL)`,
|
|
305
|
-
[runId, task.slug, session.id, prompt, now, now, now]
|
|
351
|
+
VALUES (?, ?, ?, ?, ?, 'running', '', '', ?, ?, ?, NULL)`,
|
|
352
|
+
[runId, task.slug, session.id, prompt, JSON.stringify(promptBlocks), now, now, now]
|
|
306
353
|
)
|
|
307
354
|
})
|
|
308
355
|
|
package/apps/server/src/db.js
CHANGED
|
@@ -206,6 +206,7 @@ function migrateToV1() {
|
|
|
206
206
|
task_slug TEXT NOT NULL,
|
|
207
207
|
session_id TEXT NOT NULL,
|
|
208
208
|
prompt TEXT NOT NULL DEFAULT '',
|
|
209
|
+
prompt_blocks_json TEXT NOT NULL DEFAULT '[]',
|
|
209
210
|
status TEXT NOT NULL,
|
|
210
211
|
response_message TEXT NOT NULL DEFAULT '',
|
|
211
212
|
error_message TEXT NOT NULL DEFAULT '',
|
|
@@ -295,11 +296,14 @@ function migrateToV1() {
|
|
|
295
296
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_run_git_final_snapshot_entries_scope_path
|
|
296
297
|
ON run_git_final_snapshot_entries(run_id, path);
|
|
297
298
|
`)
|
|
299
|
+
}
|
|
298
300
|
|
|
301
|
+
function applyAdditiveSchemaPatches() {
|
|
299
302
|
const alterStatements = [
|
|
300
303
|
`ALTER TABLE tasks ADD COLUMN auto_title TEXT NOT NULL DEFAULT ''`,
|
|
301
304
|
`ALTER TABLE tasks ADD COLUMN last_prompt_preview TEXT NOT NULL DEFAULT ''`,
|
|
302
305
|
`ALTER TABLE tasks ADD COLUMN codex_session_id TEXT NOT NULL DEFAULT ''`,
|
|
306
|
+
`ALTER TABLE codex_runs ADD COLUMN prompt_blocks_json TEXT NOT NULL DEFAULT '[]'`,
|
|
303
307
|
`ALTER TABLE task_git_baselines ADD COLUMN branch_label TEXT NOT NULL DEFAULT ''`,
|
|
304
308
|
`ALTER TABLE run_git_baselines ADD COLUMN branch_label TEXT NOT NULL DEFAULT ''`,
|
|
305
309
|
`ALTER TABLE codex_run_events ADD COLUMN event_type TEXT NOT NULL DEFAULT 'event'`,
|
|
@@ -329,6 +333,8 @@ function ensureSchema() {
|
|
|
329
333
|
writeSchemaVersion(1)
|
|
330
334
|
}
|
|
331
335
|
|
|
336
|
+
applyAdditiveSchemaPatches()
|
|
337
|
+
|
|
332
338
|
if (readSchemaVersion() < SCHEMA_VERSION) {
|
|
333
339
|
writeSchemaVersion(SCHEMA_VERSION)
|
|
334
340
|
}
|
package/apps/server/src/index.js
CHANGED
|
@@ -57,6 +57,8 @@ import {
|
|
|
57
57
|
searchWorkspaceEntries,
|
|
58
58
|
} from './workspaceFiles.js'
|
|
59
59
|
import { ensurePromptxStorageReady, serverRootDir } from './appPaths.js'
|
|
60
|
+
import { createRelayClient } from './relayClient.js'
|
|
61
|
+
import { getRelayConfigForClient, isRelayConfigManagedByEnv, writeStoredRelayConfig } from './relayConfig.js'
|
|
60
62
|
import { createSseHub } from './sseHub.js'
|
|
61
63
|
|
|
62
64
|
const app = Fastify({ logger: true })
|
|
@@ -80,6 +82,13 @@ function readPromptxVersion() {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
const promptxVersion = readPromptxVersion()
|
|
85
|
+
const relayConfig = getRelayConfigForClient()
|
|
86
|
+
const relayClient = createRelayClient({
|
|
87
|
+
logger: app.log,
|
|
88
|
+
appVersion: promptxVersion,
|
|
89
|
+
localBaseUrl: process.env.PROMPTX_RELAY_LOCAL_BASE_URL || `http://127.0.0.1:${port}`,
|
|
90
|
+
...relayConfig,
|
|
91
|
+
})
|
|
83
92
|
|
|
84
93
|
let lastExpiredPurgeAt = 0
|
|
85
94
|
const sseHub = createSseHub()
|
|
@@ -338,6 +347,32 @@ app.get('/api/meta', async () => ({
|
|
|
338
347
|
visibilityOptions: VISIBILITY_OPTIONS,
|
|
339
348
|
}))
|
|
340
349
|
|
|
350
|
+
app.get('/api/relay/status', async () => ({
|
|
351
|
+
relay: relayClient.getStatus(),
|
|
352
|
+
}))
|
|
353
|
+
|
|
354
|
+
app.get('/api/relay/config', async () => ({
|
|
355
|
+
config: {
|
|
356
|
+
...getRelayConfigForClient(),
|
|
357
|
+
},
|
|
358
|
+
managedByEnv: isRelayConfigManagedByEnv(),
|
|
359
|
+
relay: relayClient.getStatus(),
|
|
360
|
+
}))
|
|
361
|
+
|
|
362
|
+
app.put('/api/relay/config', async (request) => {
|
|
363
|
+
const savedConfig = writeStoredRelayConfig(request.body || {})
|
|
364
|
+
relayClient.updateConfig({
|
|
365
|
+
...savedConfig,
|
|
366
|
+
localBaseUrl: process.env.PROMPTX_RELAY_LOCAL_BASE_URL || `http://127.0.0.1:${port}`,
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
config: getRelayConfigForClient(),
|
|
371
|
+
managedByEnv: isRelayConfigManagedByEnv(),
|
|
372
|
+
relay: relayClient.getStatus(),
|
|
373
|
+
}
|
|
374
|
+
})
|
|
375
|
+
|
|
341
376
|
app.get('/api/events/stream', async (request, reply) => {
|
|
342
377
|
reply.hijack()
|
|
343
378
|
const requestOrigin = request.headers.origin
|
|
@@ -522,8 +557,9 @@ app.post('/api/tasks/:slug/codex-runs', async (request, reply) => {
|
|
|
522
557
|
return reply.code(404).send({ message: '任务不存在。' })
|
|
523
558
|
}
|
|
524
559
|
|
|
525
|
-
const sessionId = String(request.body?.sessionId || '').trim()
|
|
526
|
-
const prompt = String(request.body?.prompt || '').trim()
|
|
560
|
+
const sessionId = String(request.body?.sessionId || '').trim()
|
|
561
|
+
const prompt = String(request.body?.prompt || '').trim()
|
|
562
|
+
const promptBlocks = Array.isArray(request.body?.promptBlocks) ? request.body.promptBlocks : []
|
|
527
563
|
|
|
528
564
|
if (!sessionId) {
|
|
529
565
|
return reply.code(400).send({ message: '请先选择一个 PromptX 项目。' })
|
|
@@ -542,11 +578,12 @@ app.post('/api/tasks/:slug/codex-runs', async (request, reply) => {
|
|
|
542
578
|
return reply.code(409).send({ message: '当前项目正在执行中,请等待完成后再发送。' })
|
|
543
579
|
}
|
|
544
580
|
|
|
545
|
-
const runRecord = createCodexRun({
|
|
546
|
-
taskSlug: request.params.slug,
|
|
547
|
-
sessionId,
|
|
548
|
-
prompt,
|
|
549
|
-
|
|
581
|
+
const runRecord = createCodexRun({
|
|
582
|
+
taskSlug: request.params.slug,
|
|
583
|
+
sessionId,
|
|
584
|
+
prompt,
|
|
585
|
+
promptBlocks,
|
|
586
|
+
})
|
|
550
587
|
|
|
551
588
|
updateTaskCodexSession(request.params.slug, sessionId)
|
|
552
589
|
codexRunRuntime.start(runRecord)
|
|
@@ -913,10 +950,11 @@ app.setErrorHandler((error, request, reply) => {
|
|
|
913
950
|
markInterruptedCodexRuns()
|
|
914
951
|
purgeExpiredContent(true)
|
|
915
952
|
|
|
916
|
-
app.listen({ port, host }).then(() => {
|
|
917
|
-
app.log.info(`server running at http://${host}:${port}`)
|
|
918
|
-
buildServerAccessUrls(host, port).forEach((message) => {
|
|
919
|
-
app.log.info(message)
|
|
920
|
-
})
|
|
921
|
-
|
|
953
|
+
app.listen({ port, host }).then(() => {
|
|
954
|
+
app.log.info(`server running at http://${host}:${port}`)
|
|
955
|
+
buildServerAccessUrls(host, port).forEach((message) => {
|
|
956
|
+
app.log.info(message)
|
|
957
|
+
})
|
|
958
|
+
relayClient.start()
|
|
959
|
+
})
|
|
922
960
|
|