|
@@ -75,8 +75,8 @@ it.skipIf(!process.env.YAOYAO_HERMES_PYTHON||!process.env.YAOYAO_COMPUTER_IMAGE)
|
|
|
const server=createServer(app.app.callback());await new Promise<void>(resolve=>server.listen(0,'127.0.0.1',resolve));app.config.port=(server.address() as {port:number}).port
|
|
const server=createServer(app.app.callback());await new Promise<void>(resolve=>server.listen(0,'127.0.0.1',resolve));app.config.port=(server.address() as {port:number}).port
|
|
|
const address=`http://127.0.0.1:${app.config.port}`,abort=new AbortController();let running:Promise<void>|undefined
|
|
const address=`http://127.0.0.1:${app.config.port}`,abort=new AbortController();let running:Promise<void>|undefined
|
|
|
try{
|
|
try{
|
|
|
- const bootstrap=await request(app.app.callback()).get('/api/app/bootstrap').set('Host',`127.0.0.1:${app.config.port}`).expect(200)
|
|
|
|
|
- const setup=await request(app.app.callback()).post('/api/app/setup').set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',bootstrap.headers['set-cookie'].map((cookie:string)=>cookie.split(';')[0]).join('; ')).set('X-CSRF-Token',bootstrap.body.csrfToken).send({username:'worker-fixture',password:'worker-fixture-password'}).expect(200)
|
|
|
|
|
|
|
+ const bootstrap=await request(address).get('/api/app/bootstrap').set('Host',`127.0.0.1:${app.config.port}`).expect(200)
|
|
|
|
|
+ const setup=await request(address).post('/api/app/setup').set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',bootstrap.headers['set-cookie'].map((cookie:string)=>cookie.split(';')[0]).join('; ')).set('X-CSRF-Token',bootstrap.body.csrfToken).send({username:'worker-fixture',password:'worker-fixture-password'}).expect(200)
|
|
|
const owner=setup.body.user.id,registered=app.runners.enroll(owner,{name:'Worker 验收节点',allowedProfiles:['default']})
|
|
const owner=setup.body.user.id,registered=app.runners.enroll(owner,{name:'Worker 验收节点',allowedProfiles:['default']})
|
|
|
const runnerConfig={protocol:1,serverURL:address,runnerId:registered.runner.id,token:registered.token,hermesURL:upstreamURL,allowedProfiles:['default'],artifactRoots:[],computers:{network:process.env.YAOYAO_WORKER_PUBLIC_NETWORK==='1'?'public-proxy':'none',runtime:'docker',imageId:process.env.YAOYAO_COMPUTER_IMAGE!,python:process.env.YAOYAO_HERMES_PYTHON!,hermesSource:process.env.YAOYAO_HERMES_SOURCE!,hermesHome}}
|
|
const runnerConfig={protocol:1,serverURL:address,runnerId:registered.runner.id,token:registered.token,hermesURL:upstreamURL,allowedProfiles:['default'],artifactRoots:[],computers:{network:process.env.YAOYAO_WORKER_PUBLIC_NETWORK==='1'?'public-proxy':'none',runtime:'docker',imageId:process.env.YAOYAO_COMPUTER_IMAGE!,python:process.env.YAOYAO_HERMES_PYTHON!,hermesSource:process.env.YAOYAO_HERMES_SOURCE!,hermesHome}}
|
|
|
if(process.env.YAOYAO_WORKER_RUNNER_BUNDLE){
|
|
if(process.env.YAOYAO_WORKER_RUNNER_BUNDLE){
|
|
@@ -90,10 +90,10 @@ it.skipIf(!process.env.YAOYAO_HERMES_PYTHON||!process.env.YAOYAO_COMPUTER_IMAGE)
|
|
|
void running.catch(()=>{})
|
|
void running.catch(()=>{})
|
|
|
await expect.poll(()=>app.runners.summary(app.runners.records()[0]!).online).toBe(true)
|
|
await expect.poll(()=>app.runners.summary(app.runners.records()[0]!).online).toBe(true)
|
|
|
const imageCookies=setup.headers['set-cookie'].map((cookie:string)=>cookie.split(';')[0]).join('; ')
|
|
const imageCookies=setup.headers['set-cookie'].map((cookie:string)=>cookie.split(';')[0]).join('; ')
|
|
|
- const imageBootstrap=await request(app.app.callback()).get('/api/app/bootstrap').set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',imageCookies).expect(200)
|
|
|
|
|
|
|
+ const imageBootstrap=await request(address).get('/api/app/bootstrap').set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',imageCookies).expect(200)
|
|
|
const imageBase=`/api/app/admin/runners/${registered.runner.id}/images`
|
|
const imageBase=`/api/app/admin/runners/${registered.runner.id}/images`
|
|
|
- const imagesGet=async()=>(await request(app.app.callback()).get(imageBase).set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',imageCookies).expect(200)).body
|
|
|
|
|
- const imagesPost=(body:unknown)=>request(app.app.callback()).post(imageBase+'/jobs').set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',imageCookies).set('X-CSRF-Token',imageBootstrap.body.csrfToken).send(body)
|
|
|
|
|
|
|
+ const imagesGet=async()=>(await request(address).get(imageBase).set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',imageCookies).expect(200)).body
|
|
|
|
|
+ const imagesPost=(body:unknown)=>request(address).post(imageBase+'/jobs').set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',imageCookies).set('X-CSRF-Token',imageBootstrap.body.csrfToken).send(body)
|
|
|
const prepareId=randomUUID();await imagesPost({requestId:prepareId,operation:'prepare'}).expect(202)
|
|
const prepareId=randomUUID();await imagesPost({requestId:prepareId,operation:'prepare'}).expect(202)
|
|
|
let prepared:any
|
|
let prepared:any
|
|
|
await expect.poll(async()=>{prepared=(await imagesGet()).jobs.find((j:any)=>j.id===prepareId);return prepared?.state},{timeout:90000}).toMatch(/complete|failed/)
|
|
await expect.poll(async()=>{prepared=(await imagesGet()).jobs.find((j:any)=>j.id===prepareId);return prepared?.state},{timeout:90000}).toMatch(/complete|failed/)
|
|
@@ -108,11 +108,11 @@ it.skipIf(!process.env.YAOYAO_HERMES_PYTHON||!process.env.YAOYAO_COMPUTER_IMAGE)
|
|
|
let exportedImage:any
|
|
let exportedImage:any
|
|
|
await expect.poll(async()=>{exportedImage=(await imagesGet()).jobs.find((j:any)=>j.id===exportId);return exportedImage?.state},{timeout:60000}).toBe('complete')
|
|
await expect.poll(async()=>{exportedImage=(await imagesGet()).jobs.find((j:any)=>j.id===exportId);return exportedImage?.state},{timeout:60000}).toBe('complete')
|
|
|
const uploadId=randomUUID()
|
|
const uploadId=randomUUID()
|
|
|
- const uploadAPI=(path:string,body:unknown)=>request(app.app.callback()).post(imageBase+path).set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',imageCookies).set('X-CSRF-Token',imageBootstrap.body.csrfToken).send(body)
|
|
|
|
|
|
|
+ const uploadAPI=(path:string,body:unknown)=>request(address).post(imageBase+path).set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',imageCookies).set('X-CSRF-Token',imageBootstrap.body.csrfToken).send(body)
|
|
|
await uploadAPI('/uploads',{id:uploadId,size:exportedImage.archiveSize,manifest:exportedImage.manifest}).expect(200)
|
|
await uploadAPI('/uploads',{id:uploadId,size:exportedImage.archiveSize,manifest:exportedImage.manifest}).expect(200)
|
|
|
const {createHash}=await import('node:crypto');const archiveHash=createHash('sha256')
|
|
const {createHash}=await import('node:crypto');const archiveHash=createHash('sha256')
|
|
|
for(let offset=0;offset<exportedImage.archiveSize;){
|
|
for(let offset=0;offset<exportedImage.archiveSize;){
|
|
|
- const part=(await request(app.app.callback()).get(imageBase+`/jobs/${exportId}/chunk?offset=${offset}`).set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',imageCookies).expect(200)).body
|
|
|
|
|
|
|
+ const part=(await request(address).get(imageBase+`/jobs/${exportId}/chunk?offset=${offset}`).set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',imageCookies).expect(200)).body
|
|
|
const bytes=Buffer.from(part.data,'base64');archiveHash.update(bytes)
|
|
const bytes=Buffer.from(part.data,'base64');archiveHash.update(bytes)
|
|
|
const accepted=await uploadAPI(`/uploads/${uploadId}`,{offset,data:part.data}).expect(200);offset+=bytes.length;expect(accepted.body.offset).toBe(offset)
|
|
const accepted=await uploadAPI(`/uploads/${uploadId}`,{offset,data:part.data}).expect(200);offset+=bytes.length;expect(accepted.body.offset).toBe(offset)
|
|
|
}
|
|
}
|
|
@@ -131,7 +131,7 @@ it.skipIf(!process.env.YAOYAO_HERMES_PYTHON||!process.env.YAOYAO_COMPUTER_IMAGE)
|
|
|
expect(message.attachments).toHaveLength(2)
|
|
expect(message.attachments).toHaveLength(2)
|
|
|
const report=message.attachments.find(file=>file.name==='result.txt')!
|
|
const report=message.attachments.find(file=>file.name==='result.txt')!
|
|
|
expect(report).toBeDefined()
|
|
expect(report).toBeDefined()
|
|
|
- const download=await request(app.app.callback()).get(`/api/app/files/${report.id}/download`).set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',setup.headers['set-cookie'].map((cookie:string)=>cookie.split(';')[0]).join('; ')).expect(200)
|
|
|
|
|
|
|
+ const download=await request(address).get(`/api/app/files/${report.id}/download`).set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',setup.headers['set-cookie'].map((cookie:string)=>cookie.split(';')[0]).join('; ')).expect(200)
|
|
|
expect(download.text).toBe('worker-proof')
|
|
expect(download.text).toBe('worker-proof')
|
|
|
const files=app.workspace.list<any>(owner,'file');expect(await readFile(files.find(file=>file.name==='result.txt').path,'utf8')).toBe('worker-proof')
|
|
const files=app.workspace.list<any>(owner,'file');expect(await readFile(files.find(file=>file.name==='result.txt').path,'utf8')).toBe('worker-proof')
|
|
|
const bulk=files.find(file=>file.name==='bulk.bin');expect((await readFile(bulk.path)).length).toBe(2100000)
|
|
const bulk=files.find(file=>file.name==='bulk.bin');expect((await readFile(bulk.path)).length).toBe(2100000)
|
|
@@ -146,14 +146,14 @@ it.skipIf(!process.env.YAOYAO_HERMES_PYTHON||!process.env.YAOYAO_COMPUTER_IMAGE)
|
|
|
const controlled=app.workspaceRuntime.send(owner,direct.id,{requestId:randomUUID(),content:'fixture:control'})
|
|
const controlled=app.workspaceRuntime.send(owner,direct.id,{requestId:randomUUID(),content:'fixture:control'})
|
|
|
await expect.poll(()=>app.workspace.messages(owner,direct.id).some(message=>message.runId===controlled.id&&message.tools.some(tool=>tool.name==='computer_shell')),{timeout:30000}).toBe(true)
|
|
await expect.poll(()=>app.workspace.messages(owner,direct.id).some(message=>message.runId===controlled.id&&message.tools.some(tool=>tool.name==='computer_shell')),{timeout:30000}).toBe(true)
|
|
|
const cookies=setup.headers['set-cookie'].map((cookie:string)=>cookie.split(';')[0]).join('; ')
|
|
const cookies=setup.headers['set-cookie'].map((cookie:string)=>cookie.split(';')[0]).join('; ')
|
|
|
- const fresh=await request(app.app.callback()).get('/api/app/bootstrap').set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',cookies).expect(200)
|
|
|
|
|
|
|
+ const fresh=await request(address).get('/api/app/bootstrap').set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',cookies).expect(200)
|
|
|
const endpoint=`/api/app/agents/${worker.id}/computer`
|
|
const endpoint=`/api/app/agents/${worker.id}/computer`
|
|
|
- const api=(path:string,data:unknown)=>request(app.app.callback()).post(endpoint+path).set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',cookies).set('X-CSRF-Token',fresh.body.csrfToken).send(data)
|
|
|
|
|
|
|
+ const api=(path:string,data:unknown)=>request(address).post(endpoint+path).set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',cookies).set('X-CSRF-Token',fresh.body.csrfToken).send(data)
|
|
|
const taken=await api('/take',{requestId:randomUUID()}).expect(200),ticket={controlId:taken.body.controlId,token:taken.body.token}
|
|
const taken=await api('/take',{requestId:randomUUID()}).expect(200),ticket={controlId:taken.body.controlId,token:taken.body.token}
|
|
|
- const status=async()=>(await request(app.app.callback()).get(endpoint).set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',cookies)).body
|
|
|
|
|
|
|
+ const status=async()=>(await request(address).get(endpoint).set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',cookies)).body
|
|
|
await expect.poll(async()=>(await status()).mode,{timeout:30000}).toBe('human')
|
|
await expect.poll(async()=>(await status()).mode,{timeout:30000}).toBe('human')
|
|
|
expect(app.workspace.require<any>(owner,'run',controlled.id).status).toBe('waiting')
|
|
expect(app.workspace.require<any>(owner,'run',controlled.id).status).toBe('waiting')
|
|
|
- const capture=async()=>(await request(app.app.callback()).get(endpoint+'/frame').set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',cookies)).body
|
|
|
|
|
|
|
+ const capture=async()=>(await request(address).get(endpoint+'/frame').set('Host',`127.0.0.1:${app.config.port}`).set('Cookie',cookies)).body
|
|
|
let frame:any
|
|
let frame:any
|
|
|
await expect.poll(async()=>{frame=await capture();return frame.width},{timeout:20000}).toBeGreaterThan(0)
|
|
await expect.poll(async()=>{frame=await capture();return frame.width},{timeout:20000}).toBeGreaterThan(0)
|
|
|
const click=await api('/input',{...ticket,requestId:randomUUID(),generation:frame.generation,frameId:frame.id,action:{kind:'click',x:568,y:875}}).expect(200)
|
|
const click=await api('/input',{...ticket,requestId:randomUUID(),generation:frame.generation,frameId:frame.id,action:{kind:'click',x:568,y:875}}).expect(200)
|
|
@@ -175,7 +175,7 @@ it.skipIf(!process.env.YAOYAO_HERMES_PYTHON||!process.env.YAOYAO_COMPUTER_IMAGE)
|
|
|
await expect(access(join(runnerHome,'computer-workspaces',worker.id,'late-write.txt'))).rejects.toMatchObject({code:'ENOENT'})
|
|
await expect(access(join(runnerHome,'computer-workspaces',worker.id,'late-write.txt'))).rejects.toMatchObject({code:'ENOENT'})
|
|
|
const second=app.workspace.createAgent(owner,{name:'共享核验成员',profile:'default',execution:'computer'})
|
|
const second=app.workspace.createAgent(owner,{name:'共享核验成员',profile:'default',execution:'computer'})
|
|
|
const secondChat=app.workspace.list<any>(owner,'conversation').find(c=>c.memberIds[0]===second.id)!
|
|
const secondChat=app.workspace.list<any>(owner,'conversation').find(c=>c.memberIds[0]===second.id)!
|
|
|
- const sharedAPI=(method:'post'|'delete',path:string,data:unknown)=>request(app.app.callback())[method](path).set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',cookies).set('X-CSRF-Token',fresh.body.csrfToken).send(data)
|
|
|
|
|
|
|
+ const sharedAPI=(method:'post'|'delete',path:string,data:unknown)=>request(address)[method](path).set('Host',`127.0.0.1:${app.config.port}`).set('Origin',address).set('Cookie',cookies).set('X-CSRF-Token',fresh.body.csrfToken).send(data)
|
|
|
const shared=await sharedAPI('post','/api/app/computers/shared',{requestId:randomUUID(),name:'真实共享电脑',memberIds:[worker.id,second.id],trusted:true}).expect(201)
|
|
const shared=await sharedAPI('post','/api/app/computers/shared',{requestId:randomUUID(),name:'真实共享电脑',memberIds:[worker.id,second.id],trusted:true}).expect(201)
|
|
|
const sharedWrite=app.workspaceRuntime.send(owner,direct.id,{requestId:randomUUID(),content:'fixture:shared-write'})
|
|
const sharedWrite=app.workspaceRuntime.send(owner,direct.id,{requestId:randomUUID(),content:'fixture:shared-write'})
|
|
|
await expect.poll(()=>app.workspace.messages(owner,direct.id).some(message=>message.runId===sharedWrite.id&&message.tools.some(tool=>tool.name==='computer_shell')),{timeout:30000}).toBe(true)
|
|
await expect.poll(()=>app.workspace.messages(owner,direct.id).some(message=>message.runId===sharedWrite.id&&message.tools.some(tool=>tool.name==='computer_shell')),{timeout:30000}).toBe(true)
|