@softize/opus 8.6.6
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 +1616 -0
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/bin/cli.mjs +528 -0
- package/bin/lib/check.mjs +307 -0
- package/bin/lib/components.mjs +151 -0
- package/bin/lib/create.mjs +208 -0
- package/bin/lib/db-check-runner.mjs +86 -0
- package/bin/lib/db-migrate-runner.mjs +89 -0
- package/bin/lib/db-scaffold-runner.mjs +84 -0
- package/bin/lib/db.mjs +261 -0
- package/bin/lib/docs-include.mjs +48 -0
- package/bin/lib/gen-dicts.mjs +134 -0
- package/bin/lib/gen-docs.mjs +288 -0
- package/bin/lib/gen-manifest.mjs +102 -0
- package/bin/lib/gen-openapi.mjs +195 -0
- package/bin/lib/gen-runner.mjs +472 -0
- package/bin/lib/gen-stubs.mjs +463 -0
- package/bin/lib/gen.mjs +311 -0
- package/bin/lib/init.mjs +514 -0
- package/bin/lib/introspect.mjs +107 -0
- package/bin/lib/mcp.mjs +85 -0
- package/bin/lib/postinstall.mjs +56 -0
- package/docs/chat-event-protocol.md +85 -0
- package/docs/code-style.md +16 -0
- package/docs/data-layer.md +246 -0
- package/docs/ownership-vs-shadcn-lock.md +102 -0
- package/docs/protocol.md +2053 -0
- package/docs/releasing.md +110 -0
- package/docs/shellnav.md +131 -0
- package/package.json +338 -0
- package/registry/hooks/hooks.json +26 -0
- package/registry/hooks/link-memory-on-start.mjs +46 -0
- package/registry/hooks/opus-check-on-stop.mjs +114 -0
- package/registry/skills/create-action/SKILL.md +49 -0
- package/registry/skills/create-action/scaffold.mjs +122 -0
- package/registry/templates/app/_gitignore +3 -0
- package/registry/templates/app/_npmrc +1 -0
- package/registry/templates/app/_opus/_gitignore +5 -0
- package/registry/templates/app/_prettierrc.json +6 -0
- package/registry/templates/app/index.html +13 -0
- package/registry/templates/app/opus.config.ts +16 -0
- package/registry/templates/app/package.json +43 -0
- package/registry/templates/app/pnpm-workspace.yaml +11 -0
- package/registry/templates/app/public/favicon.svg +4 -0
- package/registry/templates/app/src/App.tsx +37 -0
- package/registry/templates/app/src/domains/tasks/actions/list.test.ts +34 -0
- package/registry/templates/app/src/domains/tasks/actions/list.ts +33 -0
- package/registry/templates/app/src/domains/tasks/index.ts +13 -0
- package/registry/templates/app/src/index.css +18 -0
- package/registry/templates/app/src/main.tsx +25 -0
- package/registry/templates/app/tsconfig.json +20 -0
- package/registry/templates/app/vite.config.ts +46 -0
- package/registry/templates/monorepo/_gitignore +3 -0
- package/registry/templates/monorepo/_npmrc +1 -0
- package/registry/templates/monorepo/package.json +9 -0
- package/registry/templates/monorepo/pnpm-workspace.yaml +14 -0
- package/src/ai/ask.ts +64 -0
- package/src/ai/drivers/anthropic.ts +309 -0
- package/src/ai/index.ts +17 -0
- package/src/audit/drivers/console.ts +117 -0
- package/src/audit/drivers/pg.ts +172 -0
- package/src/audit/index.ts +51 -0
- package/src/auth/drivers/better-auth.ts +103 -0
- package/src/auth/drivers/jwt.ts +188 -0
- package/src/auth/index.ts +9 -0
- package/src/client/drivers/fetch.ts +202 -0
- package/src/client/index.ts +22 -0
- package/src/core/actions.ts +110 -0
- package/src/core/audit.ts +239 -0
- package/src/core/contracts.ts +137 -0
- package/src/core/domain.ts +310 -0
- package/src/core/errors.ts +181 -0
- package/src/core/index.ts +174 -0
- package/src/core/logical-type.ts +31 -0
- package/src/core/reactions.ts +81 -0
- package/src/core/runtime.ts +1167 -0
- package/src/core/schedules.ts +41 -0
- package/src/core/types.ts +1356 -0
- package/src/data/drivers/kysely.ts +389 -0
- package/src/data/index.ts +10 -0
- package/src/data/readonly-pool.ts +160 -0
- package/src/dsl/eval.ts +136 -0
- package/src/dsl/index.ts +29 -0
- package/src/dsl/kysely.ts +230 -0
- package/src/dsl/loads.ts +123 -0
- package/src/dsl/parser.ts +423 -0
- package/src/dsl/types.ts +113 -0
- package/src/events/drivers/mitt.ts +70 -0
- package/src/events/index.ts +9 -0
- package/src/log/drivers/pino.ts +57 -0
- package/src/log/index.ts +9 -0
- package/src/mcp/index.ts +62 -0
- package/src/queue/drivers/bullmq.ts +190 -0
- package/src/queue/index.ts +9 -0
- package/src/scheduler/drivers/node-cron.ts +93 -0
- package/src/scheduler/every.ts +45 -0
- package/src/scheduler/index.ts +9 -0
- package/src/schema/drivers/zod.ts +765 -0
- package/src/schema/entity.ts +439 -0
- package/src/schema/format/locale.ts +144 -0
- package/src/schema/index.ts +65 -0
- package/src/schema/openapi.ts +302 -0
- package/src/schema/scaffold.ts +160 -0
- package/src/server/drivers/fastify.ts +224 -0
- package/src/server/drivers/node.ts +386 -0
- package/src/server/index.ts +142 -0
- package/src/storage/drivers/fs.ts +90 -0
- package/src/storage/drivers/s3.ts +117 -0
- package/src/storage/index.ts +27 -0
- package/src/testing/fake.ts +298 -0
- package/src/testing/index.ts +324 -0
- package/src/ui/components/patterns/action-form-card.tsx +48 -0
- package/src/ui/components/patterns/action-list-dialog.tsx +93 -0
- package/src/ui/components/patterns/app-shell.tsx +227 -0
- package/src/ui/components/patterns/confirm.tsx +226 -0
- package/src/ui/components/patterns/data-state.tsx +75 -0
- package/src/ui/components/patterns/form-dialog.tsx +64 -0
- package/src/ui/components/patterns/form.tsx +584 -0
- package/src/ui/components/patterns/list.tsx +1488 -0
- package/src/ui/components/patterns/page.tsx +46 -0
- package/src/ui/components/patterns/section-shell.tsx +246 -0
- package/src/ui/components/patterns/shell-nav.tsx +150 -0
- package/src/ui/components/patterns/sidebar.tsx +89 -0
- package/src/ui/components/patterns/split.tsx +93 -0
- package/src/ui/components/patterns/trigger.tsx +196 -0
- package/src/ui/components/patterns/view.tsx +84 -0
- package/src/ui/components/primitives/accordion.tsx +64 -0
- package/src/ui/components/primitives/alert-dialog.tsx +190 -0
- package/src/ui/components/primitives/alert.tsx +116 -0
- package/src/ui/components/primitives/aspect-ratio.tsx +9 -0
- package/src/ui/components/primitives/avatar.tsx +107 -0
- package/src/ui/components/primitives/badge.tsx +37 -0
- package/src/ui/components/primitives/breadcrumb.tsx +109 -0
- package/src/ui/components/primitives/button-group.tsx +83 -0
- package/src/ui/components/primitives/button.tsx +102 -0
- package/src/ui/components/primitives/calendar.tsx +218 -0
- package/src/ui/components/primitives/card.tsx +56 -0
- package/src/ui/components/primitives/carousel.tsx +239 -0
- package/src/ui/components/primitives/chat.tsx +407 -0
- package/src/ui/components/primitives/checkbox.tsx +30 -0
- package/src/ui/components/primitives/collapsible.tsx +31 -0
- package/src/ui/components/primitives/command.tsx +182 -0
- package/src/ui/components/primitives/composer.tsx +121 -0
- package/src/ui/components/primitives/copyable.tsx +50 -0
- package/src/ui/components/primitives/dialog.tsx +147 -0
- package/src/ui/components/primitives/drawer.tsx +141 -0
- package/src/ui/components/primitives/empty.tsx +104 -0
- package/src/ui/components/primitives/field.tsx +246 -0
- package/src/ui/components/primitives/icon-picker.tsx +180 -0
- package/src/ui/components/primitives/input-group.tsx +168 -0
- package/src/ui/components/primitives/input-otp.tsx +75 -0
- package/src/ui/components/primitives/input.tsx +72 -0
- package/src/ui/components/primitives/item.tsx +193 -0
- package/src/ui/components/primitives/kbd.tsx +28 -0
- package/src/ui/components/primitives/label.tsx +22 -0
- package/src/ui/components/primitives/markdown.tsx +35 -0
- package/src/ui/components/primitives/menu.tsx +255 -0
- package/src/ui/components/primitives/pagination.tsx +127 -0
- package/src/ui/components/primitives/popover.tsx +87 -0
- package/src/ui/components/primitives/progress.tsx +29 -0
- package/src/ui/components/primitives/radio-group.tsx +43 -0
- package/src/ui/components/primitives/resizable.tsx +51 -0
- package/src/ui/components/primitives/scroll-area.tsx +56 -0
- package/src/ui/components/primitives/select.tsx +479 -0
- package/src/ui/components/primitives/separator.tsx +26 -0
- package/src/ui/components/primitives/skeleton.tsx +13 -0
- package/src/ui/components/primitives/slider.tsx +61 -0
- package/src/ui/components/primitives/sonner.tsx +46 -0
- package/src/ui/components/primitives/spinner.tsx +29 -0
- package/src/ui/components/primitives/switch.tsx +33 -0
- package/src/ui/components/primitives/table.tsx +114 -0
- package/src/ui/components/primitives/tabs.tsx +104 -0
- package/src/ui/components/primitives/textarea.tsx +18 -0
- package/src/ui/components/primitives/toggle-group.tsx +81 -0
- package/src/ui/components/primitives/toggle.tsx +45 -0
- package/src/ui/components/primitives/tooltip.tsx +55 -0
- package/src/ui/components/primitives/truncate.tsx +49 -0
- package/src/ui/docs/DocBrowser.tsx +90 -0
- package/src/ui/docs/changelog.tsx +80 -0
- package/src/ui/docs/content/accordion.md +86 -0
- package/src/ui/docs/content/action-form-card.md +24 -0
- package/src/ui/docs/content/action-form-dialog.md +30 -0
- package/src/ui/docs/content/action-form.md +125 -0
- package/src/ui/docs/content/action-list-dialog.md +68 -0
- package/src/ui/docs/content/action-list.md +194 -0
- package/src/ui/docs/content/action-trigger.md +72 -0
- package/src/ui/docs/content/action-view.md +47 -0
- package/src/ui/docs/content/actions.md +138 -0
- package/src/ui/docs/content/ai.md +112 -0
- package/src/ui/docs/content/alert-dialog.md +73 -0
- package/src/ui/docs/content/alert.md +69 -0
- package/src/ui/docs/content/app-shell.md +155 -0
- package/src/ui/docs/content/aspect-ratio.md +66 -0
- package/src/ui/docs/content/audit.md +84 -0
- package/src/ui/docs/content/auth.md +70 -0
- package/src/ui/docs/content/avatar.md +94 -0
- package/src/ui/docs/content/badge.md +48 -0
- package/src/ui/docs/content/breadcrumb.md +87 -0
- package/src/ui/docs/content/button-group.md +71 -0
- package/src/ui/docs/content/button.md +60 -0
- package/src/ui/docs/content/calendar.md +62 -0
- package/src/ui/docs/content/card.md +49 -0
- package/src/ui/docs/content/carousel.md +85 -0
- package/src/ui/docs/content/chat.md +69 -0
- package/src/ui/docs/content/checkbox.md +75 -0
- package/src/ui/docs/content/cli.md +58 -0
- package/src/ui/docs/content/collapsible.md +64 -0
- package/src/ui/docs/content/command.md +56 -0
- package/src/ui/docs/content/composer.md +50 -0
- package/src/ui/docs/content/confirm.md +120 -0
- package/src/ui/docs/content/copyable.md +30 -0
- package/src/ui/docs/content/customization.md +110 -0
- package/src/ui/docs/content/cycle.md +34 -0
- package/src/ui/docs/content/data-state.md +47 -0
- package/src/ui/docs/content/data.md +99 -0
- package/src/ui/docs/content/dialog.md +60 -0
- package/src/ui/docs/content/drawer.md +55 -0
- package/src/ui/docs/content/empty.md +66 -0
- package/src/ui/docs/content/events.md +61 -0
- package/src/ui/docs/content/field.md +58 -0
- package/src/ui/docs/content/getting-started.md +109 -0
- package/src/ui/docs/content/icon-picker.md +51 -0
- package/src/ui/docs/content/input-group.md +78 -0
- package/src/ui/docs/content/input-otp.md +72 -0
- package/src/ui/docs/content/input.md +78 -0
- package/src/ui/docs/content/item.md +84 -0
- package/src/ui/docs/content/kbd.md +62 -0
- package/src/ui/docs/content/label.md +32 -0
- package/src/ui/docs/content/log.md +55 -0
- package/src/ui/docs/content/markdown.md +41 -0
- package/src/ui/docs/content/mcp.md +44 -0
- package/src/ui/docs/content/menu.md +114 -0
- package/src/ui/docs/content/microcopy.md +83 -0
- package/src/ui/docs/content/page.md +34 -0
- package/src/ui/docs/content/pagination.md +99 -0
- package/src/ui/docs/content/popover.md +49 -0
- package/src/ui/docs/content/progress.md +69 -0
- package/src/ui/docs/content/queue.md +62 -0
- package/src/ui/docs/content/radio-group.md +77 -0
- package/src/ui/docs/content/resizable.md +86 -0
- package/src/ui/docs/content/router.md +56 -0
- package/src/ui/docs/content/runtime.md +77 -0
- package/src/ui/docs/content/scheduler.md +66 -0
- package/src/ui/docs/content/scroll-area.md +89 -0
- package/src/ui/docs/content/section-shell.md +121 -0
- package/src/ui/docs/content/select.md +342 -0
- package/src/ui/docs/content/separator.md +33 -0
- package/src/ui/docs/content/sidebar.md +38 -0
- package/src/ui/docs/content/skeleton.md +34 -0
- package/src/ui/docs/content/slider.md +64 -0
- package/src/ui/docs/content/spinner.md +37 -0
- package/src/ui/docs/content/split.md +33 -0
- package/src/ui/docs/content/storage.md +69 -0
- package/src/ui/docs/content/switch.md +69 -0
- package/src/ui/docs/content/table.md +102 -0
- package/src/ui/docs/content/tabs.md +94 -0
- package/src/ui/docs/content/testing.md +89 -0
- package/src/ui/docs/content/textarea.md +30 -0
- package/src/ui/docs/content/toast.md +67 -0
- package/src/ui/docs/content/toggle-group.md +81 -0
- package/src/ui/docs/content/toggle.md +72 -0
- package/src/ui/docs/content/tokens.md +171 -0
- package/src/ui/docs/content/tooltip.md +50 -0
- package/src/ui/docs/content/truncate.md +37 -0
- package/src/ui/docs/content/ui.md +40 -0
- package/src/ui/docs/content/upgrading.md +48 -0
- package/src/ui/docs/doc-client.tsx +214 -0
- package/src/ui/docs/doc.tsx +301 -0
- package/src/ui/docs/folder.tsx +149 -0
- package/src/ui/docs/index.ts +21 -0
- package/src/ui/docs/markdown.tsx +130 -0
- package/src/ui/docs/md-raw.d.ts +4 -0
- package/src/ui/docs/plugin.ts +104 -0
- package/src/ui/docs/registry.tsx +424 -0
- package/src/ui/docs/standalone.tsx +107 -0
- package/src/ui/drivers/react.tsx +627 -0
- package/src/ui/index.ts +92 -0
- package/src/ui/lib/cn.ts +10 -0
- package/src/ui/lib/zod-pt-br.ts +38 -0
- package/src/ui/meta.ts +412 -0
- package/src/ui/react.tsx +235 -0
- package/src/ui/router.ts +96 -0
- package/src/ui/theme.css +234 -0
- package/src/vite/design.ts +652 -0
- package/src/vite/index.ts +8 -0
package/bin/lib/gen.mjs
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gen — orquestra o comando `opus gen`.
|
|
3
|
+
*
|
|
4
|
+
* Fluxo:
|
|
5
|
+
* 1. Resolve config path (--config ou ./opus.config.ts).
|
|
6
|
+
* 2. Spawn `tsx <package>/bin/lib/gen-runner.mjs <configPath>` capturando
|
|
7
|
+
* stdout. Runner emite linha JSON com payload serializado.
|
|
8
|
+
* 3. Constrói manifest, openapi, docs, client-stubs.
|
|
9
|
+
* 4. Escreve em <output>/manifest.json, openapi.json, docs/*.md,
|
|
10
|
+
* client-stubs/*.ts.
|
|
11
|
+
* 5. Poda órfãos: .md/.ts que rodada anterior gerou e esta não gerou saem
|
|
12
|
+
* do disco (docs/ é a superfície de spec que o AGENTE lê — doc órfã
|
|
13
|
+
* anuncia endpoint que já dá 404).
|
|
14
|
+
*
|
|
15
|
+
* Defaults:
|
|
16
|
+
* - config: ./opus.config.ts (no cwd)
|
|
17
|
+
* - output: o que o config disser, ou ./.gen
|
|
18
|
+
*
|
|
19
|
+
* Flag `--force`: sobrescreve arquivos existentes (default sobrescreve
|
|
20
|
+
* silenciosamente — opus gen é idempotente e o output mora num dir
|
|
21
|
+
* dedicado, então não há valor real em bloquear sem --force. Mantemos a
|
|
22
|
+
* flag pra paridade com `add`).
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { promises as fs } from 'node:fs'
|
|
26
|
+
import path from 'node:path'
|
|
27
|
+
import { fileURLToPath } from 'node:url'
|
|
28
|
+
import { execFile } from 'node:child_process'
|
|
29
|
+
import { promisify } from 'node:util'
|
|
30
|
+
|
|
31
|
+
import { buildManifest } from './gen-manifest.mjs'
|
|
32
|
+
import { buildOpenAPI } from './gen-openapi.mjs'
|
|
33
|
+
import { buildDocs } from './gen-docs.mjs'
|
|
34
|
+
import { buildClientStubs } from './gen-stubs.mjs'
|
|
35
|
+
import { buildDictStubs } from './gen-dicts.mjs'
|
|
36
|
+
|
|
37
|
+
const execFileAsync = promisify(execFile)
|
|
38
|
+
|
|
39
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
40
|
+
const __dirname = path.dirname(__filename)
|
|
41
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..', '..')
|
|
42
|
+
const RUNNER_PATH = path.join(__dirname, 'gen-runner.mjs')
|
|
43
|
+
|
|
44
|
+
const COLORS = {
|
|
45
|
+
info: '\x1b[36m',
|
|
46
|
+
success: '\x1b[32m',
|
|
47
|
+
error: '\x1b[31m',
|
|
48
|
+
warn: '\x1b[33m',
|
|
49
|
+
dim: '\x1b[2m',
|
|
50
|
+
}
|
|
51
|
+
const RESET = '\x1b[0m'
|
|
52
|
+
|
|
53
|
+
function log(level, msg) {
|
|
54
|
+
console.log(`${COLORS[level] ?? ''}${msg}${RESET}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// =============================================================================
|
|
58
|
+
// Comando
|
|
59
|
+
// =============================================================================
|
|
60
|
+
|
|
61
|
+
export async function cmdGen(flags) {
|
|
62
|
+
const cwd = process.cwd()
|
|
63
|
+
const configRel = flags.config ?? 'opus.config.ts'
|
|
64
|
+
const configPath = path.isAbsolute(configRel)
|
|
65
|
+
? configRel
|
|
66
|
+
: path.resolve(cwd, configRel)
|
|
67
|
+
|
|
68
|
+
if (!(await fileExists(configPath))) {
|
|
69
|
+
log('error', `opus.config.ts não encontrado em ${configPath}`)
|
|
70
|
+
log(
|
|
71
|
+
'dim',
|
|
72
|
+
' Cria um arquivo na raiz do projeto exportando `{ domains: [...] }` como default,',
|
|
73
|
+
)
|
|
74
|
+
log('dim', ' ou passa o path via --config <path>.')
|
|
75
|
+
process.exit(1)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
log('info', `→ Carregando ${path.relative(cwd, configPath)}...`)
|
|
79
|
+
|
|
80
|
+
const payload = await loadConfig(configPath)
|
|
81
|
+
|
|
82
|
+
// Output: flag > config > default.
|
|
83
|
+
const outputDir = flags.output ?? payload.output ?? '.gen'
|
|
84
|
+
const outputAbs = path.isAbsolute(outputDir)
|
|
85
|
+
? outputDir
|
|
86
|
+
: path.resolve(cwd, outputDir)
|
|
87
|
+
|
|
88
|
+
const manifest = buildManifest(payload)
|
|
89
|
+
const openapi = buildOpenAPI(manifest)
|
|
90
|
+
const docs = buildDocs(manifest)
|
|
91
|
+
const stubs = buildClientStubs(manifest)
|
|
92
|
+
const dictStubs = buildDictStubs(manifest)
|
|
93
|
+
|
|
94
|
+
await fs.mkdir(outputAbs, { recursive: true })
|
|
95
|
+
await fs.mkdir(path.join(outputAbs, 'docs'), { recursive: true })
|
|
96
|
+
await fs.mkdir(path.join(outputAbs, 'client-stubs'), { recursive: true })
|
|
97
|
+
if (dictStubs.length > 0) {
|
|
98
|
+
await fs.mkdir(path.join(outputAbs, 'client-stubs', 'dicts'), {
|
|
99
|
+
recursive: true,
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await writeJson(path.join(outputAbs, 'manifest.json'), manifest, flags.force)
|
|
104
|
+
await writeJson(path.join(outputAbs, 'openapi.json'), openapi, flags.force)
|
|
105
|
+
|
|
106
|
+
for (const doc of docs) {
|
|
107
|
+
await writeText(path.join(outputAbs, 'docs', doc.filename), doc.content, flags.force)
|
|
108
|
+
}
|
|
109
|
+
for (const stub of stubs) {
|
|
110
|
+
await writeText(
|
|
111
|
+
path.join(outputAbs, 'client-stubs', stub.filename),
|
|
112
|
+
stub.content,
|
|
113
|
+
flags.force,
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
for (const dict of dictStubs) {
|
|
117
|
+
await writeText(
|
|
118
|
+
path.join(outputAbs, 'client-stubs', 'dicts', dict.filename),
|
|
119
|
+
dict.content,
|
|
120
|
+
flags.force,
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const pruned = [
|
|
125
|
+
...(await pruneOrphans(path.join(outputAbs, 'docs'), new Set(docs.map((d) => d.filename)), ['.md'])).map(
|
|
126
|
+
(f) => `docs/${f}`,
|
|
127
|
+
),
|
|
128
|
+
...(await pruneOrphans(path.join(outputAbs, 'client-stubs'), new Set(stubs.map((s) => s.filename)), ['.ts'])).map(
|
|
129
|
+
(f) => `client-stubs/${f}`,
|
|
130
|
+
),
|
|
131
|
+
...(
|
|
132
|
+
await pruneOrphans(path.join(outputAbs, 'client-stubs', 'dicts'), new Set(dictStubs.map((d) => d.filename)), ['.ts'])
|
|
133
|
+
).map((f) => `client-stubs/dicts/${f}`),
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
log('success', `✓ Gerado em ${path.relative(cwd, outputAbs)}`)
|
|
137
|
+
log('dim', ` manifest.json`)
|
|
138
|
+
log('dim', ` openapi.json`)
|
|
139
|
+
log('dim', ` docs/ (${docs.length} arquivo${docs.length === 1 ? '' : 's'})`)
|
|
140
|
+
log(
|
|
141
|
+
'dim',
|
|
142
|
+
` client-stubs/ (${stubs.length} arquivo${stubs.length === 1 ? '' : 's'})`,
|
|
143
|
+
)
|
|
144
|
+
if (dictStubs.length > 0) {
|
|
145
|
+
log(
|
|
146
|
+
'dim',
|
|
147
|
+
` client-stubs/dicts/ (${dictStubs.length} arquivo${dictStubs.length === 1 ? '' : 's'})`,
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
if (pruned.length > 0) {
|
|
151
|
+
log('dim', ` podado${pruned.length === 1 ? '' : 's'} ${pruned.length} órfão${pruned.length === 1 ? '' : 's'}: ${pruned.join(', ')}`)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Auditoria de doc: o `description` é a fonte de entendimento (negócio + contrato) e
|
|
155
|
+
// flui pro manifest/lente. O gen vê a descrição RESOLVIDA (concatenada inclusive) —
|
|
156
|
+
// por isso a checagem mora aqui, não no opus check estático. Avisa, não reprova.
|
|
157
|
+
auditDocs(manifest, cwd)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Lista as actions sem `description` no manifest (o doc de negócio — a fonte). */
|
|
161
|
+
function auditDocs(manifest, cwd) {
|
|
162
|
+
const missing = []
|
|
163
|
+
const walk = (d) => {
|
|
164
|
+
for (const a of d.actions ?? []) {
|
|
165
|
+
const doc = typeof a.description === 'string' ? a.description.trim() : ''
|
|
166
|
+
if (doc.length === 0) missing.push(a.name)
|
|
167
|
+
}
|
|
168
|
+
for (const sub of d.subdomains ?? []) walk(sub)
|
|
169
|
+
}
|
|
170
|
+
for (const d of manifest.domains ?? []) walk(d)
|
|
171
|
+
if (missing.length === 0) {
|
|
172
|
+
log('success', ' ✓ doc: todas as actions documentadas (spec abrange tudo).')
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
log('warn', `\n ⚠ doc: ${missing.length} action(s) sem \`description\` (o doc de negócio — a fonte):`)
|
|
176
|
+
for (const name of missing) log('dim', ` • ${name}`)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// =============================================================================
|
|
180
|
+
// Runner spawn
|
|
181
|
+
// =============================================================================
|
|
182
|
+
|
|
183
|
+
async function loadConfig(configPath) {
|
|
184
|
+
// tsx mora em <package>/node_modules/.bin/tsx — usamos absoluto pra
|
|
185
|
+
// evitar depender de PATH do consumer.
|
|
186
|
+
const tsxBin = path.join(PACKAGE_ROOT, 'node_modules', '.bin', 'tsx')
|
|
187
|
+
const hasLocalTsx = await fileExists(tsxBin)
|
|
188
|
+
|
|
189
|
+
// Args do runner: <runner.mjs> <configPath>. tsx aceita .mjs também
|
|
190
|
+
// (passthrough) e nos permite resolver TS na cadeia de imports.
|
|
191
|
+
const args = [RUNNER_PATH, configPath]
|
|
192
|
+
const cmd = hasLocalTsx ? tsxBin : 'tsx'
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// Buffer grande pra acomodar manifests gordos.
|
|
196
|
+
const { stdout, stderr } = await execFileAsync(cmd, args, {
|
|
197
|
+
encoding: 'utf8',
|
|
198
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
199
|
+
env: { ...process.env },
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// Runner emite a linha JSON na ÚLTIMA linha de stdout. Qualquer log do
|
|
203
|
+
// consumer durante o import vai pro inicio — varremos de trás pra
|
|
204
|
+
// frente.
|
|
205
|
+
const line = lastJsonLine(stdout)
|
|
206
|
+
if (line === null) {
|
|
207
|
+
log('error', 'Runner não emitiu payload JSON válido.')
|
|
208
|
+
if (stderr.length > 0) log('dim', stderr.trim())
|
|
209
|
+
if (stdout.length > 0) log('dim', stdout.trim())
|
|
210
|
+
process.exit(1)
|
|
211
|
+
}
|
|
212
|
+
const parsed = JSON.parse(line)
|
|
213
|
+
if (parsed.ok !== true) {
|
|
214
|
+
log('error', `Falha ao carregar config: ${parsed.error}`)
|
|
215
|
+
process.exit(1)
|
|
216
|
+
}
|
|
217
|
+
return parsed.payload
|
|
218
|
+
} catch (err) {
|
|
219
|
+
log('error', `Falha ao executar tsx: ${err.message}`)
|
|
220
|
+
if (typeof err.stderr === 'string' && err.stderr.length > 0) {
|
|
221
|
+
log('dim', err.stderr.trim())
|
|
222
|
+
}
|
|
223
|
+
if (typeof err.stdout === 'string' && err.stdout.length > 0) {
|
|
224
|
+
log('dim', err.stdout.trim())
|
|
225
|
+
}
|
|
226
|
+
process.exit(1)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function lastJsonLine(stdout) {
|
|
231
|
+
const lines = stdout.split('\n').filter((l) => l.trim().length > 0)
|
|
232
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
233
|
+
const trimmed = lines[i].trim()
|
|
234
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
235
|
+
return trimmed
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return null
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// =============================================================================
|
|
242
|
+
// FS helpers
|
|
243
|
+
// =============================================================================
|
|
244
|
+
|
|
245
|
+
async function fileExists(p) {
|
|
246
|
+
try {
|
|
247
|
+
await fs.access(p)
|
|
248
|
+
return true
|
|
249
|
+
} catch {
|
|
250
|
+
return false
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function writeJson(p, value, _force) {
|
|
255
|
+
await fs.writeFile(p, JSON.stringify(value, null, 2) + '\n', 'utf8')
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Poda órfãos de um dir gerenciado pelo gen: arquivo com extensão gerada (`exts`)
|
|
260
|
+
* que NÃO saiu desta rodada (`keep`) é removido. Só olha arquivos do próprio dir
|
|
261
|
+
* (subdirs têm chamada própria) e só as extensões geradas — qualquer outra coisa
|
|
262
|
+
* que o consumer tenha posto lá fica intacta. Exportada pra teste.
|
|
263
|
+
*/
|
|
264
|
+
export async function pruneOrphans(dir, keep, exts) {
|
|
265
|
+
let entries
|
|
266
|
+
try {
|
|
267
|
+
entries = await fs.readdir(dir, { withFileTypes: true })
|
|
268
|
+
} catch {
|
|
269
|
+
return [] // dir nem existe → nada a podar
|
|
270
|
+
}
|
|
271
|
+
const removed = []
|
|
272
|
+
for (const e of entries) {
|
|
273
|
+
if (!e.isFile()) continue
|
|
274
|
+
if (!exts.some((x) => e.name.endsWith(x))) continue
|
|
275
|
+
if (keep.has(e.name)) continue
|
|
276
|
+
await fs.unlink(path.join(dir, e.name))
|
|
277
|
+
removed.push(e.name)
|
|
278
|
+
}
|
|
279
|
+
return removed.sort()
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function writeText(p, content, _force) {
|
|
283
|
+
await fs.writeFile(p, content, 'utf8')
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// =============================================================================
|
|
287
|
+
// Help
|
|
288
|
+
// =============================================================================
|
|
289
|
+
|
|
290
|
+
export function helpGen() {
|
|
291
|
+
console.log(`
|
|
292
|
+
@softize/opus gen
|
|
293
|
+
|
|
294
|
+
Lê opus.config.ts do consumer e gera:
|
|
295
|
+
- <output>/manifest.json Spec serializada (JSON Schema dos inputs/outputs)
|
|
296
|
+
- <output>/openapi.json OpenAPI 3.1 derivada do manifest
|
|
297
|
+
- <output>/docs/<domain>.md Doc human-readable por domínio
|
|
298
|
+
- <output>/client-stubs/<domain>.ts Stubs com schema Zod reconstruído pro frontend
|
|
299
|
+
- <output>/client-stubs/dicts/<domain>.ts Dicts (t.dict({...})) prontos pro frontend
|
|
300
|
+
|
|
301
|
+
Flags:
|
|
302
|
+
--config <path> Caminho do opus.config.ts. Default: ./opus.config.ts
|
|
303
|
+
--output <path> Pasta de saída. Default: do config (ou ./.gen)
|
|
304
|
+
--force, -f Sobrescreve existente (atualmente sempre sobrescreve)
|
|
305
|
+
|
|
306
|
+
Exemplos:
|
|
307
|
+
npx @softize/opus gen
|
|
308
|
+
npx @softize/opus gen --config ./apps/api/opus.config.ts
|
|
309
|
+
npx @softize/opus gen --output ./custom/gen
|
|
310
|
+
`)
|
|
311
|
+
}
|