@zonease/aiworker-cli 0.12.2 → 0.13.0
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/README.md +158 -350
- package/aiworker-bun.js +614 -694
- package/drizzle/worker/0000_polite_stellaris.sql +219 -0
- package/drizzle/worker/0001_red_lady_mastermind.sql +29 -0
- package/drizzle/worker/0002_concerned_slyde.sql +45 -0
- package/drizzle/worker/meta/0000_snapshot.json +1055 -194
- package/drizzle/worker/meta/0001_snapshot.json +1244 -222
- package/drizzle/worker/meta/0002_snapshot.json +1557 -273
- package/drizzle/worker/meta/_journal.json +6 -48
- package/official-apps/aiworker-hr/README.md +16 -0
- package/official-apps/aiworker-hr/capabilities/candidate-screen/prompt.md +3 -0
- package/official-apps/aiworker-hr/capabilities/candidate-screen/review.md +5 -0
- package/official-apps/aiworker-hr/capabilities/person-profile/prompt.md +3 -0
- package/official-apps/aiworker-hr/capabilities/person-profile/review.md +5 -0
- package/official-apps/aiworker-hr/dist/host-mounted.js +15677 -0
- package/official-apps/aiworker-hr/dist/index.js +15411 -0
- package/official-apps/aiworker-hr/dist/standalone.js +15451 -0
- package/official-apps/aiworker-hr/migrations/0001_hr.sql +2 -0
- package/official-apps/aiworker-hr/package.json +31 -0
- package/official-apps/aiworker-hr/packs/hr-recruiting/SOUL.md +7 -0
- package/official-apps/aiworker-hr/review/candidate-screen.md +5 -0
- package/official-apps/aiworker-hr/review/person-profile.md +5 -0
- package/official-apps/aiworker-hr/schemas/candidate-screen.schema.json +50 -0
- package/official-apps/aiworker-hr/schemas/person-profile.schema.json +50 -0
- package/official-apps/aiworker-hr/soul-app.manifest.json +374 -0
- package/official-apps/aiworker-hr/src/api.ts +1 -0
- package/official-apps/aiworker-hr/src/host-mounted.ts +308 -0
- package/official-apps/aiworker-hr/src/index.ts +152 -0
- package/official-apps/aiworker-hr/src/protocol/artifact.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/connectors.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/lifecycle.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/review.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/runtime.ts +2 -0
- package/official-apps/aiworker-hr/src/protocol/ui.ts +2 -0
- package/official-apps/aiworker-hr/src/standalone.ts +43 -0
- package/official-apps/aiworker-hr/src/ui/candidate-screen-preview.tsx +2 -0
- package/official-apps/aiworker-hr/src/ui/hr-route.tsx +1 -0
- package/official-apps/aiworker-hr/src/ui/people-widget.tsx +1 -0
- package/official-apps/aiworker-hr/src/ui/person-profile-preview.tsx +2 -0
- package/official-apps/aiworker-hr/src/ui/profile-panel.tsx +1 -0
- package/official-apps/aiworker-hr/src/ui/review-panel.tsx +1 -0
- package/official-apps/aiworker-hr/tsconfig.json +20 -0
- package/official-apps/aiworker-qa/README.md +14 -0
- package/official-apps/aiworker-qa/capabilities/regression-matrix/prompt.md +3 -0
- package/official-apps/aiworker-qa/capabilities/regression-matrix/review.md +5 -0
- package/official-apps/aiworker-qa/capabilities/release-gate/prompt.md +3 -0
- package/official-apps/aiworker-qa/capabilities/release-gate/review.md +5 -0
- package/official-apps/aiworker-qa/dist/host-mounted.js +15655 -0
- package/official-apps/aiworker-qa/dist/index.js +15395 -0
- package/official-apps/aiworker-qa/dist/standalone.js +15435 -0
- package/official-apps/aiworker-qa/migrations/0001_qa.sql +2 -0
- package/official-apps/aiworker-qa/package.json +31 -0
- package/official-apps/aiworker-qa/packs/qa-reviewer/SOUL.md +7 -0
- package/official-apps/aiworker-qa/review/regression-matrix.md +5 -0
- package/official-apps/aiworker-qa/review/release-gate.md +5 -0
- package/official-apps/aiworker-qa/schemas/regression-matrix.schema.json +50 -0
- package/official-apps/aiworker-qa/schemas/release-gate.schema.json +50 -0
- package/official-apps/aiworker-qa/soul-app.manifest.json +356 -0
- package/official-apps/aiworker-qa/src/api.ts +1 -0
- package/official-apps/aiworker-qa/src/host-mounted.ts +302 -0
- package/official-apps/aiworker-qa/src/index.ts +152 -0
- package/official-apps/aiworker-qa/src/protocol/artifact.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/connectors.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/lifecycle.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/review.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/runtime.ts +2 -0
- package/official-apps/aiworker-qa/src/protocol/ui.ts +2 -0
- package/official-apps/aiworker-qa/src/standalone.ts +43 -0
- package/official-apps/aiworker-qa/src/ui/qa-route.tsx +1 -0
- package/official-apps/aiworker-qa/src/ui/regression-matrix-preview.tsx +2 -0
- package/official-apps/aiworker-qa/src/ui/release-gate-preview.tsx +2 -0
- package/official-apps/aiworker-qa/src/ui/release-panel.tsx +1 -0
- package/official-apps/aiworker-qa/src/ui/release-review-panel.tsx +1 -0
- package/official-apps/aiworker-qa/src/ui/release-widget.tsx +1 -0
- package/official-apps/aiworker-qa/src/ui/review-panel.tsx +1 -0
- package/official-apps/aiworker-qa/tsconfig.json +20 -0
- package/package.json +5 -4
- package/web/worker/assets/index-ByOwFiyz.js +18 -0
- package/web/worker/assets/index-K-y56wrL.css +2 -0
- package/web/worker/assets/markdown-preview-DFe-rfff.js +29 -0
- package/web/worker/assets/people-workbench-V1Ajqfzv.js +1 -0
- package/web/worker/engine-icons/claude.svg +1 -0
- package/web/worker/engine-icons/cursor.svg +1 -0
- package/web/worker/engine-icons/gemini.svg +1 -0
- package/web/worker/engine-icons/hermesagent.svg +1 -0
- package/web/worker/engine-icons/openai.svg +1 -0
- package/web/worker/engine-icons/opencode.svg +1 -0
- package/web/worker/engine-icons/qwen.svg +1 -0
- package/web/worker/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/web/worker/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
- package/web/worker/fonts/nunito-latin-wght-normal.woff2 +0 -0
- package/web/worker/index.html +8 -4
- package/web/worker/logo.svg +8 -0
- package/drizzle/fleet/0000_fine_havok.sql +0 -23
- package/drizzle/fleet/meta/0000_snapshot.json +0 -165
- package/drizzle/fleet/meta/_journal.json +0 -13
- package/drizzle/worker/0000_spooky_kat_farrell.sql +0 -112
- package/drizzle/worker/0001_secret_dagger.sql +0 -1
- package/drizzle/worker/0002_jazzy_moondragon.sql +0 -13
- package/drizzle/worker/0003_rare_cloak.sql +0 -7
- package/drizzle/worker/0004_daffy_thing.sql +0 -26
- package/drizzle/worker/0005_worthless_whiplash.sql +0 -20
- package/drizzle/worker/0006_fair_jetstream.sql +0 -34
- package/drizzle/worker/0007_solid_bromley.sql +0 -11
- package/drizzle/worker/0008_peaceful_titanium_man.sql +0 -14
- package/drizzle/worker/meta/0003_snapshot.json +0 -873
- package/drizzle/worker/meta/0004_snapshot.json +0 -1058
- package/drizzle/worker/meta/0005_snapshot.json +0 -1192
- package/drizzle/worker/meta/0006_snapshot.json +0 -1420
- package/drizzle/worker/meta/0007_snapshot.json +0 -1489
- package/drizzle/worker/meta/0008_snapshot.json +0 -1593
- package/web/fleet/assets/index-BTknRPEg.js +0 -1372
- package/web/fleet/assets/index-lu-9OhC0.css +0 -2
- package/web/fleet/favicon.svg +0 -4
- package/web/fleet/index.html +0 -14
- package/web/worker/assets/index-DuxsPbd7.js +0 -1382
- package/web/worker/assets/index-lu-9OhC0.css +0 -2
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
|
|
4
|
+
import { createSoulAppClient } from '@zonease/aiworker-soul-app-sdk'
|
|
5
|
+
|
|
6
|
+
import { qaReferenceSoulApp, qaSoulAppManifest } from './index'
|
|
7
|
+
|
|
8
|
+
interface MountContext {
|
|
9
|
+
brokerUrl?: string
|
|
10
|
+
hostUrl?: string
|
|
11
|
+
operatorId?: string | null
|
|
12
|
+
sessionId?: string | null
|
|
13
|
+
surface?: {
|
|
14
|
+
id?: string
|
|
15
|
+
scope?: string
|
|
16
|
+
}
|
|
17
|
+
workerId?: string | null
|
|
18
|
+
workspaceId?: string | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface BrokerSearchResult {
|
|
22
|
+
items?: Array<Record<string, unknown>>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function serveHostMounted(port = Number(Bun.env.PORT ?? 0)) {
|
|
26
|
+
return Bun.serve({
|
|
27
|
+
async fetch(request) {
|
|
28
|
+
const url = new URL(request.url)
|
|
29
|
+
if (url.pathname === '/health') {
|
|
30
|
+
return Response.json({
|
|
31
|
+
appId: qaSoulAppManifest.id,
|
|
32
|
+
mode: 'host-mounted',
|
|
33
|
+
status: 'ok',
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
const tokenError = verifyMountToken(request)
|
|
37
|
+
if (tokenError)
|
|
38
|
+
return tokenError
|
|
39
|
+
if (url.pathname === '/domain') {
|
|
40
|
+
return Response.json({
|
|
41
|
+
appId: qaSoulAppManifest.id,
|
|
42
|
+
capabilities: qaSoulAppManifest.capabilities.map(capability => capability.id),
|
|
43
|
+
mounted: true,
|
|
44
|
+
soul: qaSoulAppManifest.soul.id,
|
|
45
|
+
workspaceTypes: qaSoulAppManifest.workspaceTypes.map(type => type.id),
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
if (url.pathname === '/surfaces/routes/qa-home' || url.pathname === '/surfaces/panels/qa-release-panel') {
|
|
49
|
+
return Response.json(qaDescriptorSurface(request, url.pathname))
|
|
50
|
+
}
|
|
51
|
+
if (url.pathname === '/frames/widgets/qa-release-widget') {
|
|
52
|
+
return new Response(qaWidgetFrameHtml(request), {
|
|
53
|
+
headers: { 'content-type': 'text/html; charset=utf-8' },
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
if (url.pathname === '/protocol/actions' && request.method === 'POST') {
|
|
57
|
+
const body = await request.json().catch(() => ({})) as Record<string, unknown>
|
|
58
|
+
return Response.json(await qaProtocolAction(request, String(body.protocolAction ?? '')))
|
|
59
|
+
}
|
|
60
|
+
if (url.pathname === '/protocol/search' && request.method === 'GET') {
|
|
61
|
+
return Response.json(await qaProtocolSearch(request, url))
|
|
62
|
+
}
|
|
63
|
+
if (url.pathname === '/broker/permissions') {
|
|
64
|
+
const hostUrl = request.headers.get('x-aiworker-host-url') ?? Bun.env.AIWORKER_HOST_URL
|
|
65
|
+
if (!hostUrl)
|
|
66
|
+
return Response.json({ appId: qaSoulAppManifest.id, broker: 'not-configured', permissions: [] })
|
|
67
|
+
const client = createSoulAppClient({ appId: qaSoulAppManifest.id, baseUrl: hostUrl })
|
|
68
|
+
return Response.json(await client.broker.permissions.list())
|
|
69
|
+
}
|
|
70
|
+
if (url.pathname === '/protocol/capabilities') {
|
|
71
|
+
return Response.json({
|
|
72
|
+
capabilities: await qaReferenceSoulApp.ui?.capabilities({
|
|
73
|
+
appId: qaSoulAppManifest.id,
|
|
74
|
+
permissions: qaSoulAppManifest.permissions,
|
|
75
|
+
}),
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
return Response.json({ error: { code: 'NOT_FOUND', message: `Unknown QA app route: ${url.pathname}` } }, { status: 404 })
|
|
79
|
+
},
|
|
80
|
+
hostname: Bun.env.HOST ?? '127.0.0.1',
|
|
81
|
+
port,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (import.meta.main) {
|
|
86
|
+
const server = serveHostMounted()
|
|
87
|
+
process.stdout.write(`${JSON.stringify({ appId: qaSoulAppManifest.id, mode: 'host-mounted', url: `http://${server.hostname}:${server.port}` })}\n`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function verifyMountToken(request: Request): Response | null {
|
|
91
|
+
const expected = Bun.env.AIWORKER_MOUNT_TOKEN
|
|
92
|
+
if (!expected)
|
|
93
|
+
return null
|
|
94
|
+
const actual = request.headers.get('x-aiworker-mount-token')
|
|
95
|
+
return actual === expected
|
|
96
|
+
? null
|
|
97
|
+
: Response.json({ error: { code: 'INVALID_MOUNT_TOKEN', message: 'Host mount token is required.' } }, { status: 401 })
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function qaDescriptorSurface(request: Request, pathname: string) {
|
|
101
|
+
const context = readMountContext(request)
|
|
102
|
+
return {
|
|
103
|
+
actions: [
|
|
104
|
+
{
|
|
105
|
+
id: 'create-release-review',
|
|
106
|
+
label: 'Create review',
|
|
107
|
+
method: 'POST',
|
|
108
|
+
target: `${context?.brokerUrl ?? '/broker'}/reviews`,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
appId: qaSoulAppManifest.id,
|
|
112
|
+
authority: 'soul-app',
|
|
113
|
+
cache: {
|
|
114
|
+
freshness: 'non-authoritative',
|
|
115
|
+
},
|
|
116
|
+
context: {
|
|
117
|
+
signed: Boolean(request.headers.get('x-aiworker-mount-signature')),
|
|
118
|
+
surfaceId: context?.surface?.id ?? null,
|
|
119
|
+
workspaceId: context?.workspaceId ?? null,
|
|
120
|
+
},
|
|
121
|
+
fields: [
|
|
122
|
+
{ label: 'Domain', value: qaSoulAppManifest.soul.domain },
|
|
123
|
+
{ label: 'Workspace types', value: qaSoulAppManifest.workspaceTypes.map(type => type.name).join(', ') },
|
|
124
|
+
{ label: 'Evidence broker', value: qaSoulAppManifest.connectors.required.map(connector => connector.id).join(', ') },
|
|
125
|
+
],
|
|
126
|
+
path: pathname,
|
|
127
|
+
renderer: 'host-descriptor',
|
|
128
|
+
status: 'ready',
|
|
129
|
+
title: pathname.includes('/routes/') ? 'QA Mounted Workbench' : 'Release Gate Panel',
|
|
130
|
+
type: 'aiworker.surface.descriptor.v1',
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function qaWidgetFrameHtml(request: Request): string {
|
|
135
|
+
const context = readMountContext(request)
|
|
136
|
+
return [
|
|
137
|
+
'<!doctype html>',
|
|
138
|
+
'<html lang="en">',
|
|
139
|
+
'<head><meta charset="utf-8"><title>QA Release Widget</title></head>',
|
|
140
|
+
'<body>',
|
|
141
|
+
'<main>',
|
|
142
|
+
'<h1>Release Widget</h1>',
|
|
143
|
+
`<p data-soul-app-id="${qaSoulAppManifest.id}">Mounted QA frame surface</p>`,
|
|
144
|
+
`<p data-surface-id="${context?.surface?.id ?? 'unknown'}">Scope ${context?.surface?.scope ?? 'workspace'}</p>`,
|
|
145
|
+
'</main>',
|
|
146
|
+
'</body>',
|
|
147
|
+
'</html>',
|
|
148
|
+
].join('')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function qaProtocolAction(request: Request, protocolAction: string) {
|
|
152
|
+
if (protocolAction === 'releaseGates.create') {
|
|
153
|
+
const persisted = await persistReleaseGateDraft(request)
|
|
154
|
+
if (!persisted.ok)
|
|
155
|
+
return persisted
|
|
156
|
+
return {
|
|
157
|
+
message: 'Release gate draft opened by QA app.',
|
|
158
|
+
ok: true,
|
|
159
|
+
redirectTo: '/qa/release',
|
|
160
|
+
refresh: true,
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (protocolAction === 'release.refresh') {
|
|
164
|
+
return {
|
|
165
|
+
message: 'Release data refreshed by QA app.',
|
|
166
|
+
ok: true,
|
|
167
|
+
refresh: true,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (protocolAction === 'settings.open') {
|
|
171
|
+
return {
|
|
172
|
+
message: 'QA settings are owned by the QA app.',
|
|
173
|
+
ok: true,
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
message: `Unknown QA protocol action: ${protocolAction}`,
|
|
178
|
+
ok: false,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function persistReleaseGateDraft(request: Request): Promise<{ message: string, ok: false } | { ok: true }> {
|
|
183
|
+
const context = readMountContext(request)
|
|
184
|
+
if (!context?.hostUrl)
|
|
185
|
+
return { ok: true }
|
|
186
|
+
|
|
187
|
+
const draftKey = `drafts/release-gate/${context.workspaceId ?? 'app'}`
|
|
188
|
+
const workspaceRef = context.workspaceId ?? 'app'
|
|
189
|
+
const client = createSoulAppClient({ appId: qaSoulAppManifest.id, baseUrl: context.hostUrl })
|
|
190
|
+
try {
|
|
191
|
+
await client.broker.storage.put(draftKey, {
|
|
192
|
+
appId: qaSoulAppManifest.id,
|
|
193
|
+
kind: 'release-gate',
|
|
194
|
+
source: 'qa-mounted-action',
|
|
195
|
+
status: 'draft',
|
|
196
|
+
workspaceId: context.workspaceId ?? null,
|
|
197
|
+
}, brokerScope(context))
|
|
198
|
+
await client.broker.search.upsert(draftKey, {
|
|
199
|
+
kind: 'release-gate',
|
|
200
|
+
reference: {
|
|
201
|
+
id: workspaceRef,
|
|
202
|
+
type: context.workspaceId ? 'workspace' : 'app',
|
|
203
|
+
},
|
|
204
|
+
sessionId: context.sessionId ?? null,
|
|
205
|
+
summary: `QA app-owned release gate draft for workspace ${workspaceRef}.`,
|
|
206
|
+
title: 'Release gate draft',
|
|
207
|
+
workspaceId: context.workspaceId ?? null,
|
|
208
|
+
}, brokerScope(context))
|
|
209
|
+
return { ok: true }
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
return {
|
|
213
|
+
message: error instanceof Error ? error.message : String(error),
|
|
214
|
+
ok: false,
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function qaProtocolSearch(request: Request, url: URL) {
|
|
220
|
+
const query = url.searchParams.get('query') ?? ''
|
|
221
|
+
const brokerItems = await queryBrokerReleaseSearch(request, query)
|
|
222
|
+
if (brokerItems?.length) {
|
|
223
|
+
return {
|
|
224
|
+
items: brokerItems,
|
|
225
|
+
providerId: 'releases.search',
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
items: [{
|
|
231
|
+
appId: qaSoulAppManifest.id,
|
|
232
|
+
authority: 'soul-app' as const,
|
|
233
|
+
id: 'release-gate-draft',
|
|
234
|
+
kind: 'release-gate',
|
|
235
|
+
openAction: {
|
|
236
|
+
id: 'create-release-gate',
|
|
237
|
+
input: { query },
|
|
238
|
+
},
|
|
239
|
+
status: 'draft',
|
|
240
|
+
summary: query ? `QA app-owned release match for ${query}` : 'Open QA release gate workspace',
|
|
241
|
+
title: query ? `Release gate: ${query}` : 'Release gate draft',
|
|
242
|
+
}],
|
|
243
|
+
providerId: 'releases.search',
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function queryBrokerReleaseSearch(request: Request, query: string): Promise<Array<Record<string, unknown>> | null> {
|
|
248
|
+
const context = readMountContext(request)
|
|
249
|
+
if (!context?.hostUrl)
|
|
250
|
+
return null
|
|
251
|
+
|
|
252
|
+
const client = createSoulAppClient({ appId: qaSoulAppManifest.id, baseUrl: context.hostUrl })
|
|
253
|
+
try {
|
|
254
|
+
const result = await client.broker.search.query(query, brokerScope(context)) as BrokerSearchResult
|
|
255
|
+
return Array.isArray(result.items) ? result.items : null
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return null
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function readMountContext(request: Request): MountContext | null {
|
|
263
|
+
const payload = request.headers.get('x-aiworker-mount-context')
|
|
264
|
+
if (!payload)
|
|
265
|
+
return null
|
|
266
|
+
try {
|
|
267
|
+
const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')) as unknown
|
|
268
|
+
if (!isRecord(parsed))
|
|
269
|
+
return null
|
|
270
|
+
const surface = isRecord(parsed.surface) ? parsed.surface : null
|
|
271
|
+
return {
|
|
272
|
+
brokerUrl: typeof parsed.brokerUrl === 'string' ? parsed.brokerUrl : undefined,
|
|
273
|
+
hostUrl: request.headers.get('x-aiworker-host-url') ?? undefined,
|
|
274
|
+
operatorId: typeof parsed.operatorId === 'string' ? parsed.operatorId : null,
|
|
275
|
+
sessionId: typeof parsed.sessionId === 'string' ? parsed.sessionId : null,
|
|
276
|
+
surface: surface
|
|
277
|
+
? {
|
|
278
|
+
id: typeof surface.id === 'string' ? surface.id : undefined,
|
|
279
|
+
scope: typeof surface.scope === 'string' ? surface.scope : undefined,
|
|
280
|
+
}
|
|
281
|
+
: undefined,
|
|
282
|
+
workerId: typeof parsed.workerId === 'string' ? parsed.workerId : null,
|
|
283
|
+
workspaceId: typeof parsed.workspaceId === 'string' ? parsed.workspaceId : null,
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return null
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function brokerScope(context: MountContext) {
|
|
292
|
+
return {
|
|
293
|
+
operatorId: context.operatorId ?? undefined,
|
|
294
|
+
sessionId: context.sessionId ?? undefined,
|
|
295
|
+
workerId: context.workerId ?? undefined,
|
|
296
|
+
workspaceId: context.workspaceId ?? undefined,
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
301
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
302
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SoulAppArtifactValidationResult,
|
|
3
|
+
SoulAppCapability,
|
|
4
|
+
SoulAppDefinition,
|
|
5
|
+
SoulAppProtocolResult,
|
|
6
|
+
SoulAppScopedContext,
|
|
7
|
+
SoulAppSessionContext,
|
|
8
|
+
} from '@zonease/aiworker-soul-app-sdk'
|
|
9
|
+
|
|
10
|
+
import { createSoulAppManifest, defineSoulApp, parseNamespacedSoulAppCapabilityId } from '@zonease/aiworker-soul-app-sdk'
|
|
11
|
+
|
|
12
|
+
import manifestJson from '../soul-app.manifest.json' with { type: 'json' }
|
|
13
|
+
|
|
14
|
+
export const qaSoulAppManifest = createSoulAppManifest(manifestJson)
|
|
15
|
+
|
|
16
|
+
export const QA_REFERENCE_APP_BOUNDARY = {
|
|
17
|
+
hostMountedEntry: './src/host-mounted.ts',
|
|
18
|
+
packageName: '@zonease/aiworker-qa',
|
|
19
|
+
primaryWorkbench: 'Release Gate Workbench',
|
|
20
|
+
standaloneEntry: './src/standalone.ts',
|
|
21
|
+
} as const
|
|
22
|
+
|
|
23
|
+
export const qaReferenceSoulApp: SoulAppDefinition = defineSoulApp({
|
|
24
|
+
artifact: {
|
|
25
|
+
async artifactSchemas() {
|
|
26
|
+
return qaSoulAppManifest.artifactTypes
|
|
27
|
+
},
|
|
28
|
+
async extractMetadata(_context, artifact) {
|
|
29
|
+
return {
|
|
30
|
+
appId: qaSoulAppManifest.id,
|
|
31
|
+
contentRef: artifact.contentRef,
|
|
32
|
+
kind: artifact.type,
|
|
33
|
+
releaseRisk: artifact.type === 'release-gate' ? 'gate-review' : 'coverage-review',
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
async validateArtifact(_context, artifact) {
|
|
37
|
+
return validateArtifactType(artifact.type)
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
connector: {
|
|
41
|
+
async declareConnectorNeeds() {
|
|
42
|
+
return [
|
|
43
|
+
...qaSoulAppManifest.connectors.required,
|
|
44
|
+
...qaSoulAppManifest.connectors.optional,
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
lifecycle: lifecycleHandlers('QA reference app ready.'),
|
|
49
|
+
manifest: qaSoulAppManifest,
|
|
50
|
+
review: {
|
|
51
|
+
async createReviewRubric(_context, artifactType) {
|
|
52
|
+
return {
|
|
53
|
+
checks: [
|
|
54
|
+
`Artifact type ${artifactType} maps test evidence to release risk.`,
|
|
55
|
+
'Known defects, missing evidence, and residual risk are separated.',
|
|
56
|
+
'Go/no-go recommendation is explicit and reviewable.',
|
|
57
|
+
],
|
|
58
|
+
policyRef: qaSoulAppManifest.artifactTypes.find(type => type.id === artifactType)?.reviewPolicyRef,
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
async proposeMemoryCandidate(context, review) {
|
|
62
|
+
return {
|
|
63
|
+
evidence: [{
|
|
64
|
+
appId: context.appId,
|
|
65
|
+
artifactId: review.artifactId,
|
|
66
|
+
reviewId: review.reviewId,
|
|
67
|
+
source: 'qa-reference-app',
|
|
68
|
+
}],
|
|
69
|
+
statement: 'Promote reviewed QA release gate guidance into the QA Soul namespace.',
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
runtime: {
|
|
74
|
+
async prepareSessionContext(context, input) {
|
|
75
|
+
const capability = resolveQaCapability(input.capabilityId)
|
|
76
|
+
return sessionContext(context, capability, input.workspaceType)
|
|
77
|
+
},
|
|
78
|
+
async resolveCapability(_context, input) {
|
|
79
|
+
return resolveQaCapability(input.capabilityId ?? input.intent)
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
ui: {
|
|
83
|
+
async artifactTypes() {
|
|
84
|
+
return qaSoulAppManifest.artifactTypes
|
|
85
|
+
},
|
|
86
|
+
async capabilities() {
|
|
87
|
+
return qaSoulAppManifest.capabilities
|
|
88
|
+
},
|
|
89
|
+
async ui() {
|
|
90
|
+
return qaSoulAppManifest.ui
|
|
91
|
+
},
|
|
92
|
+
async workspaceTypes() {
|
|
93
|
+
return qaSoulAppManifest.workspaceTypes
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
function resolveQaCapability(input?: string): SoulAppCapability {
|
|
99
|
+
const id = normalizeCapabilityId(input) ?? qaSoulAppManifest.capabilities[0]!.id
|
|
100
|
+
const capability = qaSoulAppManifest.capabilities.find(item => item.id === id)
|
|
101
|
+
if (!capability)
|
|
102
|
+
throw new Error(`QA capability not found: ${input}`)
|
|
103
|
+
return capability
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalizeCapabilityId(input?: string): string | null {
|
|
107
|
+
if (!input)
|
|
108
|
+
return null
|
|
109
|
+
return parseNamespacedSoulAppCapabilityId(input)?.capabilityId ?? input
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function sessionContext(context: SoulAppScopedContext, capability: SoulAppCapability, workspaceType: string): SoulAppSessionContext {
|
|
113
|
+
return {
|
|
114
|
+
artifactTypes: capability.artifactTypes,
|
|
115
|
+
capabilityId: capability.id,
|
|
116
|
+
contextMarkdown: [
|
|
117
|
+
'# QA Soul App Context',
|
|
118
|
+
`App: ${context.appId}`,
|
|
119
|
+
`Workspace type: ${workspaceType}`,
|
|
120
|
+
'Use release, test suite, defect evidence, regression matrix, and release gate language.',
|
|
121
|
+
].join('\n'),
|
|
122
|
+
promptFragments: [
|
|
123
|
+
`Use QA capability ${capability.name}.`,
|
|
124
|
+
'Map test evidence and known defects to user-facing release risk.',
|
|
125
|
+
'Return a reviewable QA business artifact, not a generic chat answer.',
|
|
126
|
+
],
|
|
127
|
+
reviewRubric: [
|
|
128
|
+
'Coverage maps to changed scope and user risk.',
|
|
129
|
+
'Known blockers and residual risk are separate.',
|
|
130
|
+
'Recommendation is explicit and actionable.',
|
|
131
|
+
],
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function validateArtifactType(type: string): SoulAppArtifactValidationResult {
|
|
136
|
+
const known = qaSoulAppManifest.artifactTypes.some(item => item.id === type)
|
|
137
|
+
return {
|
|
138
|
+
issues: known ? [] : [{ message: `Unknown QA artifact type: ${type}`, severity: 'error' }],
|
|
139
|
+
ok: known,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function lifecycleHandlers(message: string) {
|
|
144
|
+
const ok = async (): Promise<SoulAppProtocolResult> => ({ message, ok: true })
|
|
145
|
+
return {
|
|
146
|
+
disable: ok,
|
|
147
|
+
enable: ok,
|
|
148
|
+
healthcheck: ok,
|
|
149
|
+
install: ok,
|
|
150
|
+
upgrade: ok,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
import { qaSoulAppManifest } from './index'
|
|
4
|
+
|
|
5
|
+
export function renderStandaloneHtml(): string {
|
|
6
|
+
return [
|
|
7
|
+
'<!doctype html>',
|
|
8
|
+
'<html lang="en">',
|
|
9
|
+
'<head><meta charset="utf-8"><title>AIWorker QA</title></head>',
|
|
10
|
+
`<body data-soul-app-id="${qaSoulAppManifest.id}">`,
|
|
11
|
+
'<main>',
|
|
12
|
+
`<h1>${qaSoulAppManifest.name}</h1>`,
|
|
13
|
+
`<p>${qaSoulAppManifest.description}</p>`,
|
|
14
|
+
'</main>',
|
|
15
|
+
'</body>',
|
|
16
|
+
'</html>',
|
|
17
|
+
].join('')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function serveStandalone(port = Number(Bun.env.PORT ?? 0)) {
|
|
21
|
+
return Bun.serve({
|
|
22
|
+
fetch(request) {
|
|
23
|
+
const url = new URL(request.url)
|
|
24
|
+
if (url.pathname === '/health') {
|
|
25
|
+
return Response.json({
|
|
26
|
+
appId: qaSoulAppManifest.id,
|
|
27
|
+
mode: 'standalone',
|
|
28
|
+
status: 'ok',
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
return new Response(renderStandaloneHtml(), {
|
|
32
|
+
headers: { 'content-type': 'text/html; charset=utf-8' },
|
|
33
|
+
})
|
|
34
|
+
},
|
|
35
|
+
hostname: Bun.env.HOST ?? '127.0.0.1',
|
|
36
|
+
port,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (import.meta.main) {
|
|
41
|
+
const server = serveStandalone()
|
|
42
|
+
process.stdout.write(`${JSON.stringify({ appId: qaSoulAppManifest.id, mode: 'standalone', url: `http://${server.hostname}:${server.port}` })}\n`)
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const routeId = 'qa-home'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const panelId = 'qa-release-panel'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const panelId = 'qa-review-panel'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const widgetId = 'qa-release-widget'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const panelId = 'qa-review-panel'
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zonease/aiworker-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "AIWorker CLI —
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "AIWorker CLI — local Host and vertical Soul workspace runtime",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -13,14 +13,15 @@
|
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
15
|
"bin": {
|
|
16
|
-
"aiworker": "
|
|
16
|
+
"aiworker": "aiworker.js"
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"aiworker.js",
|
|
20
20
|
"aiworker-bun.js",
|
|
21
21
|
"README.md",
|
|
22
22
|
"drizzle/",
|
|
23
|
-
"web/"
|
|
23
|
+
"web/",
|
|
24
|
+
"official-apps/"
|
|
24
25
|
],
|
|
25
26
|
"engines": {
|
|
26
27
|
"bun": ">=1.1"
|