@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,30 +0,0 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
|
-
import test from 'node:test'
|
|
3
|
-
import assert from 'node:assert/strict'
|
|
4
|
-
import {
|
|
5
|
-
createTempFilePath,
|
|
6
|
-
getSafeTempExtension,
|
|
7
|
-
normalizeUploadFileName,
|
|
8
|
-
} from './upload.js'
|
|
9
|
-
|
|
10
|
-
test('normalizeUploadFileName 保留基础文件名并支持回退值', () => {
|
|
11
|
-
assert.equal(normalizeUploadFileName('C:\\temp\\测试文档.PDF'), '测试文档.PDF')
|
|
12
|
-
assert.equal(normalizeUploadFileName(''), 'file')
|
|
13
|
-
assert.equal(normalizeUploadFileName('', 'task.pdf'), 'task.pdf')
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
test('getSafeTempExtension 只保留安全扩展名', () => {
|
|
17
|
-
assert.equal(getSafeTempExtension('测试文档.PDF'), '.pdf')
|
|
18
|
-
assert.equal(getSafeTempExtension('archive.tar.gz'), '.gz')
|
|
19
|
-
assert.equal(getSafeTempExtension('README'), '')
|
|
20
|
-
assert.equal(getSafeTempExtension('evil.<script>', '.txt'), '.txt')
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test('createTempFilePath 生成 ASCII 临时文件名并保留扩展名', () => {
|
|
24
|
-
const tempPath = createTempFilePath('D:\\code\\tmpprompt\\apps\\server\\tmp', '受试者招募反馈.pdf', '.pdf')
|
|
25
|
-
const baseName = path.basename(tempPath)
|
|
26
|
-
|
|
27
|
-
assert.match(baseName, /^[A-Za-z0-9_-]{12}\.pdf$/)
|
|
28
|
-
assert.doesNotMatch(baseName, /[^\x00-\x7F]/)
|
|
29
|
-
assert.equal(path.dirname(tempPath), 'D:\\code\\tmpprompt\\apps\\server\\tmp')
|
|
30
|
-
})
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import Fastify from 'fastify'
|
|
3
|
-
import test from 'node:test'
|
|
4
|
-
|
|
5
|
-
import { registerWebAppRoutes } from './webAppRoutes.js'
|
|
6
|
-
|
|
7
|
-
test('web app routes do nothing when disabled', async () => {
|
|
8
|
-
const app = Fastify()
|
|
9
|
-
registerWebAppRoutes(app, {
|
|
10
|
-
enabled: false,
|
|
11
|
-
webDistDir: 'dist',
|
|
12
|
-
})
|
|
13
|
-
await app.ready()
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
const response = await app.inject({
|
|
17
|
-
method: 'GET',
|
|
18
|
-
url: '/',
|
|
19
|
-
})
|
|
20
|
-
assert.equal(response.statusCode, 404)
|
|
21
|
-
} finally {
|
|
22
|
-
await app.close()
|
|
23
|
-
}
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
test('web app routes keep api and uploads paths as 404 under catchall', async () => {
|
|
27
|
-
const app = Fastify()
|
|
28
|
-
app.decorateReply('sendFile', function sendFile(fileName, rootDir) {
|
|
29
|
-
return this.send({ fileName, rootDir })
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
registerWebAppRoutes(app, {
|
|
33
|
-
enabled: true,
|
|
34
|
-
webDistDir: 'dist',
|
|
35
|
-
})
|
|
36
|
-
await app.ready()
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
const rootResponse = await app.inject({
|
|
40
|
-
method: 'GET',
|
|
41
|
-
url: '/',
|
|
42
|
-
})
|
|
43
|
-
assert.equal(rootResponse.statusCode, 200)
|
|
44
|
-
assert.deepEqual(rootResponse.json(), {
|
|
45
|
-
fileName: 'index.html',
|
|
46
|
-
rootDir: 'dist',
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
const apiResponse = await app.inject({
|
|
50
|
-
method: 'GET',
|
|
51
|
-
url: '/api/tasks',
|
|
52
|
-
})
|
|
53
|
-
assert.equal(apiResponse.statusCode, 404)
|
|
54
|
-
|
|
55
|
-
const appResponse = await app.inject({
|
|
56
|
-
method: 'GET',
|
|
57
|
-
url: '/workspace/demo',
|
|
58
|
-
})
|
|
59
|
-
assert.equal(appResponse.statusCode, 200)
|
|
60
|
-
assert.deepEqual(appResponse.json(), {
|
|
61
|
-
fileName: 'index.html',
|
|
62
|
-
rootDir: 'dist',
|
|
63
|
-
})
|
|
64
|
-
} finally {
|
|
65
|
-
await app.close()
|
|
66
|
-
}
|
|
67
|
-
})
|
|
@@ -1,279 +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
|
-
import {
|
|
8
|
-
listDirectoryPickerTree,
|
|
9
|
-
listWorkspaceTree,
|
|
10
|
-
readWorkspaceFileContent,
|
|
11
|
-
searchWorkspaceFileContent,
|
|
12
|
-
searchWorkspaceEntries,
|
|
13
|
-
searchDirectoryPickerEntries,
|
|
14
|
-
} from './workspaceFiles.js'
|
|
15
|
-
|
|
16
|
-
test('listDirectoryPickerTree returns filesystem roots when path is empty', () => {
|
|
17
|
-
const payload = listDirectoryPickerTree()
|
|
18
|
-
|
|
19
|
-
assert.equal(payload.path, path.resolve(os.homedir()))
|
|
20
|
-
assert.equal(payload.parentPath, path.dirname(path.resolve(os.homedir())))
|
|
21
|
-
assert.equal(Array.isArray(payload.items), true)
|
|
22
|
-
assert.equal(Array.isArray(payload.roots), true)
|
|
23
|
-
assert.equal(payload.roots[0]?.path, path.resolve(os.homedir()))
|
|
24
|
-
assert.equal(payload.roots[0]?.type, 'directory')
|
|
25
|
-
assert.equal(typeof payload.roots[0]?.name, 'string')
|
|
26
|
-
assert.equal(Boolean(payload.roots[0]?.name), true)
|
|
27
|
-
assert.equal(payload.items.length > 0, true)
|
|
28
|
-
assert.equal(payload.items.every((item) => item.type === 'directory'), true)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test('listDirectoryPickerTree returns parentPath for nested paths', () => {
|
|
32
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-dir-picker-parent-'))
|
|
33
|
-
const childDir = path.join(tempDir, 'project-a')
|
|
34
|
-
|
|
35
|
-
fs.mkdirSync(childDir)
|
|
36
|
-
|
|
37
|
-
const payload = listDirectoryPickerTree({ path: childDir })
|
|
38
|
-
|
|
39
|
-
assert.equal(payload.path, childDir)
|
|
40
|
-
assert.equal(payload.parentPath, tempDir)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
test('listDirectoryPickerTree lists child directories and excludes files', () => {
|
|
44
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-dir-picker-'))
|
|
45
|
-
const childDir = path.join(tempDir, 'project-a')
|
|
46
|
-
const nestedDir = path.join(tempDir, 'project-b')
|
|
47
|
-
const hiddenDir = path.join(tempDir, '.secret')
|
|
48
|
-
const downloadsDir = path.join(tempDir, 'Downloads')
|
|
49
|
-
const filePath = path.join(tempDir, 'note.txt')
|
|
50
|
-
|
|
51
|
-
fs.mkdirSync(childDir)
|
|
52
|
-
fs.mkdirSync(nestedDir)
|
|
53
|
-
fs.mkdirSync(hiddenDir)
|
|
54
|
-
fs.mkdirSync(downloadsDir)
|
|
55
|
-
fs.writeFileSync(filePath, 'hello')
|
|
56
|
-
|
|
57
|
-
const payload = listDirectoryPickerTree({ path: tempDir })
|
|
58
|
-
|
|
59
|
-
assert.equal(payload.path, tempDir)
|
|
60
|
-
assert.equal(payload.items.some((item) => item.path === childDir), true)
|
|
61
|
-
assert.equal(payload.items.some((item) => item.path === nestedDir), true)
|
|
62
|
-
assert.equal(payload.items.some((item) => item.path === hiddenDir), false)
|
|
63
|
-
assert.equal(payload.items.some((item) => item.path === downloadsDir), false)
|
|
64
|
-
assert.equal(payload.items.some((item) => item.path === filePath), false)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
test('searchDirectoryPickerEntries returns matching directories only', () => {
|
|
68
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-dir-search-'))
|
|
69
|
-
const alphaDir = path.join(tempDir, 'alpha-project')
|
|
70
|
-
const betaDir = path.join(tempDir, 'beta-notes')
|
|
71
|
-
const hiddenAlphaDir = path.join(tempDir, '.alpha-hidden')
|
|
72
|
-
const alphaFile = path.join(tempDir, 'alpha-project.txt')
|
|
73
|
-
|
|
74
|
-
fs.mkdirSync(alphaDir)
|
|
75
|
-
fs.mkdirSync(betaDir)
|
|
76
|
-
fs.mkdirSync(hiddenAlphaDir)
|
|
77
|
-
fs.writeFileSync(alphaFile, 'hello')
|
|
78
|
-
|
|
79
|
-
const payload = searchDirectoryPickerEntries({
|
|
80
|
-
path: tempDir,
|
|
81
|
-
query: 'alpha',
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
assert.equal(payload.items.some((item) => item.path === alphaDir), true)
|
|
85
|
-
assert.equal(payload.items.some((item) => item.path === betaDir), false)
|
|
86
|
-
assert.equal(payload.items.some((item) => item.path === hiddenAlphaDir), false)
|
|
87
|
-
assert.equal(payload.items.some((item) => item.path === alphaFile), false)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
test('listWorkspaceTree keeps project tmp directory visible', () => {
|
|
91
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-tree-'))
|
|
92
|
-
const tmpDir = path.join(tempDir, 'tmp')
|
|
93
|
-
const uploadsDir = path.join(tempDir, 'uploads')
|
|
94
|
-
const nodeModulesDir = path.join(tempDir, 'node_modules')
|
|
95
|
-
|
|
96
|
-
fs.mkdirSync(tmpDir)
|
|
97
|
-
fs.mkdirSync(uploadsDir)
|
|
98
|
-
fs.mkdirSync(nodeModulesDir)
|
|
99
|
-
|
|
100
|
-
const payload = listWorkspaceTree(tempDir)
|
|
101
|
-
|
|
102
|
-
assert.equal(payload.items.some((item) => item.path === 'tmp' && item.type === 'directory'), true)
|
|
103
|
-
assert.equal(payload.items.some((item) => item.path === 'uploads' && item.type === 'directory'), true)
|
|
104
|
-
assert.equal(payload.items.some((item) => item.path === 'node_modules'), false)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
test('searchWorkspaceEntries keeps tmp files searchable', () => {
|
|
108
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-search-'))
|
|
109
|
-
const screenshotPath = path.join(tempDir, 'tmp', 'screenshots', 'mobile.png')
|
|
110
|
-
const hiddenPath = path.join(tempDir, 'node_modules', 'pkg', 'index.js')
|
|
111
|
-
|
|
112
|
-
fs.mkdirSync(path.dirname(screenshotPath), { recursive: true })
|
|
113
|
-
fs.mkdirSync(path.dirname(hiddenPath), { recursive: true })
|
|
114
|
-
fs.writeFileSync(screenshotPath, 'image')
|
|
115
|
-
fs.writeFileSync(hiddenPath, 'export default 1')
|
|
116
|
-
|
|
117
|
-
const payload = searchWorkspaceEntries(tempDir, {
|
|
118
|
-
query: 'mobile',
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
assert.equal(payload.items.some((item) => item.path === 'tmp/screenshots/mobile.png'), true)
|
|
122
|
-
assert.equal(payload.items.some((item) => item.path.includes('node_modules')), false)
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
test('searchWorkspaceFileContent finds text matches with line metadata', () => {
|
|
126
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-content-search-'))
|
|
127
|
-
const sourcePath = path.join(tempDir, 'src', 'main.ts')
|
|
128
|
-
const hiddenPath = path.join(tempDir, 'node_modules', 'pkg', 'index.js')
|
|
129
|
-
const binaryPath = path.join(tempDir, 'assets', 'icon.bin')
|
|
130
|
-
|
|
131
|
-
fs.mkdirSync(path.dirname(sourcePath), { recursive: true })
|
|
132
|
-
fs.mkdirSync(path.dirname(hiddenPath), { recursive: true })
|
|
133
|
-
fs.mkdirSync(path.dirname(binaryPath), { recursive: true })
|
|
134
|
-
|
|
135
|
-
fs.writeFileSync(sourcePath, 'const answer = 42\nconsole.log("needle here")\n', 'utf8')
|
|
136
|
-
fs.writeFileSync(hiddenPath, 'needle in hidden dependency', 'utf8')
|
|
137
|
-
fs.writeFileSync(binaryPath, Buffer.from([0x00, 0x01, 0x02, 0x03]))
|
|
138
|
-
|
|
139
|
-
const payload = searchWorkspaceFileContent(tempDir, {
|
|
140
|
-
query: 'needle',
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
assert.equal(payload.items.length, 1)
|
|
144
|
-
assert.deepEqual(payload.items[0], {
|
|
145
|
-
name: 'main.ts',
|
|
146
|
-
path: 'src/main.ts',
|
|
147
|
-
type: 'file',
|
|
148
|
-
line: 2,
|
|
149
|
-
column: 14,
|
|
150
|
-
preview: 'console.log("needle here")',
|
|
151
|
-
})
|
|
152
|
-
assert.equal(payload.items.some((item) => item.path.includes('node_modules')), false)
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
test('searchWorkspaceFileContent dedupes repeated matches on the same line', () => {
|
|
156
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-content-dedupe-'))
|
|
157
|
-
const sourcePath = path.join(tempDir, 'src', 'main.ts')
|
|
158
|
-
|
|
159
|
-
fs.mkdirSync(path.dirname(sourcePath), { recursive: true })
|
|
160
|
-
fs.writeFileSync(sourcePath, 'const needle = "needle needle"\n', 'utf8')
|
|
161
|
-
|
|
162
|
-
const payload = searchWorkspaceFileContent(tempDir, {
|
|
163
|
-
query: 'needle',
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
assert.equal(payload.items.length, 1)
|
|
167
|
-
assert.equal(payload.items[0].line, 1)
|
|
168
|
-
assert.equal(payload.items[0].column, 7)
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
test('readWorkspaceFileContent returns text preview for workspace file', () => {
|
|
172
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-file-'))
|
|
173
|
-
const filePath = path.join(tempDir, 'src', 'main.ts')
|
|
174
|
-
|
|
175
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
176
|
-
fs.writeFileSync(filePath, 'export const answer = 42\n', 'utf8')
|
|
177
|
-
|
|
178
|
-
const payload = readWorkspaceFileContent(tempDir, {
|
|
179
|
-
path: 'src/main.ts',
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
assert.equal(payload.path, 'src/main.ts')
|
|
183
|
-
assert.equal(payload.language, 'typescript')
|
|
184
|
-
assert.equal(payload.binary, false)
|
|
185
|
-
assert.equal(payload.content, 'export const answer = 42\n')
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
test('readWorkspaceFileContent returns image preview for svg file', () => {
|
|
189
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-svg-'))
|
|
190
|
-
const filePath = path.join(tempDir, 'assets', 'logo.svg')
|
|
191
|
-
|
|
192
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
193
|
-
fs.writeFileSync(
|
|
194
|
-
filePath,
|
|
195
|
-
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="#18ac71"/></svg>',
|
|
196
|
-
'utf8'
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
const payload = readWorkspaceFileContent(tempDir, {
|
|
200
|
-
path: 'assets/logo.svg',
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
assert.equal(payload.path, 'assets/logo.svg')
|
|
204
|
-
assert.equal(payload.mimeType, 'image/svg+xml')
|
|
205
|
-
assert.equal(payload.binary, true)
|
|
206
|
-
assert.match(payload.previewUrl, /^data:image\/svg\+xml;base64,/)
|
|
207
|
-
assert.equal(payload.content, '')
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
test('readWorkspaceFileContent detects language for extensionless python script by file name', () => {
|
|
211
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-pip3-'))
|
|
212
|
-
const filePath = path.join(tempDir, 'bin', 'pip3')
|
|
213
|
-
|
|
214
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
215
|
-
fs.writeFileSync(filePath, '#!/usr/bin/python3\nprint("hello")\n', 'utf8')
|
|
216
|
-
|
|
217
|
-
const payload = readWorkspaceFileContent(tempDir, {
|
|
218
|
-
path: 'bin/pip3',
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
assert.equal(payload.language, 'python')
|
|
222
|
-
assert.equal(payload.binary, false)
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
test('readWorkspaceFileContent detects shell language variants', () => {
|
|
226
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-shell-variants-'))
|
|
227
|
-
const fishPath = path.join(tempDir, 'scripts', 'hello.fish')
|
|
228
|
-
const ps1Path = path.join(tempDir, 'scripts', 'hello.ps1')
|
|
229
|
-
const cshPath = path.join(tempDir, 'scripts', 'hello.csh')
|
|
230
|
-
|
|
231
|
-
fs.mkdirSync(path.dirname(fishPath), { recursive: true })
|
|
232
|
-
fs.writeFileSync(fishPath, 'echo hello\n', 'utf8')
|
|
233
|
-
fs.writeFileSync(ps1Path, 'Write-Host "hello"\n', 'utf8')
|
|
234
|
-
fs.writeFileSync(cshPath, 'echo hello\n', 'utf8')
|
|
235
|
-
|
|
236
|
-
assert.equal(readWorkspaceFileContent(tempDir, { path: 'scripts/hello.fish' }).language, 'fish')
|
|
237
|
-
assert.equal(readWorkspaceFileContent(tempDir, { path: 'scripts/hello.ps1' }).language, 'powershell')
|
|
238
|
-
assert.equal(readWorkspaceFileContent(tempDir, { path: 'scripts/hello.csh' }).language, 'bash')
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
test('readWorkspaceFileContent detects common style and config languages', () => {
|
|
242
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-style-config-'))
|
|
243
|
-
const scssPath = path.join(tempDir, 'src', 'style.scss')
|
|
244
|
-
const envPath = path.join(tempDir, '.env')
|
|
245
|
-
const tomlPath = path.join(tempDir, 'config.toml')
|
|
246
|
-
|
|
247
|
-
fs.mkdirSync(path.dirname(scssPath), { recursive: true })
|
|
248
|
-
fs.writeFileSync(scssPath, '$color: #18ac71;\n.button { color: $color; }\n', 'utf8')
|
|
249
|
-
fs.writeFileSync(envPath, 'PROMPTX_TEST=1\n', 'utf8')
|
|
250
|
-
fs.writeFileSync(tomlPath, 'name = "promptx"\n', 'utf8')
|
|
251
|
-
|
|
252
|
-
assert.equal(readWorkspaceFileContent(tempDir, { path: 'src/style.scss' }).language, 'scss')
|
|
253
|
-
assert.equal(readWorkspaceFileContent(tempDir, { path: '.env' }).language, 'dotenv')
|
|
254
|
-
assert.equal(readWorkspaceFileContent(tempDir, { path: 'config.toml' }).language, 'toml')
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
test('readWorkspaceFileContent blocks paths outside workspace', () => {
|
|
258
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-escape-'))
|
|
259
|
-
|
|
260
|
-
assert.throws(() => {
|
|
261
|
-
readWorkspaceFileContent(tempDir, {
|
|
262
|
-
path: '../secret.txt',
|
|
263
|
-
})
|
|
264
|
-
}, /路径不合法|只能访问当前工作目录内的文件/)
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
test('readWorkspaceFileContent marks binary files', () => {
|
|
268
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'promptx-workspace-binary-'))
|
|
269
|
-
const filePath = path.join(tempDir, 'sample.bin')
|
|
270
|
-
|
|
271
|
-
fs.writeFileSync(filePath, Buffer.from([0x00, 0x01, 0x02, 0x03, 0xff]))
|
|
272
|
-
|
|
273
|
-
const payload = readWorkspaceFileContent(tempDir, {
|
|
274
|
-
path: 'sample.bin',
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
assert.equal(payload.binary, true)
|
|
278
|
-
assert.equal(payload.content, '')
|
|
279
|
-
})
|