@open-mercato/cli 0.6.6-develop.6383.1.27fcc83455 → 0.6.6-develop.6385.1.9a81faa5f0
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/.turbo/turbo-build.log +11 -0
- package/build.mjs +70 -2
- package/dist/agentic/guides/core.auth.md +3 -0
- package/dist/agentic/guides/core.catalog.md +3 -0
- package/dist/agentic/guides/core.currencies.md +3 -0
- package/dist/agentic/guides/core.customer_accounts.md +3 -0
- package/dist/agentic/guides/core.customers.md +3 -0
- package/dist/agentic/guides/core.data_sync.md +3 -0
- package/dist/agentic/guides/core.integrations.md +3 -0
- package/dist/agentic/guides/core.sales.md +3 -0
- package/dist/agentic/guides/core.workflows.md +3 -0
- package/dist/agentic/guides/module-facts.json +2073 -0
- package/dist/agentic/guides/modules/auth.md +64 -0
- package/dist/agentic/guides/modules/catalog.md +70 -0
- package/dist/agentic/guides/modules/currencies.md +50 -0
- package/dist/agentic/guides/modules/customer_accounts.md +76 -0
- package/dist/agentic/guides/modules/customers.md +115 -0
- package/dist/agentic/guides/modules/data_sync.md +49 -0
- package/dist/agentic/guides/modules/integrations.md +49 -0
- package/dist/agentic/guides/modules/sales.md +109 -0
- package/dist/agentic/guides/modules/workflows.md +73 -0
- package/dist/lib/__integration__/TC-INT-008.spec.js +29 -1
- package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
- package/dist/lib/agentic-setup.js +87 -0
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/worker-job-handler.js +13 -1
- package/dist/lib/worker-job-handler.js.map +2 -2
- package/dist/mercato.js +4 -1
- package/dist/mercato.js.map +2 -2
- package/package.json +5 -5
- package/src/lib/__integration__/TC-INT-008.spec.ts +41 -1
- package/src/lib/__tests__/worker-job-handler.test.ts +28 -0
- package/src/lib/agentic-setup.ts +129 -0
- package/src/lib/worker-job-handler.ts +13 -1
- package/src/mercato.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/cli",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.6385.1.9a81faa5f0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"@mikro-orm/decorators": "^7.1.4",
|
|
61
61
|
"@mikro-orm/migrations": "^7.1.4",
|
|
62
62
|
"@mikro-orm/postgresql": "^7.1.4",
|
|
63
|
-
"@open-mercato/queue": "0.6.6-develop.
|
|
64
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
63
|
+
"@open-mercato/queue": "0.6.6-develop.6385.1.9a81faa5f0",
|
|
64
|
+
"@open-mercato/shared": "0.6.6-develop.6385.1.9a81faa5f0",
|
|
65
65
|
"cross-spawn": "^7.0.6",
|
|
66
66
|
"pg": "8.22.0",
|
|
67
67
|
"semver": "^7.8.5",
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
"typescript": "^6.0.3"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
74
|
+
"@open-mercato/shared": "0.6.6-develop.6385.1.9a81faa5f0"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
77
|
+
"@open-mercato/shared": "0.6.6-develop.6385.1.9a81faa5f0",
|
|
78
78
|
"@types/jest": "^30.0.0",
|
|
79
79
|
"jest": "^30.4.2",
|
|
80
80
|
"ts-jest": "^29.4.11"
|
|
@@ -4,6 +4,7 @@ import fs from 'node:fs'
|
|
|
4
4
|
import os from 'node:os'
|
|
5
5
|
import path from 'node:path'
|
|
6
6
|
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { MODULE_FACTS_ALLOWLIST } from '../generators/module-facts'
|
|
7
8
|
|
|
8
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
10
|
const repoRoot = path.resolve(__dirname, '..', '..', '..', '..', '..')
|
|
@@ -15,6 +16,11 @@ const standaloneTemplatePackageJsonPath = path.join(repoRoot, 'packages', 'creat
|
|
|
15
16
|
const agenticRoot = path.join(repoRoot, 'packages', 'create-app', 'agentic')
|
|
16
17
|
const packagesRoot = path.join(repoRoot, 'packages')
|
|
17
18
|
|
|
19
|
+
// Modules the standalone fixture enables in src/modules.ts. Both are on the
|
|
20
|
+
// fact-sheet allowlist, so agentic:init must ship exactly their fact-sheets
|
|
21
|
+
// (enabled ∩ allowlist — spec D6) and list them in the AGENTS.md marker block.
|
|
22
|
+
const FIXTURE_ENABLED_MODULES = ['customers', 'sales']
|
|
23
|
+
|
|
18
24
|
function normalizePath(value: string): string {
|
|
19
25
|
return value.split(path.sep).join('/')
|
|
20
26
|
}
|
|
@@ -57,7 +63,8 @@ function createStandaloneFixture(rootDir: string): string {
|
|
|
57
63
|
2,
|
|
58
64
|
),
|
|
59
65
|
)
|
|
60
|
-
|
|
66
|
+
const moduleEntries = FIXTURE_ENABLED_MODULES.map((moduleId) => ` { id: '${moduleId}' },`).join('\n')
|
|
67
|
+
writeFile(path.join(appDir, 'src', 'modules.ts'), `export const enabledModules = [\n${moduleEntries}\n]\n`)
|
|
61
68
|
return appDir
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -167,6 +174,17 @@ function readPlaywrightConfigPathFromCliRunner(): string {
|
|
|
167
174
|
function expectedGuideOutputNames(): string[] {
|
|
168
175
|
const collected = new Set<string>()
|
|
169
176
|
|
|
177
|
+
// Static conceptual guides checked into create-app (e.g. module-system.md) are
|
|
178
|
+
// bundled into dist/agentic/guides by the CLI build and copied wholesale.
|
|
179
|
+
const staticGuidesRoot = path.join(agenticRoot, 'guides')
|
|
180
|
+
if (fs.existsSync(staticGuidesRoot)) {
|
|
181
|
+
for (const entry of fs.readdirSync(staticGuidesRoot, { withFileTypes: true })) {
|
|
182
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
183
|
+
collected.add(entry.name)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
170
188
|
for (const packageName of fs.readdirSync(packagesRoot)) {
|
|
171
189
|
const packageGuide = path.join(packagesRoot, packageName, 'agentic', 'standalone-guide.md')
|
|
172
190
|
if (fs.existsSync(packageGuide)) {
|
|
@@ -186,6 +204,21 @@ function expectedGuideOutputNames(): string[] {
|
|
|
186
204
|
}
|
|
187
205
|
}
|
|
188
206
|
|
|
207
|
+
// Generated fact-sheet artifacts (spec 2026-06-27-ts-morph-module-fact-sheets):
|
|
208
|
+
// the module-facts.json sidecar is copied as-is, fact-sheets are filtered to the
|
|
209
|
+
// fixture's enabled modules, and every allowlisted module whose hand-written
|
|
210
|
+
// core.<module>.md guide no longer exists gets a legacy redirect stub.
|
|
211
|
+
collected.add('module-facts.json')
|
|
212
|
+
for (const moduleId of FIXTURE_ENABLED_MODULES) {
|
|
213
|
+
collected.add(normalizePath(path.join('modules', `${moduleId}.md`)))
|
|
214
|
+
}
|
|
215
|
+
for (const moduleId of MODULE_FACTS_ALLOWLIST) {
|
|
216
|
+
const legacyGuideSource = path.join(packagesRoot, 'core', 'src', 'modules', moduleId, 'agentic', 'standalone-guide.md')
|
|
217
|
+
if (!fs.existsSync(legacyGuideSource)) {
|
|
218
|
+
collected.add(`core.${moduleId}.md`)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
189
222
|
return Array.from(collected).sort()
|
|
190
223
|
}
|
|
191
224
|
|
|
@@ -230,6 +263,13 @@ test.describe('TC-INT-008: CLI agentic init mirrors standalone scaffolding asset
|
|
|
230
263
|
expect(agentsSource).toContain('<!-- CODEX_ENFORCEMENT_RULES_START -->')
|
|
231
264
|
expect(agentsSource).toContain('.ai/guides/core.md')
|
|
232
265
|
|
|
266
|
+
// The Module-Specific Guides marker block lists exactly the enabled modules'
|
|
267
|
+
// fact-sheets (enabled ∩ allowlist), not the full bundled set.
|
|
268
|
+
for (const moduleId of FIXTURE_ENABLED_MODULES) {
|
|
269
|
+
expect(agentsSource).toContain(`.ai/guides/modules/${moduleId}.md`)
|
|
270
|
+
}
|
|
271
|
+
expect(agentsSource).not.toContain('.ai/guides/modules/auth.md')
|
|
272
|
+
|
|
233
273
|
const specsReadmeSource = fs.readFileSync(path.join(appDir, '.ai', 'specs', 'README.md'), 'utf8')
|
|
234
274
|
expect(specsReadmeSource).toContain('sample-store')
|
|
235
275
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ModuleWorker } from '@open-mercato/shared/modules/registry'
|
|
2
|
+
import {
|
|
3
|
+
getModuleResourceUsageReport,
|
|
4
|
+
resetModuleResourceUsage,
|
|
5
|
+
} from '@open-mercato/shared/lib/modules/resource-usage'
|
|
2
6
|
import { createPerJobWorkerHandler, type WorkerJobContainer } from '../worker-job-handler'
|
|
3
7
|
|
|
4
8
|
type FakeContainer = WorkerJobContainer & {
|
|
@@ -32,6 +36,14 @@ function makeWorker(id: string, handler: ModuleWorker['handler']): ModuleWorker
|
|
|
32
36
|
const baseCtx = { jobId: 'job-1', attemptNumber: 1, queueName: 'test' }
|
|
33
37
|
|
|
34
38
|
describe('createPerJobWorkerHandler', () => {
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
resetModuleResourceUsage()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
resetModuleResourceUsage()
|
|
45
|
+
})
|
|
46
|
+
|
|
35
47
|
it('creates a fresh container for every job invocation', async () => {
|
|
36
48
|
const { factory } = makeContainerFactory()
|
|
37
49
|
const worker = makeWorker('w', jest.fn())
|
|
@@ -132,4 +144,20 @@ describe('createPerJobWorkerHandler', () => {
|
|
|
132
144
|
await expect(handler({ id: 'a' }, baseCtx)).rejects.toBe(boom)
|
|
133
145
|
expect(containers[0].em.clear).toHaveBeenCalledTimes(1)
|
|
134
146
|
})
|
|
147
|
+
|
|
148
|
+
it('attributes worker execution to the owning module', async () => {
|
|
149
|
+
const { factory } = makeContainerFactory()
|
|
150
|
+
const handler = createPerJobWorkerHandler(
|
|
151
|
+
[makeWorker('customers:workers:bulk-update', jest.fn())],
|
|
152
|
+
factory,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
await handler({ id: 'a' }, baseCtx)
|
|
156
|
+
|
|
157
|
+
const report = getModuleResourceUsageReport()
|
|
158
|
+
expect(report.modules).toHaveLength(1)
|
|
159
|
+
expect(report.modules[0].moduleId).toBe('customers')
|
|
160
|
+
expect(report.modules[0].surfaces[0].surface).toBe('worker')
|
|
161
|
+
expect(report.modules[0].topOperations[0].operation).toBe('customers:workers:bulk-update')
|
|
162
|
+
})
|
|
135
163
|
})
|
package/src/lib/agentic-setup.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, symlinkSync, lstatSync, unlinkSync, readdirSync } from 'node:fs'
|
|
10
10
|
import { join, dirname, basename } from 'node:path'
|
|
11
11
|
import { fileURLToPath } from 'node:url'
|
|
12
|
+
import { Project, SyntaxKind } from 'ts-morph'
|
|
12
13
|
|
|
13
14
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
14
15
|
// In the built output (dist/lib/agentic-setup.js), __dirname is dist/lib/.
|
|
@@ -52,14 +53,125 @@ function copyFile(srcDir: string, srcRelative: string, destPath: string): void {
|
|
|
52
53
|
copyFileSync(srcPath, destPath)
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
// ─── Module fact-sheet selection (mirrors packages/create-app/src/setup/tools/shared.ts) ──
|
|
57
|
+
|
|
58
|
+
// AST-parse the static `enabledModules` array literal in the app's src/modules.ts
|
|
59
|
+
// and collect each entry's `id`. Only the static literal is read (conditional
|
|
60
|
+
// .push()/spread entries are intentionally not seen — see spec D6).
|
|
61
|
+
function readEnabledModuleIds(modulesPath: string): string[] {
|
|
62
|
+
if (!existsSync(modulesPath)) return []
|
|
63
|
+
try {
|
|
64
|
+
const project = new Project({ useInMemoryFileSystem: true })
|
|
65
|
+
const sourceFile = project.createSourceFile('modules.ts', readFileSync(modulesPath, 'utf-8'))
|
|
66
|
+
const declaration = sourceFile.getVariableDeclaration('enabledModules')
|
|
67
|
+
const arrayLiteral = declaration?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression)
|
|
68
|
+
if (!arrayLiteral) return []
|
|
69
|
+
const ids: string[] = []
|
|
70
|
+
for (const element of arrayLiteral.getElements()) {
|
|
71
|
+
const objectLiteral = element.asKind(SyntaxKind.ObjectLiteralExpression)
|
|
72
|
+
if (!objectLiteral) continue
|
|
73
|
+
const idProperty = objectLiteral.getProperty('id')?.asKind(SyntaxKind.PropertyAssignment)
|
|
74
|
+
const idValue = idProperty?.getInitializerIfKind(SyntaxKind.StringLiteral)?.getLiteralValue()
|
|
75
|
+
if (idValue) ids.push(idValue)
|
|
76
|
+
}
|
|
77
|
+
return ids
|
|
78
|
+
} catch {
|
|
79
|
+
return []
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Resolve which per-module fact-sheets to ship: the intersection of the bundled
|
|
84
|
+
// fact-sheets (the D5 allowlist, materialized by build.mjs) with the app's enabled
|
|
85
|
+
// modules. Falls back to the full bundled set when the enabled set cannot be read
|
|
86
|
+
// (R5 — degraded, never empty).
|
|
87
|
+
function selectModuleFactSheets(targetDir: string, modulesSubdir: string): string[] {
|
|
88
|
+
const available = existsSync(modulesSubdir)
|
|
89
|
+
? readdirSync(modulesSubdir)
|
|
90
|
+
.filter((file) => file.endsWith('.md'))
|
|
91
|
+
.map((file) => file.replace(/\.md$/, ''))
|
|
92
|
+
: []
|
|
93
|
+
if (available.length === 0) return []
|
|
94
|
+
const enabled = new Set(readEnabledModuleIds(join(targetDir, 'src', 'modules.ts')))
|
|
95
|
+
const selected = available.filter((moduleId) => enabled.has(moduleId))
|
|
96
|
+
return selected.length > 0 ? selected : available
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const MODULE_GUIDES_START = '<!-- om:module-guides:start -->'
|
|
100
|
+
const MODULE_GUIDES_END = '<!-- om:module-guides:end -->'
|
|
101
|
+
|
|
102
|
+
// Read each module's guide label from the bundled `module-facts.json` (emitted by
|
|
103
|
+
// build.mjs from the generator's extraction of each module's own `metadata`). The
|
|
104
|
+
// label falls back description → title → generic, so the CLI never re-declares
|
|
105
|
+
// specific module names or descriptions. A missing/malformed sidecar degrades to an
|
|
106
|
+
// empty map (generic labels), never a throw.
|
|
107
|
+
function readModuleGuideLabels(guidesDir: string): Record<string, string> {
|
|
108
|
+
const factsPath = join(guidesDir, 'module-facts.json')
|
|
109
|
+
if (!existsSync(factsPath)) return {}
|
|
110
|
+
try {
|
|
111
|
+
const parsed = JSON.parse(readFileSync(factsPath, 'utf-8')) as Record<
|
|
112
|
+
string,
|
|
113
|
+
{ description?: unknown; title?: unknown }
|
|
114
|
+
>
|
|
115
|
+
const labels: Record<string, string> = {}
|
|
116
|
+
for (const [moduleId, entry] of Object.entries(parsed)) {
|
|
117
|
+
const label =
|
|
118
|
+
(entry && typeof entry.description === 'string' && entry.description) ||
|
|
119
|
+
(entry && typeof entry.title === 'string' && entry.title) ||
|
|
120
|
+
''
|
|
121
|
+
if (label) labels[moduleId] = label
|
|
122
|
+
}
|
|
123
|
+
return labels
|
|
124
|
+
} catch {
|
|
125
|
+
return {}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function renderModuleGuidesBlock(selected: string[], labels: Record<string, string>): string {
|
|
130
|
+
if (selected.length === 0) return '_No module fact-sheets are bundled for this app._'
|
|
131
|
+
const rows = selected.map((moduleId) => {
|
|
132
|
+
const label = labels[moduleId] ?? `Use the ${moduleId} module`
|
|
133
|
+
return `| ${label} | \`.ai/guides/modules/${moduleId}.md\` |`
|
|
134
|
+
})
|
|
135
|
+
return ['| Task | Load |', '|---|---|', ...rows].join('\n')
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Regenerate the marker-delimited Module-Specific Guides block in the written
|
|
139
|
+
// AGENTS.md from the selected module set. Replaces strictly between the markers so
|
|
140
|
+
// surrounding prose is untouched and repeat runs are idempotent.
|
|
141
|
+
function injectModuleGuides(
|
|
142
|
+
agentsMdPath: string,
|
|
143
|
+
selected: string[],
|
|
144
|
+
labels: Record<string, string> = {},
|
|
145
|
+
): void {
|
|
146
|
+
if (!existsSync(agentsMdPath)) return
|
|
147
|
+
const content = readFileSync(agentsMdPath, 'utf-8')
|
|
148
|
+
const startIndex = content.indexOf(MODULE_GUIDES_START)
|
|
149
|
+
const endIndex = content.indexOf(MODULE_GUIDES_END)
|
|
150
|
+
if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
|
|
151
|
+
console.warn(
|
|
152
|
+
`[agentic] Module-Specific Guides markers (${MODULE_GUIDES_START} … ${MODULE_GUIDES_END}) not found in ${agentsMdPath}; the per-module guide list was not generated.`,
|
|
153
|
+
)
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
const before = content.slice(0, startIndex + MODULE_GUIDES_START.length)
|
|
157
|
+
const after = content.slice(endIndex)
|
|
158
|
+
const next = `${before}\n${renderModuleGuidesBlock(selected, labels)}\n${after}`
|
|
159
|
+
if (next !== content) writeFileSync(agentsMdPath, next)
|
|
160
|
+
}
|
|
161
|
+
|
|
55
162
|
// ─── Generators ──────────────────────────────────────────────────────────
|
|
56
163
|
|
|
57
164
|
function generateShared(config: AgenticConfig): void {
|
|
58
165
|
const { targetDir } = config
|
|
59
166
|
const srcDir = join(AGENTIC_DIR, 'shared')
|
|
60
167
|
|
|
168
|
+
// Resolve which per-module fact-sheets this app gets (enabled ∩ bundled allowlist).
|
|
169
|
+
const selectedModules = selectModuleFactSheets(targetDir, join(GUIDES_DIR, 'modules'))
|
|
170
|
+
const moduleGuideLabels = readModuleGuideLabels(GUIDES_DIR)
|
|
171
|
+
|
|
61
172
|
// AGENTS.md
|
|
62
173
|
writeTemplate(srcDir, 'AGENTS.md.template', join(targetDir, 'AGENTS.md'), config)
|
|
174
|
+
injectModuleGuides(join(targetDir, 'AGENTS.md'), selectedModules, moduleGuideLabels)
|
|
63
175
|
|
|
64
176
|
// .ai/ structure
|
|
65
177
|
writeTemplate(srcDir, 'ai/specs/README.md', join(targetDir, '.ai', 'specs', 'README.md'), config)
|
|
@@ -244,6 +356,9 @@ function generateShared(config: AgenticConfig): void {
|
|
|
244
356
|
|
|
245
357
|
copyFile(srcDir, 'ai/qa/tests/playwright.config.ts', join(targetDir, '.ai', 'qa', 'tests', 'playwright.config.ts'))
|
|
246
358
|
|
|
359
|
+
// Package & conceptual guides are copied wholesale (framework-wide). Per-module
|
|
360
|
+
// fact-sheets (.ai/guides/modules/<module>.md) are filtered to the app's enabled
|
|
361
|
+
// module set; the combined module-facts.json sidecar is copied as-is.
|
|
247
362
|
if (existsSync(GUIDES_DIR)) {
|
|
248
363
|
const guidesDestDir = join(targetDir, '.ai', 'guides')
|
|
249
364
|
for (const file of readdirSync(GUIDES_DIR)) {
|
|
@@ -253,6 +368,20 @@ function generateShared(config: AgenticConfig): void {
|
|
|
253
368
|
ensureDir(destPath)
|
|
254
369
|
copyFileSync(srcPath, destPath)
|
|
255
370
|
}
|
|
371
|
+
|
|
372
|
+
const moduleFactsPath = join(GUIDES_DIR, 'module-facts.json')
|
|
373
|
+
if (existsSync(moduleFactsPath)) {
|
|
374
|
+
const destPath = join(guidesDestDir, 'module-facts.json')
|
|
375
|
+
ensureDir(destPath)
|
|
376
|
+
copyFileSync(moduleFactsPath, destPath)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const modulesSubdir = join(GUIDES_DIR, 'modules')
|
|
380
|
+
for (const moduleId of selectedModules) {
|
|
381
|
+
const destPath = join(guidesDestDir, 'modules', `${moduleId}.md`)
|
|
382
|
+
ensureDir(destPath)
|
|
383
|
+
copyFileSync(join(modulesSubdir, `${moduleId}.md`), destPath)
|
|
384
|
+
}
|
|
256
385
|
}
|
|
257
386
|
}
|
|
258
387
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { JobContext, JobHandler } from '@open-mercato/queue'
|
|
2
2
|
import type { ModuleWorker } from '@open-mercato/shared/modules/registry'
|
|
3
|
+
import {
|
|
4
|
+
inferModuleIdFromResourceId,
|
|
5
|
+
withModuleResourceUsage,
|
|
6
|
+
} from '@open-mercato/shared/lib/modules/resource-usage'
|
|
3
7
|
|
|
4
8
|
export type WorkerJobContainer = {
|
|
5
9
|
resolve: <T = unknown>(name: string) => T
|
|
@@ -30,7 +34,15 @@ export function createPerJobWorkerHandler(
|
|
|
30
34
|
const container = await createContainer()
|
|
31
35
|
try {
|
|
32
36
|
for (const worker of workers) {
|
|
33
|
-
await
|
|
37
|
+
await withModuleResourceUsage(
|
|
38
|
+
{
|
|
39
|
+
moduleId: worker.moduleId ?? inferModuleIdFromResourceId(worker.id),
|
|
40
|
+
surface: 'worker',
|
|
41
|
+
operation: worker.id,
|
|
42
|
+
resourceId: worker.id,
|
|
43
|
+
},
|
|
44
|
+
() => worker.handler(job, { ...ctx, resolve: container.resolve.bind(container) }),
|
|
45
|
+
)
|
|
34
46
|
}
|
|
35
47
|
} finally {
|
|
36
48
|
try {
|
package/src/mercato.ts
CHANGED
|
@@ -66,7 +66,10 @@ function getRegisteredCliWorkers(modules: Module[] = getCliModules()): ModuleWor
|
|
|
66
66
|
const allWorkers: ModuleWorker[] = []
|
|
67
67
|
for (const mod of modules) {
|
|
68
68
|
if (mod.workers) {
|
|
69
|
-
allWorkers.push(...mod.workers)
|
|
69
|
+
allWorkers.push(...mod.workers.map((worker) => ({
|
|
70
|
+
...worker,
|
|
71
|
+
moduleId: worker.moduleId ?? mod.id,
|
|
72
|
+
})))
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
return allWorkers
|