@muyichengshayu/promptx 0.2.16 → 0.2.17
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 +4 -0
- package/apps/runner/src/codex.js +114 -16
- package/apps/runner/src/engines/claudeCodeRunner.test.js +467 -0
- package/apps/runner/src/engines/kimiCodeRunner.test.js +127 -0
- package/apps/runner/src/engines/openCodeRunner.test.js +236 -0
- package/apps/runner/src/engines/runnerContract.test.js +510 -0
- package/apps/runner/src/engines/shellRunner.test.js +46 -0
- package/apps/runner/src/runManager.test.js +913 -0
- package/apps/runner/src/serverClient.test.js +93 -0
- package/apps/server/src/agentSessionDiscovery.test.js +572 -0
- package/apps/server/src/appPaths.test.js +52 -0
- package/apps/server/src/assetRoutes.test.js +168 -0
- package/apps/server/src/codex.test.js +518 -0
- package/apps/server/src/codexRoutes.test.js +376 -0
- package/apps/server/src/codexRuns.test.js +160 -0
- package/apps/server/src/codexSessions.test.js +369 -0
- package/apps/server/src/db.test.js +182 -0
- package/apps/server/src/gitDiff.test.js +542 -0
- package/apps/server/src/gitDiffClient.test.js +140 -0
- package/apps/server/src/internalRoutes.test.js +134 -0
- package/apps/server/src/maintenance.test.js +154 -0
- package/apps/server/src/processControl.test.js +147 -0
- package/apps/server/src/relayClient.test.js +478 -0
- package/apps/server/src/relayConfig.test.js +73 -0
- package/apps/server/src/relayProtocol.test.js +49 -0
- package/apps/server/src/relayServer.test.js +798 -0
- package/apps/server/src/relayTenants.test.js +137 -0
- package/apps/server/src/relayUsageStore.test.js +65 -0
- package/apps/server/src/repository.test.js +150 -0
- package/apps/server/src/runDispatchService.test.js +563 -0
- package/apps/server/src/runEventIngest.test.js +225 -0
- package/apps/server/src/runRecovery.test.js +73 -0
- package/apps/server/src/runnerClient.test.js +80 -0
- package/apps/server/src/runnerDispatch.test.js +136 -0
- package/apps/server/src/systemConfig.test.js +112 -0
- package/apps/server/src/systemRoutes.test.js +319 -0
- package/apps/server/src/taskRoutes.test.js +775 -0
- package/apps/server/src/upload.test.js +30 -0
- package/apps/server/src/webAppRoutes.test.js +67 -0
- package/apps/server/src/workspaceFiles.test.js +279 -0
- package/package.json +14 -21
- package/packages/shared/src/dailyLogStream.test.js +29 -0
- package/packages/shared/src/shellCommands.test.js +45 -0
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
function withEnv(overrides, fn) {
|
|
8
|
+
const previous = new Map()
|
|
9
|
+
|
|
10
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
11
|
+
previous.set(key, process.env[key])
|
|
12
|
+
if (typeof value === 'undefined') {
|
|
13
|
+
delete process.env[key]
|
|
14
|
+
} else {
|
|
15
|
+
process.env[key] = value
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return Promise.resolve()
|
|
20
|
+
.then(fn)
|
|
21
|
+
.finally(() => {
|
|
22
|
+
for (const [key, value] of previous.entries()) {
|
|
23
|
+
if (typeof value === 'undefined') {
|
|
24
|
+
delete process.env[key]
|
|
25
|
+
} else {
|
|
26
|
+
process.env[key] = value
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test('appPaths resolves storage under PROMPTX_HOME by default', async () => {
|
|
33
|
+
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-home-'))
|
|
34
|
+
|
|
35
|
+
await withEnv(
|
|
36
|
+
{
|
|
37
|
+
PROMPTX_HOME: tempHome,
|
|
38
|
+
PROMPTX_DATA_DIR: undefined,
|
|
39
|
+
PROMPTX_UPLOADS_DIR: undefined,
|
|
40
|
+
PROMPTX_TMP_DIR: undefined,
|
|
41
|
+
},
|
|
42
|
+
async () => {
|
|
43
|
+
const appPaths = await import(`./appPaths.js?test=${Date.now()}`)
|
|
44
|
+
const resolved = appPaths.ensurePromptxStorageReady()
|
|
45
|
+
|
|
46
|
+
assert.equal(resolved.promptxHomeDir, tempHome)
|
|
47
|
+
assert.equal(resolved.dataDir, path.join(tempHome, 'data'))
|
|
48
|
+
assert.equal(resolved.uploadsDir, path.join(tempHome, 'uploads'))
|
|
49
|
+
assert.equal(resolved.tmpDir, path.join(tempHome, 'tmp'))
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
})
|
|
@@ -0,0 +1,168 @@
|
|
|
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 Fastify from 'fastify'
|
|
6
|
+
import multipart from '@fastify/multipart'
|
|
7
|
+
import test from 'node:test'
|
|
8
|
+
import { createCanvas } from '@napi-rs/canvas'
|
|
9
|
+
|
|
10
|
+
import { registerAssetRoutes } from './assetRoutes.js'
|
|
11
|
+
|
|
12
|
+
function createMultipartBody(fileName, mimeType, buffer) {
|
|
13
|
+
const boundary = `----promptx-test-${Date.now()}`
|
|
14
|
+
const head = Buffer.from(
|
|
15
|
+
`--${boundary}\r\n`
|
|
16
|
+
+ `Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`
|
|
17
|
+
+ `Content-Type: ${mimeType}\r\n\r\n`
|
|
18
|
+
)
|
|
19
|
+
const tail = Buffer.from(`\r\n--${boundary}--\r\n`)
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
boundary,
|
|
23
|
+
body: Buffer.concat([head, buffer, tail]),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createTestPngBuffer(width = 2000, height = 1000) {
|
|
28
|
+
const canvas = createCanvas(width, height)
|
|
29
|
+
const context = canvas.getContext('2d')
|
|
30
|
+
context.fillStyle = '#0ea5e9'
|
|
31
|
+
context.fillRect(0, 0, width, height)
|
|
32
|
+
return canvas.toBuffer('image/png')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function createTestSvgBuffer(width = 120, height = 80) {
|
|
36
|
+
return Buffer.from(
|
|
37
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">`
|
|
38
|
+
+ '<rect width="100%" height="100%" fill="#0ea5e9"/>'
|
|
39
|
+
+ '</svg>',
|
|
40
|
+
'utf8'
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
test('asset routes reject missing upload file', async () => {
|
|
45
|
+
const app = Fastify()
|
|
46
|
+
await app.register(multipart)
|
|
47
|
+
registerAssetRoutes(app, {
|
|
48
|
+
createTempFilePath: () => '',
|
|
49
|
+
importPdfBlocks: async () => ({ blocks: [] }),
|
|
50
|
+
normalizeUploadFileName: (name) => name,
|
|
51
|
+
removeAssetFiles: () => {},
|
|
52
|
+
tmpDir: 'tmp',
|
|
53
|
+
uploadsDir: 'uploads',
|
|
54
|
+
})
|
|
55
|
+
await app.ready()
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const uploadResponse = await app.inject({
|
|
59
|
+
method: 'POST',
|
|
60
|
+
url: '/api/uploads',
|
|
61
|
+
})
|
|
62
|
+
assert.equal(uploadResponse.statusCode, 406)
|
|
63
|
+
|
|
64
|
+
const pdfResponse = await app.inject({
|
|
65
|
+
method: 'POST',
|
|
66
|
+
url: '/api/imports/pdf',
|
|
67
|
+
})
|
|
68
|
+
assert.equal(pdfResponse.statusCode, 406)
|
|
69
|
+
} finally {
|
|
70
|
+
await app.close()
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('asset routes keep uploaded png format and original bytes', async () => {
|
|
75
|
+
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-asset-routes-'))
|
|
76
|
+
const uploadsDir = path.join(rootDir, 'uploads')
|
|
77
|
+
const tmpDir = path.join(rootDir, 'tmp')
|
|
78
|
+
fs.mkdirSync(uploadsDir, { recursive: true })
|
|
79
|
+
fs.mkdirSync(tmpDir, { recursive: true })
|
|
80
|
+
|
|
81
|
+
const app = Fastify()
|
|
82
|
+
await app.register(multipart)
|
|
83
|
+
registerAssetRoutes(app, {
|
|
84
|
+
createTempFilePath: (_dir, fileName) => path.join(tmpDir, fileName),
|
|
85
|
+
importPdfBlocks: async () => ({ blocks: [] }),
|
|
86
|
+
normalizeUploadFileName: (name) => name,
|
|
87
|
+
removeAssetFiles: () => {},
|
|
88
|
+
tmpDir,
|
|
89
|
+
uploadsDir,
|
|
90
|
+
})
|
|
91
|
+
await app.ready()
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const sourceBuffer = createTestPngBuffer()
|
|
95
|
+
const payload = createMultipartBody('demo.png', 'image/png', sourceBuffer)
|
|
96
|
+
const response = await app.inject({
|
|
97
|
+
method: 'POST',
|
|
98
|
+
url: '/api/uploads',
|
|
99
|
+
headers: {
|
|
100
|
+
'content-type': `multipart/form-data; boundary=${payload.boundary}`,
|
|
101
|
+
},
|
|
102
|
+
payload: payload.body,
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
assert.equal(response.statusCode, 201)
|
|
106
|
+
const body = response.json()
|
|
107
|
+
assert.match(body.url || '', /^\/uploads\/.+\.png$/)
|
|
108
|
+
assert.equal(body.width, 2000)
|
|
109
|
+
assert.equal(body.height, 1000)
|
|
110
|
+
assert.equal(body.mimeType, 'image/png')
|
|
111
|
+
assert.equal(typeof body.size, 'number')
|
|
112
|
+
assert.ok(body.size > 0)
|
|
113
|
+
|
|
114
|
+
const outputPath = path.join(uploadsDir, path.basename(body.url))
|
|
115
|
+
assert.equal(fs.existsSync(outputPath), true)
|
|
116
|
+
assert.deepEqual(fs.readFileSync(outputPath), sourceBuffer)
|
|
117
|
+
} finally {
|
|
118
|
+
await app.close()
|
|
119
|
+
fs.rmSync(rootDir, { recursive: true, force: true })
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('asset routes keep uploaded svg format and original bytes', async () => {
|
|
124
|
+
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-asset-routes-svg-'))
|
|
125
|
+
const uploadsDir = path.join(rootDir, 'uploads')
|
|
126
|
+
const tmpDir = path.join(rootDir, 'tmp')
|
|
127
|
+
fs.mkdirSync(uploadsDir, { recursive: true })
|
|
128
|
+
fs.mkdirSync(tmpDir, { recursive: true })
|
|
129
|
+
|
|
130
|
+
const app = Fastify()
|
|
131
|
+
await app.register(multipart)
|
|
132
|
+
registerAssetRoutes(app, {
|
|
133
|
+
createTempFilePath: (_dir, fileName) => path.join(tmpDir, fileName),
|
|
134
|
+
importPdfBlocks: async () => ({ blocks: [] }),
|
|
135
|
+
normalizeUploadFileName: (name) => name,
|
|
136
|
+
removeAssetFiles: () => {},
|
|
137
|
+
tmpDir,
|
|
138
|
+
uploadsDir,
|
|
139
|
+
})
|
|
140
|
+
await app.ready()
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const sourceBuffer = createTestSvgBuffer()
|
|
144
|
+
const payload = createMultipartBody('logo.svg', 'image/svg+xml', sourceBuffer)
|
|
145
|
+
const response = await app.inject({
|
|
146
|
+
method: 'POST',
|
|
147
|
+
url: '/api/uploads',
|
|
148
|
+
headers: {
|
|
149
|
+
'content-type': `multipart/form-data; boundary=${payload.boundary}`,
|
|
150
|
+
},
|
|
151
|
+
payload: payload.body,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
assert.equal(response.statusCode, 201)
|
|
155
|
+
const body = response.json()
|
|
156
|
+
assert.match(body.url || '', /^\/uploads\/.+\.svg$/)
|
|
157
|
+
assert.equal(body.mimeType, 'image/svg+xml')
|
|
158
|
+
assert.equal(typeof body.size, 'number')
|
|
159
|
+
assert.ok(body.size > 0)
|
|
160
|
+
|
|
161
|
+
const outputPath = path.join(uploadsDir, path.basename(body.url))
|
|
162
|
+
assert.equal(fs.existsSync(outputPath), true)
|
|
163
|
+
assert.deepEqual(fs.readFileSync(outputPath), sourceBuffer)
|
|
164
|
+
} finally {
|
|
165
|
+
await app.close()
|
|
166
|
+
fs.rmSync(rootDir, { recursive: true, force: true })
|
|
167
|
+
}
|
|
168
|
+
})
|