@muyichengshayu/promptx 0.2.13 → 0.2.15
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 +11 -0
- package/apps/server/src/agentSessionDiscovery.js +180 -7
- package/apps/web/dist/assets/{CodexSessionManagerDialog-Dic9kMHK.js → CodexSessionManagerDialog-y7O-JTxP.js} +1 -1
- package/apps/web/dist/assets/{TaskDiffReviewDialog-CKiZdXqi.js → TaskDiffReviewDialog-CTr_zoAn.js} +1 -1
- package/apps/web/dist/assets/{WorkbenchSettingsDialog-CP0z90bm.js → WorkbenchSettingsDialog-Bf2DCuN_.js} +1 -1
- package/apps/web/dist/assets/{WorkbenchView-D1oxqNr4.css → WorkbenchView-CK1snPBz.css} +1 -1
- package/apps/web/dist/assets/WorkbenchView-Gq3mmtsK.js +60 -0
- package/apps/web/dist/assets/index-Co1Ssha9.js +2 -0
- package/apps/web/dist/index.html +1 -1
- package/package.json +21 -14
- package/apps/runner/src/engines/claudeCodeRunner.test.js +0 -467
- package/apps/runner/src/engines/kimiCodeRunner.test.js +0 -127
- package/apps/runner/src/engines/openCodeRunner.test.js +0 -236
- package/apps/runner/src/engines/runnerContract.test.js +0 -449
- package/apps/runner/src/engines/shellRunner.test.js +0 -46
- package/apps/runner/src/runManager.test.js +0 -913
- package/apps/runner/src/serverClient.test.js +0 -93
- package/apps/server/src/agentSessionDiscovery.test.js +0 -186
- package/apps/server/src/appPaths.test.js +0 -52
- package/apps/server/src/assetRoutes.test.js +0 -168
- package/apps/server/src/codex.test.js +0 -518
- package/apps/server/src/codexRoutes.test.js +0 -376
- package/apps/server/src/codexRuns.test.js +0 -160
- package/apps/server/src/codexSessions.test.js +0 -369
- package/apps/server/src/db.test.js +0 -182
- package/apps/server/src/gitDiff.test.js +0 -542
- package/apps/server/src/gitDiffClient.test.js +0 -140
- package/apps/server/src/internalRoutes.test.js +0 -134
- package/apps/server/src/maintenance.test.js +0 -154
- package/apps/server/src/processControl.test.js +0 -147
- package/apps/server/src/relayClient.test.js +0 -478
- package/apps/server/src/relayConfig.test.js +0 -73
- package/apps/server/src/relayProtocol.test.js +0 -49
- package/apps/server/src/relayServer.test.js +0 -798
- package/apps/server/src/relayTenants.test.js +0 -137
- package/apps/server/src/relayUsageStore.test.js +0 -65
- package/apps/server/src/repository.test.js +0 -150
- package/apps/server/src/runDispatchService.test.js +0 -563
- package/apps/server/src/runEventIngest.test.js +0 -225
- package/apps/server/src/runRecovery.test.js +0 -73
- package/apps/server/src/runnerClient.test.js +0 -80
- package/apps/server/src/runnerDispatch.test.js +0 -136
- package/apps/server/src/systemConfig.test.js +0 -112
- package/apps/server/src/systemRoutes.test.js +0 -319
- package/apps/server/src/taskRoutes.test.js +0 -775
- package/apps/server/src/upload.test.js +0 -30
- package/apps/server/src/webAppRoutes.test.js +0 -67
- package/apps/server/src/workspaceFiles.test.js +0 -279
- package/apps/web/dist/assets/WorkbenchView-noayQwj4.js +0 -60
- package/apps/web/dist/assets/index-HLkdzIYF.js +0 -2
- package/packages/shared/src/dailyLogStream.test.js +0 -29
- package/packages/shared/src/shellCommands.test.js +0 -45
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
import os from 'node:os'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import test from 'node:test'
|
|
6
|
-
|
|
7
|
-
test('未启动的项目允许切换执行引擎,已启动后不允许', async () => {
|
|
8
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-codex-sessions-'))
|
|
9
|
-
const originalCwd = process.cwd()
|
|
10
|
-
const originalDataDir = process.env.PROMPTX_DATA_DIR
|
|
11
|
-
const dataDir = path.join(tempDir, 'data')
|
|
12
|
-
const workspaceDir = path.join(tempDir, 'workspace')
|
|
13
|
-
|
|
14
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
15
|
-
fs.mkdirSync(workspaceDir, { recursive: true })
|
|
16
|
-
process.chdir(tempDir)
|
|
17
|
-
process.env.PROMPTX_DATA_DIR = dataDir
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const { createPromptxCodexSession, updatePromptxCodexSession } = await import(`./codexSessions.js?test=${Date.now()}`)
|
|
21
|
-
|
|
22
|
-
const created = createPromptxCodexSession({
|
|
23
|
-
title: 'Engine Switch Test',
|
|
24
|
-
cwd: workspaceDir,
|
|
25
|
-
engine: 'codex',
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
const switched = updatePromptxCodexSession(created.id, {
|
|
29
|
-
engine: 'opencode',
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
assert.equal(switched?.engine, 'opencode')
|
|
33
|
-
|
|
34
|
-
const started = updatePromptxCodexSession(created.id, {
|
|
35
|
-
codexThreadId: 'thread-1',
|
|
36
|
-
engineThreadId: 'thread-1',
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
assert.equal(started?.started, true)
|
|
40
|
-
|
|
41
|
-
assert.throws(() => {
|
|
42
|
-
updatePromptxCodexSession(created.id, {
|
|
43
|
-
engine: 'claude-code',
|
|
44
|
-
})
|
|
45
|
-
}, /不能直接切换执行引擎/)
|
|
46
|
-
} finally {
|
|
47
|
-
process.chdir(originalCwd)
|
|
48
|
-
if (typeof originalDataDir === 'string') {
|
|
49
|
-
process.env.PROMPTX_DATA_DIR = originalDataDir
|
|
50
|
-
} else {
|
|
51
|
-
delete process.env.PROMPTX_DATA_DIR
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
test('手动填写会话 ID 后,项目在真正运行前仍允许修改,运行后锁定', async () => {
|
|
57
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-codex-sessions-manual-'))
|
|
58
|
-
const originalCwd = process.cwd()
|
|
59
|
-
const originalDataDir = process.env.PROMPTX_DATA_DIR
|
|
60
|
-
const dataDir = path.join(tempDir, 'data')
|
|
61
|
-
const workspaceDirA = path.join(tempDir, 'workspace-a')
|
|
62
|
-
const workspaceDirB = path.join(tempDir, 'workspace-b')
|
|
63
|
-
|
|
64
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
65
|
-
fs.mkdirSync(workspaceDirA, { recursive: true })
|
|
66
|
-
fs.mkdirSync(workspaceDirB, { recursive: true })
|
|
67
|
-
process.chdir(tempDir)
|
|
68
|
-
process.env.PROMPTX_DATA_DIR = dataDir
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const { createPromptxCodexSession, updatePromptxCodexSession } = await import(`./codexSessions.js?test=${Date.now()}`)
|
|
72
|
-
|
|
73
|
-
const created = createPromptxCodexSession({
|
|
74
|
-
title: 'Resume Existing Session',
|
|
75
|
-
cwd: workspaceDirA,
|
|
76
|
-
engine: 'codex',
|
|
77
|
-
sessionId: 'thread-manual-1',
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
assert.equal(created?.started, false)
|
|
81
|
-
assert.equal(created?.sessionId, 'thread-manual-1')
|
|
82
|
-
assert.equal(created?.engineMeta?.manualSessionBinding, true)
|
|
83
|
-
|
|
84
|
-
const updatedBeforeStart = updatePromptxCodexSession(created.id, {
|
|
85
|
-
cwd: workspaceDirB,
|
|
86
|
-
engine: 'claude-code',
|
|
87
|
-
sessionId: 'claude-session-2',
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
assert.equal(updatedBeforeStart?.started, false)
|
|
91
|
-
assert.equal(updatedBeforeStart?.cwd, workspaceDirB)
|
|
92
|
-
assert.equal(updatedBeforeStart?.engine, 'claude-code')
|
|
93
|
-
assert.equal(updatedBeforeStart?.sessionId, 'claude-session-2')
|
|
94
|
-
assert.equal(updatedBeforeStart?.engineSessionId, 'claude-session-2')
|
|
95
|
-
assert.equal(updatedBeforeStart?.engineThreadId, 'claude-session-2')
|
|
96
|
-
|
|
97
|
-
const started = updatePromptxCodexSession(created.id, {
|
|
98
|
-
engineSessionId: 'claude-session-2',
|
|
99
|
-
engineThreadId: 'claude-session-2',
|
|
100
|
-
clearManualSessionBinding: true,
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
assert.equal(started?.started, true)
|
|
104
|
-
assert.equal(started?.engineMeta?.manualSessionBinding, undefined)
|
|
105
|
-
|
|
106
|
-
assert.throws(() => {
|
|
107
|
-
updatePromptxCodexSession(created.id, {
|
|
108
|
-
sessionId: 'claude-session-3',
|
|
109
|
-
})
|
|
110
|
-
}, /不能直接修改会话 ID/)
|
|
111
|
-
|
|
112
|
-
assert.throws(() => {
|
|
113
|
-
updatePromptxCodexSession(created.id, {
|
|
114
|
-
cwd: workspaceDirA,
|
|
115
|
-
})
|
|
116
|
-
}, /不能直接修改工作目录/)
|
|
117
|
-
|
|
118
|
-
assert.throws(() => {
|
|
119
|
-
updatePromptxCodexSession(created.id, {
|
|
120
|
-
engine: 'opencode',
|
|
121
|
-
})
|
|
122
|
-
}, /不能直接切换执行引擎/)
|
|
123
|
-
} finally {
|
|
124
|
-
process.chdir(originalCwd)
|
|
125
|
-
if (typeof originalDataDir === 'string') {
|
|
126
|
-
process.env.PROMPTX_DATA_DIR = originalDataDir
|
|
127
|
-
} else {
|
|
128
|
-
delete process.env.PROMPTX_DATA_DIR
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
test('项目可以挂多个 agent,并且列表只返回 root 项目', async () => {
|
|
134
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-codex-sessions-multi-'))
|
|
135
|
-
const originalCwd = process.cwd()
|
|
136
|
-
const originalDataDir = process.env.PROMPTX_DATA_DIR
|
|
137
|
-
const dataDir = path.join(tempDir, 'data')
|
|
138
|
-
const workspaceDir = path.join(tempDir, 'workspace')
|
|
139
|
-
|
|
140
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
141
|
-
fs.mkdirSync(workspaceDir, { recursive: true })
|
|
142
|
-
process.chdir(tempDir)
|
|
143
|
-
process.env.PROMPTX_DATA_DIR = dataDir
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
const {
|
|
147
|
-
createPromptxCodexSession,
|
|
148
|
-
getPromptxCodexSessionById,
|
|
149
|
-
listPromptxCodexSessions,
|
|
150
|
-
updatePromptxCodexSession,
|
|
151
|
-
} = await import(`./codexSessions.js?test=${Date.now()}`)
|
|
152
|
-
|
|
153
|
-
const created = createPromptxCodexSession({
|
|
154
|
-
title: 'Multi Agent Project',
|
|
155
|
-
cwd: workspaceDir,
|
|
156
|
-
engine: 'codex',
|
|
157
|
-
agentEngines: ['codex', 'claude-code', 'opencode'],
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
assert.equal(created?.agentBindings?.length, 3)
|
|
161
|
-
assert.deepEqual(created?.agentBindings?.map((item) => item.engine), ['codex', 'claude-code', 'opencode'])
|
|
162
|
-
|
|
163
|
-
const listed = listPromptxCodexSessions(10)
|
|
164
|
-
const listedProject = listed.find((item) => item.id === created?.id)
|
|
165
|
-
assert.equal(Boolean(listedProject), true)
|
|
166
|
-
assert.equal(listedProject?.agentBindings?.length, 3)
|
|
167
|
-
|
|
168
|
-
const updated = updatePromptxCodexSession(created.id, {
|
|
169
|
-
engine: 'claude-code',
|
|
170
|
-
agentEngines: ['claude-code', 'opencode'],
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
assert.equal(updated?.engine, 'claude-code')
|
|
174
|
-
assert.deepEqual(updated?.agentBindings?.map((item) => item.engine), ['claude-code', 'opencode'])
|
|
175
|
-
|
|
176
|
-
const fetched = getPromptxCodexSessionById(created.id)
|
|
177
|
-
assert.equal(fetched?.started, false)
|
|
178
|
-
assert.deepEqual(fetched?.agentBindings?.map((item) => item.engine), ['claude-code', 'opencode'])
|
|
179
|
-
} finally {
|
|
180
|
-
process.chdir(originalCwd)
|
|
181
|
-
if (typeof originalDataDir === 'string') {
|
|
182
|
-
process.env.PROMPTX_DATA_DIR = originalDataDir
|
|
183
|
-
} else {
|
|
184
|
-
delete process.env.PROMPTX_DATA_DIR
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
test('重置和删除项目时会同步处理 hidden member sessions', async () => {
|
|
190
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-codex-sessions-reset-delete-'))
|
|
191
|
-
const originalCwd = process.cwd()
|
|
192
|
-
const originalDataDir = process.env.PROMPTX_DATA_DIR
|
|
193
|
-
const dataDir = path.join(tempDir, 'data')
|
|
194
|
-
const workspaceDir = path.join(tempDir, 'workspace')
|
|
195
|
-
|
|
196
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
197
|
-
fs.mkdirSync(workspaceDir, { recursive: true })
|
|
198
|
-
process.chdir(tempDir)
|
|
199
|
-
process.env.PROMPTX_DATA_DIR = dataDir
|
|
200
|
-
|
|
201
|
-
try {
|
|
202
|
-
const suffix = `test=${Date.now()}`
|
|
203
|
-
const {
|
|
204
|
-
createPromptxCodexSession,
|
|
205
|
-
deletePromptxCodexSession,
|
|
206
|
-
getPromptxCodexSessionById,
|
|
207
|
-
resetPromptxCodexSession,
|
|
208
|
-
updatePromptxCodexSession,
|
|
209
|
-
} = await import(`./codexSessions.js?${suffix}`)
|
|
210
|
-
const { all } = await import(`./db.js?${suffix}`)
|
|
211
|
-
|
|
212
|
-
const created = createPromptxCodexSession({
|
|
213
|
-
title: 'Reset Delete Project',
|
|
214
|
-
cwd: workspaceDir,
|
|
215
|
-
engine: 'codex',
|
|
216
|
-
agentEngines: ['codex', 'claude-code', 'opencode'],
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
const bindingIds = new Map(created.agentBindings.map((item) => [item.engine, item.sessionRecordId]))
|
|
220
|
-
|
|
221
|
-
updatePromptxCodexSession(created.id, {
|
|
222
|
-
codexThreadId: 'root-thread',
|
|
223
|
-
engineThreadId: 'root-thread',
|
|
224
|
-
})
|
|
225
|
-
updatePromptxCodexSession(bindingIds.get('claude-code'), {
|
|
226
|
-
engineSessionId: 'claude-session',
|
|
227
|
-
engineThreadId: 'claude-session',
|
|
228
|
-
})
|
|
229
|
-
updatePromptxCodexSession(bindingIds.get('opencode'), {
|
|
230
|
-
engineSessionId: 'opencode-session',
|
|
231
|
-
engineThreadId: 'opencode-session',
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
const resetProject = resetPromptxCodexSession(created.id)
|
|
235
|
-
assert.equal(resetProject?.started, false)
|
|
236
|
-
assert.deepEqual(
|
|
237
|
-
resetProject?.agentBindings?.map((item) => ({
|
|
238
|
-
engine: item.engine,
|
|
239
|
-
sessionId: item.sessionId,
|
|
240
|
-
started: item.started,
|
|
241
|
-
})),
|
|
242
|
-
[
|
|
243
|
-
{ engine: 'codex', sessionId: '', started: false },
|
|
244
|
-
{ engine: 'claude-code', sessionId: '', started: false },
|
|
245
|
-
{ engine: 'opencode', sessionId: '', started: false },
|
|
246
|
-
]
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
assert.equal(getPromptxCodexSessionById(created.id)?.sessionId, '')
|
|
250
|
-
assert.equal(getPromptxCodexSessionById(bindingIds.get('claude-code'))?.sessionId, '')
|
|
251
|
-
assert.equal(getPromptxCodexSessionById(bindingIds.get('opencode'))?.sessionId, '')
|
|
252
|
-
assert.equal(getPromptxCodexSessionById(bindingIds.get('claude-code'))?.engineMeta?.hidden, true)
|
|
253
|
-
assert.equal(getPromptxCodexSessionById(bindingIds.get('opencode'))?.engineMeta?.projectRootId, created.id)
|
|
254
|
-
|
|
255
|
-
deletePromptxCodexSession(created.id)
|
|
256
|
-
|
|
257
|
-
assert.equal(getPromptxCodexSessionById(created.id), null)
|
|
258
|
-
assert.equal(getPromptxCodexSessionById(bindingIds.get('claude-code')), null)
|
|
259
|
-
assert.equal(getPromptxCodexSessionById(bindingIds.get('opencode')), null)
|
|
260
|
-
assert.equal(all('SELECT id FROM codex_sessions').length, 0)
|
|
261
|
-
} finally {
|
|
262
|
-
process.chdir(originalCwd)
|
|
263
|
-
if (typeof originalDataDir === 'string') {
|
|
264
|
-
process.env.PROMPTX_DATA_DIR = originalDataDir
|
|
265
|
-
} else {
|
|
266
|
-
delete process.env.PROMPTX_DATA_DIR
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
test('更新项目 agentEngines 会增删 hidden member sessions', async () => {
|
|
272
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-codex-sessions-members-update-'))
|
|
273
|
-
const originalCwd = process.cwd()
|
|
274
|
-
const originalDataDir = process.env.PROMPTX_DATA_DIR
|
|
275
|
-
const dataDir = path.join(tempDir, 'data')
|
|
276
|
-
const workspaceDir = path.join(tempDir, 'workspace')
|
|
277
|
-
|
|
278
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
279
|
-
fs.mkdirSync(workspaceDir, { recursive: true })
|
|
280
|
-
process.chdir(tempDir)
|
|
281
|
-
process.env.PROMPTX_DATA_DIR = dataDir
|
|
282
|
-
|
|
283
|
-
try {
|
|
284
|
-
const suffix = `test=${Date.now()}`
|
|
285
|
-
const {
|
|
286
|
-
createPromptxCodexSession,
|
|
287
|
-
getPromptxCodexSessionById,
|
|
288
|
-
updatePromptxCodexSession,
|
|
289
|
-
} = await import(`./codexSessions.js?${suffix}`)
|
|
290
|
-
|
|
291
|
-
const created = createPromptxCodexSession({
|
|
292
|
-
title: 'Member Update Project',
|
|
293
|
-
cwd: workspaceDir,
|
|
294
|
-
engine: 'codex',
|
|
295
|
-
agentEngines: ['codex', 'claude-code'],
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
const initialClaudeId = created.agentBindings.find((item) => item.engine === 'claude-code')?.sessionRecordId
|
|
299
|
-
assert.ok(initialClaudeId)
|
|
300
|
-
assert.equal(getPromptxCodexSessionById(initialClaudeId)?.engineMeta?.hidden, true)
|
|
301
|
-
|
|
302
|
-
const updated = updatePromptxCodexSession(created.id, {
|
|
303
|
-
agentEngines: ['codex', 'opencode'],
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
const nextBindings = updated?.agentBindings || []
|
|
307
|
-
assert.deepEqual(nextBindings.map((item) => item.engine), ['codex', 'opencode'])
|
|
308
|
-
assert.equal(getPromptxCodexSessionById(initialClaudeId), null)
|
|
309
|
-
|
|
310
|
-
const openCodeId = nextBindings.find((item) => item.engine === 'opencode')?.sessionRecordId
|
|
311
|
-
assert.ok(openCodeId)
|
|
312
|
-
assert.equal(getPromptxCodexSessionById(openCodeId)?.engineMeta?.projectRootId, created.id)
|
|
313
|
-
} finally {
|
|
314
|
-
process.chdir(originalCwd)
|
|
315
|
-
if (typeof originalDataDir === 'string') {
|
|
316
|
-
process.env.PROMPTX_DATA_DIR = originalDataDir
|
|
317
|
-
} else {
|
|
318
|
-
delete process.env.PROMPTX_DATA_DIR
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
test('单 agent 项目会自动补默认 agentBindings', async () => {
|
|
324
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-codex-sessions-legacy-'))
|
|
325
|
-
const originalCwd = process.cwd()
|
|
326
|
-
const originalDataDir = process.env.PROMPTX_DATA_DIR
|
|
327
|
-
const dataDir = path.join(tempDir, 'data')
|
|
328
|
-
const workspaceDir = path.join(tempDir, 'workspace')
|
|
329
|
-
|
|
330
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
331
|
-
fs.mkdirSync(workspaceDir, { recursive: true })
|
|
332
|
-
process.chdir(tempDir)
|
|
333
|
-
process.env.PROMPTX_DATA_DIR = dataDir
|
|
334
|
-
|
|
335
|
-
try {
|
|
336
|
-
const {
|
|
337
|
-
createPromptxCodexSession,
|
|
338
|
-
getPromptxCodexSessionById,
|
|
339
|
-
listPromptxCodexSessions,
|
|
340
|
-
} = await import(`./codexSessions.js?test=${Date.now()}`)
|
|
341
|
-
|
|
342
|
-
const created = createPromptxCodexSession({
|
|
343
|
-
title: 'Single Agent Project',
|
|
344
|
-
cwd: workspaceDir,
|
|
345
|
-
engine: 'codex',
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
const listed = listPromptxCodexSessions(20).find((item) => item.id === created.id)
|
|
349
|
-
assert.equal(Boolean(listed), true)
|
|
350
|
-
assert.deepEqual(listed?.agentBindings, [{
|
|
351
|
-
engine: 'codex',
|
|
352
|
-
sessionRecordId: created.id,
|
|
353
|
-
sessionId: '',
|
|
354
|
-
started: false,
|
|
355
|
-
running: false,
|
|
356
|
-
isDefault: true,
|
|
357
|
-
}])
|
|
358
|
-
|
|
359
|
-
const fetched = getPromptxCodexSessionById(created.id)
|
|
360
|
-
assert.deepEqual(fetched?.agentBindings, listed?.agentBindings)
|
|
361
|
-
} finally {
|
|
362
|
-
process.chdir(originalCwd)
|
|
363
|
-
if (typeof originalDataDir === 'string') {
|
|
364
|
-
process.env.PROMPTX_DATA_DIR = originalDataDir
|
|
365
|
-
} else {
|
|
366
|
-
delete process.env.PROMPTX_DATA_DIR
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
})
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
import os from 'node:os'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import test from 'node:test'
|
|
6
|
-
import Database from 'better-sqlite3'
|
|
7
|
-
|
|
8
|
-
test('db module backs up legacy schema and initializes the latest schema', async () => {
|
|
9
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-db-legacy-'))
|
|
10
|
-
const dataDir = path.join(tempDir, 'data')
|
|
11
|
-
const legacyDbPath = path.join(dataDir, 'promptx.sqlite')
|
|
12
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
13
|
-
|
|
14
|
-
const legacyDb = new Database(legacyDbPath)
|
|
15
|
-
legacyDb.exec(`
|
|
16
|
-
CREATE TABLE documents (
|
|
17
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
-
title TEXT NOT NULL DEFAULT ''
|
|
19
|
-
);
|
|
20
|
-
`)
|
|
21
|
-
legacyDb.close()
|
|
22
|
-
|
|
23
|
-
const originalCwd = process.cwd()
|
|
24
|
-
const originalDataDir = process.env.PROMPTX_DATA_DIR
|
|
25
|
-
process.chdir(tempDir)
|
|
26
|
-
process.env.PROMPTX_DATA_DIR = dataDir
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const dbModule = await import(`./db.js?test=${Date.now()}`)
|
|
30
|
-
const schemaVersionRow = dbModule.get(
|
|
31
|
-
'SELECT value FROM schema_meta WHERE key = ?',
|
|
32
|
-
['schema_version']
|
|
33
|
-
)
|
|
34
|
-
const tasksTable = dbModule.get(
|
|
35
|
-
"SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?",
|
|
36
|
-
['tasks']
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
assert.equal(schemaVersionRow?.value, '1')
|
|
40
|
-
assert.equal(tasksTable?.name, 'tasks')
|
|
41
|
-
|
|
42
|
-
const backupFiles = fs.readdirSync(dataDir).filter((fileName) => fileName.includes('.legacy-') && fileName.endsWith('.bak'))
|
|
43
|
-
assert.equal(backupFiles.length, 1)
|
|
44
|
-
} finally {
|
|
45
|
-
process.chdir(originalCwd)
|
|
46
|
-
if (typeof originalDataDir === 'string') {
|
|
47
|
-
process.env.PROMPTX_DATA_DIR = originalDataDir
|
|
48
|
-
} else {
|
|
49
|
-
delete process.env.PROMPTX_DATA_DIR
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test('db module reads latest disk data even when another connection writes to the same sqlite file', async () => {
|
|
55
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-db-shared-'))
|
|
56
|
-
const dataDir = path.join(tempDir, 'data')
|
|
57
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
58
|
-
|
|
59
|
-
const originalCwd = process.cwd()
|
|
60
|
-
const originalDataDir = process.env.PROMPTX_DATA_DIR
|
|
61
|
-
process.chdir(tempDir)
|
|
62
|
-
process.env.PROMPTX_DATA_DIR = dataDir
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
const dbModule = await import(`./db.js?test=${Date.now()}`)
|
|
66
|
-
const now = new Date().toISOString()
|
|
67
|
-
|
|
68
|
-
dbModule.run(
|
|
69
|
-
`INSERT INTO tasks (slug, edit_token, title, auto_title, last_prompt_preview, codex_session_id, visibility, expires_at, created_at, updated_at)
|
|
70
|
-
VALUES (?, ?, '', '', '', '', 'private', NULL, ?, ?)`,
|
|
71
|
-
['task-from-module', 'token-module', now, now]
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
const dbPath = path.join(dataDir, 'promptx.sqlite')
|
|
75
|
-
const externalDb = new Database(dbPath)
|
|
76
|
-
externalDb.pragma('foreign_keys = ON')
|
|
77
|
-
externalDb.prepare(
|
|
78
|
-
`INSERT INTO tasks (slug, edit_token, title, auto_title, last_prompt_preview, codex_session_id, visibility, expires_at, created_at, updated_at)
|
|
79
|
-
VALUES (?, ?, '', '', '', '', 'private', NULL, ?, ?)`
|
|
80
|
-
).run('task-from-external', 'token-external', now, now)
|
|
81
|
-
externalDb.close()
|
|
82
|
-
|
|
83
|
-
const rows = dbModule.all('SELECT slug FROM tasks ORDER BY slug ASC')
|
|
84
|
-
assert.deepEqual(rows.map((row) => row.slug), ['task-from-external', 'task-from-module'])
|
|
85
|
-
} finally {
|
|
86
|
-
process.chdir(originalCwd)
|
|
87
|
-
if (typeof originalDataDir === 'string') {
|
|
88
|
-
process.env.PROMPTX_DATA_DIR = originalDataDir
|
|
89
|
-
} else {
|
|
90
|
-
delete process.env.PROMPTX_DATA_DIR
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
test('db module applies additive schema patches for existing schema_version 1 databases', async () => {
|
|
96
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-db-additive-'))
|
|
97
|
-
const dataDir = path.join(tempDir, 'data')
|
|
98
|
-
const dbPath = path.join(dataDir, 'promptx.sqlite')
|
|
99
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
100
|
-
|
|
101
|
-
const legacyDb = new Database(dbPath)
|
|
102
|
-
legacyDb.exec(`
|
|
103
|
-
CREATE TABLE schema_meta (
|
|
104
|
-
key TEXT PRIMARY KEY,
|
|
105
|
-
value TEXT NOT NULL
|
|
106
|
-
);
|
|
107
|
-
INSERT INTO schema_meta (key, value) VALUES ('schema_version', '1');
|
|
108
|
-
|
|
109
|
-
CREATE TABLE tasks (
|
|
110
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
111
|
-
slug TEXT NOT NULL UNIQUE,
|
|
112
|
-
edit_token TEXT NOT NULL,
|
|
113
|
-
title TEXT NOT NULL DEFAULT '',
|
|
114
|
-
auto_title TEXT NOT NULL DEFAULT '',
|
|
115
|
-
last_prompt_preview TEXT NOT NULL DEFAULT '',
|
|
116
|
-
codex_session_id TEXT NOT NULL DEFAULT '',
|
|
117
|
-
visibility TEXT NOT NULL DEFAULT 'private',
|
|
118
|
-
expires_at TEXT,
|
|
119
|
-
created_at TEXT NOT NULL,
|
|
120
|
-
updated_at TEXT NOT NULL
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
CREATE TABLE blocks (
|
|
124
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
125
|
-
task_id INTEGER NOT NULL,
|
|
126
|
-
type TEXT NOT NULL,
|
|
127
|
-
content TEXT NOT NULL DEFAULT '',
|
|
128
|
-
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
129
|
-
meta_json TEXT NOT NULL DEFAULT '{}',
|
|
130
|
-
created_at TEXT NOT NULL,
|
|
131
|
-
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
CREATE TABLE codex_sessions (
|
|
135
|
-
id TEXT PRIMARY KEY,
|
|
136
|
-
title TEXT NOT NULL,
|
|
137
|
-
cwd TEXT NOT NULL,
|
|
138
|
-
codex_thread_id TEXT,
|
|
139
|
-
created_at TEXT NOT NULL,
|
|
140
|
-
updated_at TEXT NOT NULL
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
CREATE TABLE codex_runs (
|
|
144
|
-
id TEXT PRIMARY KEY,
|
|
145
|
-
task_slug TEXT NOT NULL,
|
|
146
|
-
session_id TEXT NOT NULL,
|
|
147
|
-
prompt TEXT NOT NULL DEFAULT '',
|
|
148
|
-
status TEXT NOT NULL,
|
|
149
|
-
response_message TEXT NOT NULL DEFAULT '',
|
|
150
|
-
error_message TEXT NOT NULL DEFAULT '',
|
|
151
|
-
created_at TEXT NOT NULL,
|
|
152
|
-
updated_at TEXT NOT NULL,
|
|
153
|
-
started_at TEXT,
|
|
154
|
-
finished_at TEXT
|
|
155
|
-
);
|
|
156
|
-
`)
|
|
157
|
-
legacyDb.close()
|
|
158
|
-
|
|
159
|
-
const originalCwd = process.cwd()
|
|
160
|
-
const originalDataDir = process.env.PROMPTX_DATA_DIR
|
|
161
|
-
process.chdir(tempDir)
|
|
162
|
-
process.env.PROMPTX_DATA_DIR = dataDir
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
const dbModule = await import(`./db.js?test=${Date.now()}`)
|
|
166
|
-
const promptBlocksColumn = dbModule.get(
|
|
167
|
-
`SELECT name
|
|
168
|
-
FROM pragma_table_info('codex_runs')
|
|
169
|
-
WHERE name = ?`,
|
|
170
|
-
['prompt_blocks_json']
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
assert.equal(promptBlocksColumn?.name, 'prompt_blocks_json')
|
|
174
|
-
} finally {
|
|
175
|
-
process.chdir(originalCwd)
|
|
176
|
-
if (typeof originalDataDir === 'string') {
|
|
177
|
-
process.env.PROMPTX_DATA_DIR = originalDataDir
|
|
178
|
-
} else {
|
|
179
|
-
delete process.env.PROMPTX_DATA_DIR
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
})
|