@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/apps/server/src/agentSessionDiscovery.js +180 -7
  3. package/apps/web/dist/assets/{CodexSessionManagerDialog-Dic9kMHK.js → CodexSessionManagerDialog-y7O-JTxP.js} +1 -1
  4. package/apps/web/dist/assets/{TaskDiffReviewDialog-CKiZdXqi.js → TaskDiffReviewDialog-CTr_zoAn.js} +1 -1
  5. package/apps/web/dist/assets/{WorkbenchSettingsDialog-CP0z90bm.js → WorkbenchSettingsDialog-Bf2DCuN_.js} +1 -1
  6. package/apps/web/dist/assets/{WorkbenchView-D1oxqNr4.css → WorkbenchView-CK1snPBz.css} +1 -1
  7. package/apps/web/dist/assets/WorkbenchView-Gq3mmtsK.js +60 -0
  8. package/apps/web/dist/assets/index-Co1Ssha9.js +2 -0
  9. package/apps/web/dist/index.html +1 -1
  10. package/package.json +21 -14
  11. package/apps/runner/src/engines/claudeCodeRunner.test.js +0 -467
  12. package/apps/runner/src/engines/kimiCodeRunner.test.js +0 -127
  13. package/apps/runner/src/engines/openCodeRunner.test.js +0 -236
  14. package/apps/runner/src/engines/runnerContract.test.js +0 -449
  15. package/apps/runner/src/engines/shellRunner.test.js +0 -46
  16. package/apps/runner/src/runManager.test.js +0 -913
  17. package/apps/runner/src/serverClient.test.js +0 -93
  18. package/apps/server/src/agentSessionDiscovery.test.js +0 -186
  19. package/apps/server/src/appPaths.test.js +0 -52
  20. package/apps/server/src/assetRoutes.test.js +0 -168
  21. package/apps/server/src/codex.test.js +0 -518
  22. package/apps/server/src/codexRoutes.test.js +0 -376
  23. package/apps/server/src/codexRuns.test.js +0 -160
  24. package/apps/server/src/codexSessions.test.js +0 -369
  25. package/apps/server/src/db.test.js +0 -182
  26. package/apps/server/src/gitDiff.test.js +0 -542
  27. package/apps/server/src/gitDiffClient.test.js +0 -140
  28. package/apps/server/src/internalRoutes.test.js +0 -134
  29. package/apps/server/src/maintenance.test.js +0 -154
  30. package/apps/server/src/processControl.test.js +0 -147
  31. package/apps/server/src/relayClient.test.js +0 -478
  32. package/apps/server/src/relayConfig.test.js +0 -73
  33. package/apps/server/src/relayProtocol.test.js +0 -49
  34. package/apps/server/src/relayServer.test.js +0 -798
  35. package/apps/server/src/relayTenants.test.js +0 -137
  36. package/apps/server/src/relayUsageStore.test.js +0 -65
  37. package/apps/server/src/repository.test.js +0 -150
  38. package/apps/server/src/runDispatchService.test.js +0 -563
  39. package/apps/server/src/runEventIngest.test.js +0 -225
  40. package/apps/server/src/runRecovery.test.js +0 -73
  41. package/apps/server/src/runnerClient.test.js +0 -80
  42. package/apps/server/src/runnerDispatch.test.js +0 -136
  43. package/apps/server/src/systemConfig.test.js +0 -112
  44. package/apps/server/src/systemRoutes.test.js +0 -319
  45. package/apps/server/src/taskRoutes.test.js +0 -775
  46. package/apps/server/src/upload.test.js +0 -30
  47. package/apps/server/src/webAppRoutes.test.js +0 -67
  48. package/apps/server/src/workspaceFiles.test.js +0 -279
  49. package/apps/web/dist/assets/WorkbenchView-noayQwj4.js +0 -60
  50. package/apps/web/dist/assets/index-HLkdzIYF.js +0 -2
  51. package/packages/shared/src/dailyLogStream.test.js +0 -29
  52. 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
- })