@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,563 +0,0 @@
1
- import assert from 'node:assert/strict'
2
- import test from 'node:test'
3
-
4
- import { createRunDispatchService } from './runDispatchService.js'
5
-
6
- test('runDispatchService keeps queued status when runner accepts queued run', async () => {
7
- const broadcasts = []
8
- const service = createRunDispatchService({
9
- broadcastServerEvent(type, payload = {}) {
10
- broadcasts.push({ type, ...payload })
11
- },
12
- createCodexRun(payload = {}) {
13
- return {
14
- id: 'run-1',
15
- status: payload.status || 'queued',
16
- }
17
- },
18
- decorateCodexSession(session) {
19
- return {
20
- ...session,
21
- running: false,
22
- }
23
- },
24
- getCodexRunById() {
25
- return {
26
- id: 'run-1',
27
- status: 'queued',
28
- }
29
- },
30
- getPromptxCodexSessionById() {
31
- return {
32
- id: 'session-1',
33
- engine: 'codex',
34
- cwd: '/tmp/demo',
35
- title: 'Demo',
36
- codexThreadId: '',
37
- engineSessionId: '',
38
- engineThreadId: '',
39
- engineMeta: {},
40
- createdAt: '2026-03-22T00:00:00.000Z',
41
- updatedAt: '2026-03-22T00:00:00.000Z',
42
- }
43
- },
44
- getRunningCodexRunBySessionId() {
45
- return null
46
- },
47
- getTaskBySlug() {
48
- return {
49
- slug: 'task-1',
50
- expired: false,
51
- }
52
- },
53
- logger: {
54
- warn() {},
55
- },
56
- runnerClient: {
57
- async startRun() {
58
- return {
59
- status: 'queued',
60
- }
61
- },
62
- },
63
- updateCodexRunFromRunnerStatus(_runId, patch = {}) {
64
- return {
65
- id: 'run-1',
66
- status: patch.status || 'queued',
67
- }
68
- },
69
- updateTaskCodexSession() {},
70
- })
71
-
72
- const result = await service.startTaskRunForTask({
73
- taskSlug: 'task-1',
74
- sessionId: 'session-1',
75
- prompt: 'hello',
76
- promptBlocks: [],
77
- })
78
-
79
- assert.equal(result.runnerDispatchPending, false)
80
- assert.equal(result.run?.status, 'queued')
81
- assert.ok(broadcasts.some((item) => item.type === 'runs.changed' && item.status === 'queued'))
82
- })
83
-
84
- test('runDispatchService dispatches shell command runs with project cwd', async () => {
85
- let createdRunPayload = null
86
- let runnerPayload = null
87
- let selectedSessionIds = []
88
- const service = createRunDispatchService({
89
- createCodexRun(payload = {}) {
90
- createdRunPayload = payload
91
- return {
92
- id: 'run-shell-1',
93
- status: payload.status || 'queued',
94
- }
95
- },
96
- decorateCodexSession(session) {
97
- return {
98
- ...session,
99
- running: false,
100
- }
101
- },
102
- getCodexRunById() {
103
- return {
104
- id: 'run-shell-1',
105
- status: 'queued',
106
- }
107
- },
108
- getPromptxCodexSessionById(sessionId) {
109
- selectedSessionIds.push(sessionId)
110
- if (sessionId === 'session-1') {
111
- return {
112
- id: 'session-1',
113
- engine: 'codex',
114
- cwd: '/tmp/demo-shell',
115
- title: 'Demo Shell',
116
- codexThreadId: 'thread-root',
117
- engineSessionId: '',
118
- engineThreadId: 'thread-root',
119
- engineMeta: { root: true },
120
- createdAt: '2026-03-22T00:00:00.000Z',
121
- updatedAt: '2026-03-22T00:00:00.000Z',
122
- }
123
- }
124
- return {
125
- id: 'member-claude',
126
- engine: 'claude-code',
127
- cwd: '/tmp/demo-shell',
128
- title: 'Claude Member',
129
- codexThreadId: '',
130
- engineSessionId: 'claude-session-1',
131
- engineThreadId: 'claude-thread-1',
132
- engineMeta: { hidden: true },
133
- createdAt: '2026-03-22T00:00:00.000Z',
134
- updatedAt: '2026-03-22T00:00:00.000Z',
135
- }
136
- },
137
- getRunningCodexRunBySessionId() {
138
- return null
139
- },
140
- getTaskBySlug() {
141
- return {
142
- slug: 'task-shell-1',
143
- expired: false,
144
- }
145
- },
146
- logger: {
147
- warn() {},
148
- },
149
- runnerClient: {
150
- async startRun(payload = {}) {
151
- runnerPayload = payload
152
- return {
153
- status: 'queued',
154
- }
155
- },
156
- },
157
- updateCodexRunFromRunnerStatus(_runId, patch = {}) {
158
- return {
159
- id: 'run-shell-1',
160
- status: patch.status || 'queued',
161
- }
162
- },
163
- updateTaskCodexSession() {},
164
- })
165
-
166
- await service.startTaskRunForTask({
167
- taskSlug: 'task-shell-1',
168
- projectSessionId: 'session-1',
169
- sessionId: 'member-claude',
170
- displayEngine: 'claude-code',
171
- prompt: '!git status --short',
172
- promptBlocks: [{ type: 'text', content: '!git status --short', meta: {} }],
173
- commandMode: 'shell',
174
- command: 'echo hacked',
175
- allowShellCommand: true,
176
- })
177
-
178
- assert.equal(createdRunPayload?.engine, 'shell')
179
- assert.equal(createdRunPayload?.displayEngine, 'claude-code')
180
- assert.equal(createdRunPayload?.sessionId, 'session-1')
181
- assert.equal(createdRunPayload?.prompt, '!git status --short')
182
- assert.equal(runnerPayload?.engine, 'shell')
183
- assert.equal(runnerPayload?.prompt, 'git status --short')
184
- assert.equal(runnerPayload?.cwd, '/tmp/demo-shell')
185
- assert.equal(runnerPayload?.sessionId, 'session-1')
186
- assert.equal(runnerPayload?.codexThreadId, '')
187
- assert.equal(runnerPayload?.engineSessionId, '')
188
- assert.equal(runnerPayload?.engineThreadId, '')
189
- assert.deepEqual(runnerPayload?.engineMeta, {})
190
- assert.deepEqual(selectedSessionIds, ['member-claude', 'session-1', 'session-1'])
191
- })
192
-
193
- test('runDispatchService rejects shell runs from non-local requests', async () => {
194
- const service = createRunDispatchService({
195
- getPromptxCodexSessionById() {
196
- return {
197
- id: 'session-1',
198
- engine: 'codex',
199
- cwd: '/tmp/demo-shell',
200
- title: 'Demo Shell',
201
- }
202
- },
203
- getRunningCodexRunBySessionId() {
204
- return null
205
- },
206
- getTaskBySlug() {
207
- return {
208
- slug: 'task-shell-1',
209
- expired: false,
210
- }
211
- },
212
- })
213
-
214
- await assert.rejects(
215
- () => service.startTaskRunForTask({
216
- taskSlug: 'task-shell-1',
217
- sessionId: 'session-1',
218
- prompt: '!pwd',
219
- promptBlocks: [{ type: 'text', content: '!pwd' }],
220
- commandMode: 'shell',
221
- allowShellCommand: false,
222
- }),
223
- (error) => error?.statusCode === 403 && error?.messageKey === 'errors.shellLocalOnly'
224
- )
225
- })
226
-
227
- test('runDispatchService marks stop request as stopping and returns accepted result', async () => {
228
- const broadcasts = []
229
- let currentStatus = 'running'
230
- const service = createRunDispatchService({
231
- broadcastServerEvent(type, payload = {}) {
232
- broadcasts.push({ type, ...payload })
233
- },
234
- getCodexRunById(runId) {
235
- return {
236
- id: runId,
237
- taskSlug: 'task-1',
238
- sessionId: 'session-1',
239
- status: currentStatus,
240
- }
241
- },
242
- logger: {
243
- warn() {},
244
- },
245
- runnerClient: {
246
- stopRun() {
247
- return Promise.resolve({ accepted: true })
248
- },
249
- },
250
- updateCodexRunFromRunnerStatus(runId, patch = {}) {
251
- currentStatus = patch.status || currentStatus
252
- return {
253
- id: runId,
254
- taskSlug: 'task-1',
255
- sessionId: 'session-1',
256
- status: currentStatus,
257
- }
258
- },
259
- })
260
-
261
- const result = await service.requestRunStop('run-1', {
262
- isActiveRunStatus(status) {
263
- return ['queued', 'starting', 'running', 'stopping'].includes(status)
264
- },
265
- reason: 'user_requested',
266
- })
267
-
268
- assert.equal(result?.accepted, true)
269
- assert.equal(result?.run?.status, 'stopping')
270
- assert.ok(broadcasts.some((item) => item.type === 'runs.changed' && item.status === 'stopping'))
271
- assert.ok(broadcasts.some((item) => item.type === 'sessions.changed' && item.sessionId === 'session-1'))
272
- })
273
-
274
- test('runDispatchService rewrites codex image urls to local server for runner payload only', async () => {
275
- let createdRunPayload = null
276
- let runnerPayload = null
277
- const service = createRunDispatchService({
278
- createCodexRun(payload = {}) {
279
- createdRunPayload = payload
280
- return {
281
- id: 'run-1',
282
- status: payload.status || 'queued',
283
- }
284
- },
285
- decorateCodexSession(session) {
286
- return {
287
- ...session,
288
- running: false,
289
- }
290
- },
291
- getCodexRunById() {
292
- return {
293
- id: 'run-1',
294
- status: 'queued',
295
- }
296
- },
297
- getPromptxCodexSessionById() {
298
- return {
299
- id: 'session-1',
300
- engine: 'codex',
301
- cwd: '/tmp/demo',
302
- title: 'Demo',
303
- codexThreadId: '',
304
- engineSessionId: '',
305
- engineThreadId: '',
306
- engineMeta: {},
307
- createdAt: '2026-03-22T00:00:00.000Z',
308
- updatedAt: '2026-03-22T00:00:00.000Z',
309
- }
310
- },
311
- getRunningCodexRunBySessionId() {
312
- return null
313
- },
314
- getTaskBySlug() {
315
- return {
316
- slug: 'task-1',
317
- expired: false,
318
- }
319
- },
320
- localServerBaseUrl: 'http://127.0.0.1:3000',
321
- publicServerBaseUrl: 'https://dongdong.promptx.mushayu.com',
322
- relayUrl: 'https://dongdong.promptx.mushayu.com',
323
- logger: {
324
- warn() {},
325
- },
326
- runnerClient: {
327
- async startRun(payload = {}) {
328
- runnerPayload = payload
329
- return {
330
- status: 'queued',
331
- }
332
- },
333
- },
334
- updateCodexRunFromRunnerStatus(_runId, patch = {}) {
335
- return {
336
- id: 'run-1',
337
- status: patch.status || 'queued',
338
- }
339
- },
340
- updateTaskCodexSession() {},
341
- })
342
-
343
- await service.startTaskRunForTask({
344
- taskSlug: 'task-1',
345
- sessionId: 'session-1',
346
- prompt: '看图:https://dongdong.promptx.mushayu.com/uploads/demo.png',
347
- promptBlocks: [
348
- {
349
- type: 'image',
350
- content: '/uploads/demo.png',
351
- meta: {},
352
- },
353
- ],
354
- })
355
-
356
- assert.equal(createdRunPayload?.prompt, '看图:https://dongdong.promptx.mushayu.com/uploads/demo.png')
357
- assert.equal(createdRunPayload?.promptBlocks?.[0]?.content, '/uploads/demo.png')
358
- assert.equal(runnerPayload?.prompt, '看图:http://127.0.0.1:3000/uploads/demo.png')
359
- assert.equal(runnerPayload?.promptBlocks?.[0]?.content, 'http://127.0.0.1:3000/uploads/demo.png')
360
- })
361
-
362
- test('runDispatchService adapts local image base url for claude dev server port', async () => {
363
- let runnerPayload = null
364
- const service = createRunDispatchService({
365
- createCodexRun(payload = {}) {
366
- return {
367
- id: 'run-2',
368
- status: payload.status || 'queued',
369
- }
370
- },
371
- decorateCodexSession(session) {
372
- return {
373
- ...session,
374
- running: false,
375
- }
376
- },
377
- getCodexRunById() {
378
- return {
379
- id: 'run-2',
380
- status: 'queued',
381
- }
382
- },
383
- getPromptxCodexSessionById() {
384
- return {
385
- id: 'session-2',
386
- engine: 'claude-code',
387
- cwd: '/tmp/demo',
388
- title: 'Demo',
389
- codexThreadId: '',
390
- engineSessionId: '',
391
- engineThreadId: '',
392
- engineMeta: {},
393
- createdAt: '2026-03-22T00:00:00.000Z',
394
- updatedAt: '2026-03-22T00:00:00.000Z',
395
- }
396
- },
397
- getRunningCodexRunBySessionId() {
398
- return null
399
- },
400
- getTaskBySlug() {
401
- return {
402
- slug: 'task-2',
403
- expired: false,
404
- }
405
- },
406
- localServerBaseUrl: 'http://127.0.0.1:3001',
407
- publicServerBaseUrl: 'https://dongdong.promptx.mushayu.com',
408
- relayUrl: 'https://dongdong.promptx.mushayu.com',
409
- logger: {
410
- warn() {},
411
- },
412
- runnerClient: {
413
- async startRun(payload = {}) {
414
- runnerPayload = payload
415
- return {
416
- status: 'queued',
417
- }
418
- },
419
- },
420
- updateCodexRunFromRunnerStatus(_runId, patch = {}) {
421
- return {
422
- id: 'run-2',
423
- status: patch.status || 'queued',
424
- }
425
- },
426
- updateTaskCodexSession() {},
427
- })
428
-
429
- await service.startTaskRunForTask({
430
- taskSlug: 'task-2',
431
- sessionId: 'session-2',
432
- prompt: '看图:https://dongdong.promptx.mushayu.com/uploads/demo-2.png',
433
- promptBlocks: [
434
- {
435
- type: 'image',
436
- content: 'https://dongdong.promptx.mushayu.com/uploads/demo-2.png',
437
- meta: {},
438
- },
439
- ],
440
- })
441
-
442
- assert.equal(runnerPayload?.prompt, '看图:http://127.0.0.1:3001/uploads/demo-2.png')
443
- assert.equal(runnerPayload?.promptBlocks?.[0]?.content, 'http://127.0.0.1:3001/uploads/demo-2.png')
444
- })
445
-
446
- test('runDispatchService 在多 agent 项目中把 run 绑定到实际 member session,并回写 root 项目 session', async () => {
447
- let createdRunPayload = null
448
- let updatedTaskSession = null
449
- let runnerPayload = null
450
- const rootSession = {
451
- id: 'project-root',
452
- engine: 'codex',
453
- cwd: '/tmp/demo',
454
- title: 'Multi Agent Project',
455
- codexThreadId: '',
456
- engineSessionId: '',
457
- engineThreadId: '',
458
- engineMeta: {},
459
- createdAt: '2026-03-22T00:00:00.000Z',
460
- updatedAt: '2026-03-22T00:00:00.000Z',
461
- agentBindings: [
462
- { engine: 'codex', sessionRecordId: 'project-root', isDefault: true },
463
- { engine: 'claude-code', sessionRecordId: 'member-claude', isDefault: false },
464
- { engine: 'opencode', sessionRecordId: 'member-opencode', isDefault: false },
465
- ],
466
- }
467
- const memberClaudeSession = {
468
- id: 'member-claude',
469
- engine: 'claude-code',
470
- cwd: '/tmp/demo',
471
- title: 'Multi Agent Project',
472
- codexThreadId: '',
473
- engineSessionId: 'claude-existing',
474
- engineThreadId: 'claude-existing',
475
- engineMeta: {
476
- hidden: true,
477
- projectRootId: 'project-root',
478
- },
479
- createdAt: '2026-03-22T00:00:00.000Z',
480
- updatedAt: '2026-03-22T00:00:00.000Z',
481
- }
482
-
483
- const service = createRunDispatchService({
484
- createCodexRun(payload = {}) {
485
- createdRunPayload = payload
486
- return {
487
- id: 'run-member-1',
488
- taskSlug: payload.taskSlug,
489
- sessionId: payload.sessionId,
490
- status: payload.status || 'queued',
491
- }
492
- },
493
- decorateCodexSession(session) {
494
- return {
495
- ...session,
496
- running: false,
497
- }
498
- },
499
- getCodexRunById() {
500
- return {
501
- id: 'run-member-1',
502
- status: 'queued',
503
- }
504
- },
505
- getPromptxCodexSessionById(sessionId) {
506
- if (sessionId === 'project-root') {
507
- return rootSession
508
- }
509
- if (sessionId === 'member-claude') {
510
- return memberClaudeSession
511
- }
512
- return null
513
- },
514
- getRunningCodexRunBySessionId() {
515
- return null
516
- },
517
- getTaskBySlug() {
518
- return {
519
- slug: 'task-1',
520
- expired: false,
521
- }
522
- },
523
- logger: {
524
- warn() {},
525
- },
526
- runnerClient: {
527
- async startRun(payload = {}) {
528
- runnerPayload = payload
529
- return {
530
- status: 'queued',
531
- }
532
- },
533
- },
534
- updateCodexRunFromRunnerStatus(_runId, patch = {}) {
535
- return {
536
- id: 'run-member-1',
537
- taskSlug: 'task-1',
538
- sessionId: createdRunPayload?.sessionId || 'member-claude',
539
- status: patch.status || 'queued',
540
- }
541
- },
542
- updateTaskCodexSession(taskSlug, sessionId) {
543
- updatedTaskSession = { taskSlug, sessionId }
544
- },
545
- })
546
-
547
- const result = await service.startTaskRunForTask({
548
- taskSlug: 'task-1',
549
- sessionId: 'member-claude',
550
- projectSessionId: 'project-root',
551
- prompt: 'hello',
552
- promptBlocks: [],
553
- })
554
-
555
- assert.equal(createdRunPayload?.sessionId, 'member-claude')
556
- assert.equal(updatedTaskSession?.taskSlug, 'task-1')
557
- assert.equal(updatedTaskSession?.sessionId, 'project-root')
558
- assert.equal(runnerPayload?.sessionId, 'member-claude')
559
- assert.equal(runnerPayload?.engine, 'claude-code')
560
- assert.equal(runnerPayload?.engineSessionId, 'claude-existing')
561
- assert.equal(result?.run?.sessionId, 'member-claude')
562
- assert.equal(result?.session?.id, 'project-root')
563
- })