@jaimevalasek/aioson 1.3.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/CHANGELOG.md +456 -0
- package/CODE_OF_CONDUCT.md +12 -0
- package/CONTRIBUTING.md +13 -0
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/bin/aioson.js +4 -0
- package/docs/en/cli-reference.md +398 -0
- package/docs/en/i18n.md +52 -0
- package/docs/en/json-schemas.md +41 -0
- package/docs/en/mcp.md +56 -0
- package/docs/en/parallel.md +82 -0
- package/docs/en/qa-browser.md +339 -0
- package/docs/en/release-flow.md +22 -0
- package/docs/en/release-notes-template.md +41 -0
- package/docs/en/release.md +28 -0
- package/docs/en/schemas/agent-prompt.schema.json +17 -0
- package/docs/en/schemas/agents.schema.json +32 -0
- package/docs/en/schemas/context-validate.schema.json +36 -0
- package/docs/en/schemas/doctor.schema.json +89 -0
- package/docs/en/schemas/error.schema.json +24 -0
- package/docs/en/schemas/i18n-add.schema.json +15 -0
- package/docs/en/schemas/index.json +116 -0
- package/docs/en/schemas/info.schema.json +39 -0
- package/docs/en/schemas/init.schema.json +48 -0
- package/docs/en/schemas/install.schema.json +60 -0
- package/docs/en/schemas/locale-apply.schema.json +30 -0
- package/docs/en/schemas/mcp-doctor.schema.json +95 -0
- package/docs/en/schemas/mcp-init.schema.json +122 -0
- package/docs/en/schemas/package-test.schema.json +24 -0
- package/docs/en/schemas/parallel-assign.schema.json +57 -0
- package/docs/en/schemas/parallel-doctor.schema.json +86 -0
- package/docs/en/schemas/parallel-init.schema.json +53 -0
- package/docs/en/schemas/parallel-status.schema.json +94 -0
- package/docs/en/schemas/setup-context.schema.json +39 -0
- package/docs/en/schemas/smoke.schema.json +23 -0
- package/docs/en/schemas/update.schema.json +48 -0
- package/docs/en/schemas/workflow-plan.schema.json +30 -0
- package/docs/en/web3.md +54 -0
- package/docs/pt/README.md +46 -0
- package/docs/pt/advisor-spec.md +335 -0
- package/docs/pt/agentes.md +453 -0
- package/docs/pt/cenarios.md +1230 -0
- package/docs/pt/clientes-ai.md +224 -0
- package/docs/pt/comandos-cli.md +511 -0
- package/docs/pt/genome-3.0-spec.md +296 -0
- package/docs/pt/guia-engineer.md +226 -0
- package/docs/pt/inicio-rapido.md +138 -0
- package/docs/pt/profiler-system.md +214 -0
- package/docs/pt/runtime-observability.md +72 -0
- package/docs/pt/squad-genoma.md +777 -0
- package/docs/pt/web3.md +797 -0
- package/docs/testing/genome-2.0-manual-regression.md +23 -0
- package/docs/testing/genome-2.0-matrix.md +36 -0
- package/docs/testing/genome-2.0-rollout.md +184 -0
- package/package.json +50 -0
- package/src/agents.js +56 -0
- package/src/cli.js +497 -0
- package/src/commands/agents.js +142 -0
- package/src/commands/cloud.js +1767 -0
- package/src/commands/config.js +90 -0
- package/src/commands/context-validate.js +91 -0
- package/src/commands/doctor.js +123 -0
- package/src/commands/genome-doctor.js +41 -0
- package/src/commands/genome-migrate.js +49 -0
- package/src/commands/i18n-add.js +56 -0
- package/src/commands/info.js +41 -0
- package/src/commands/init.js +75 -0
- package/src/commands/install.js +68 -0
- package/src/commands/locale-apply.js +51 -0
- package/src/commands/locale-diff.js +126 -0
- package/src/commands/mcp-doctor.js +406 -0
- package/src/commands/mcp-init.js +379 -0
- package/src/commands/package-e2e.js +273 -0
- package/src/commands/parallel-assign.js +403 -0
- package/src/commands/parallel-doctor.js +437 -0
- package/src/commands/parallel-init.js +249 -0
- package/src/commands/parallel-status.js +290 -0
- package/src/commands/qa-doctor.js +185 -0
- package/src/commands/qa-init.js +161 -0
- package/src/commands/qa-report.js +58 -0
- package/src/commands/qa-run.js +873 -0
- package/src/commands/qa-scan.js +337 -0
- package/src/commands/runtime.js +948 -0
- package/src/commands/scan-project.js +1107 -0
- package/src/commands/setup-context.js +650 -0
- package/src/commands/smoke.js +426 -0
- package/src/commands/squad-doctor.js +358 -0
- package/src/commands/squad-export.js +46 -0
- package/src/commands/squad-pipeline.js +97 -0
- package/src/commands/squad-repair-genomes.js +39 -0
- package/src/commands/squad-status.js +424 -0
- package/src/commands/squad-validate.js +230 -0
- package/src/commands/test-agents.js +194 -0
- package/src/commands/update.js +55 -0
- package/src/commands/workflow-next.js +594 -0
- package/src/commands/workflow-plan.js +108 -0
- package/src/constants.js +314 -0
- package/src/context-parse-reason.js +22 -0
- package/src/context-writer.js +150 -0
- package/src/context.js +217 -0
- package/src/detector.js +261 -0
- package/src/doctor.js +289 -0
- package/src/execution-gateway.js +461 -0
- package/src/genome-files.js +198 -0
- package/src/genome-format.js +442 -0
- package/src/genome-schema.js +215 -0
- package/src/genomes/bindings.js +281 -0
- package/src/genomes.js +467 -0
- package/src/i18n/index.js +103 -0
- package/src/i18n/messages/en.js +784 -0
- package/src/i18n/messages/es.js +718 -0
- package/src/i18n/messages/fr.js +725 -0
- package/src/i18n/messages/pt-BR.js +818 -0
- package/src/i18n/scaffold.js +64 -0
- package/src/installer.js +232 -0
- package/src/lib/genomes/compat.js +206 -0
- package/src/lib/genomes/migrate.js +90 -0
- package/src/lib/squads/genome-repair.js +49 -0
- package/src/locales.js +84 -0
- package/src/onboarding.js +305 -0
- package/src/parser.js +53 -0
- package/src/prompt-tool.js +20 -0
- package/src/qa-html-report.js +472 -0
- package/src/runtime-store.js +1527 -0
- package/src/squads/apply-genome.js +21 -0
- package/src/squads/genome-binding-service.js +154 -0
- package/src/updater.js +32 -0
- package/src/utils.js +46 -0
- package/src/version.js +50 -0
- package/template/.aioson/advisors/.gitkeep +1 -0
- package/template/.aioson/agents/analyst.md +225 -0
- package/template/.aioson/agents/architect.md +221 -0
- package/template/.aioson/agents/dev.md +201 -0
- package/template/.aioson/agents/discovery-design-doc.md +196 -0
- package/template/.aioson/agents/genoma.md +300 -0
- package/template/.aioson/agents/orchestrator.md +107 -0
- package/template/.aioson/agents/pm.md +89 -0
- package/template/.aioson/agents/product.md +361 -0
- package/template/.aioson/agents/profiler-enricher.md +266 -0
- package/template/.aioson/agents/profiler-forge.md +188 -0
- package/template/.aioson/agents/profiler-researcher.md +245 -0
- package/template/.aioson/agents/qa.md +344 -0
- package/template/.aioson/agents/setup.md +381 -0
- package/template/.aioson/agents/squad.md +837 -0
- package/template/.aioson/agents/ux-ui.md +416 -0
- package/template/.aioson/config.md +56 -0
- package/template/.aioson/context/.gitkeep +0 -0
- package/template/.aioson/context/parallel/.gitkeep +0 -0
- package/template/.aioson/context/spec.md.template +37 -0
- package/template/.aioson/genomas/.gitkeep +0 -0
- package/template/.aioson/locales/en/agents/analyst.md +214 -0
- package/template/.aioson/locales/en/agents/architect.md +210 -0
- package/template/.aioson/locales/en/agents/dev.md +187 -0
- package/template/.aioson/locales/en/agents/discovery-design-doc.md +27 -0
- package/template/.aioson/locales/en/agents/genoma.md +212 -0
- package/template/.aioson/locales/en/agents/orchestrator.md +105 -0
- package/template/.aioson/locales/en/agents/pm.md +77 -0
- package/template/.aioson/locales/en/agents/product.md +310 -0
- package/template/.aioson/locales/en/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/en/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/en/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/en/agents/qa.md +214 -0
- package/template/.aioson/locales/en/agents/setup.md +342 -0
- package/template/.aioson/locales/en/agents/squad.md +247 -0
- package/template/.aioson/locales/en/agents/ux-ui.md +320 -0
- package/template/.aioson/locales/es/agents/analyst.md +203 -0
- package/template/.aioson/locales/es/agents/architect.md +208 -0
- package/template/.aioson/locales/es/agents/dev.md +183 -0
- package/template/.aioson/locales/es/agents/discovery-design-doc.md +19 -0
- package/template/.aioson/locales/es/agents/genoma.md +102 -0
- package/template/.aioson/locales/es/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/es/agents/pm.md +81 -0
- package/template/.aioson/locales/es/agents/product.md +310 -0
- package/template/.aioson/locales/es/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/es/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/es/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/es/agents/qa.md +163 -0
- package/template/.aioson/locales/es/agents/setup.md +347 -0
- package/template/.aioson/locales/es/agents/squad.md +247 -0
- package/template/.aioson/locales/es/agents/ux-ui.md +201 -0
- package/template/.aioson/locales/fr/agents/analyst.md +203 -0
- package/template/.aioson/locales/fr/agents/architect.md +208 -0
- package/template/.aioson/locales/fr/agents/dev.md +183 -0
- package/template/.aioson/locales/fr/agents/discovery-design-doc.md +19 -0
- package/template/.aioson/locales/fr/agents/genoma.md +102 -0
- package/template/.aioson/locales/fr/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/fr/agents/pm.md +81 -0
- package/template/.aioson/locales/fr/agents/product.md +310 -0
- package/template/.aioson/locales/fr/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/fr/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/fr/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/fr/agents/qa.md +163 -0
- package/template/.aioson/locales/fr/agents/setup.md +347 -0
- package/template/.aioson/locales/fr/agents/squad.md +247 -0
- package/template/.aioson/locales/fr/agents/ux-ui.md +201 -0
- package/template/.aioson/locales/pt-BR/agents/analyst.md +217 -0
- package/template/.aioson/locales/pt-BR/agents/architect.md +213 -0
- package/template/.aioson/locales/pt-BR/agents/dev.md +198 -0
- package/template/.aioson/locales/pt-BR/agents/discovery-design-doc.md +198 -0
- package/template/.aioson/locales/pt-BR/agents/genoma.md +297 -0
- package/template/.aioson/locales/pt-BR/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/pt-BR/agents/pm.md +81 -0
- package/template/.aioson/locales/pt-BR/agents/product.md +316 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/qa.md +217 -0
- package/template/.aioson/locales/pt-BR/agents/setup.md +371 -0
- package/template/.aioson/locales/pt-BR/agents/squad.md +772 -0
- package/template/.aioson/locales/pt-BR/agents/ux-ui.md +322 -0
- package/template/.aioson/mcp/servers.md +24 -0
- package/template/.aioson/profiler-reports/.gitkeep +1 -0
- package/template/.aioson/schemas/content-blueprint.schema.json +30 -0
- package/template/.aioson/schemas/genome-meta.schema.json +150 -0
- package/template/.aioson/schemas/genome.schema.json +115 -0
- package/template/.aioson/schemas/readiness.schema.json +27 -0
- package/template/.aioson/schemas/squad-blueprint.schema.json +172 -0
- package/template/.aioson/schemas/squad-manifest.schema.json +276 -0
- package/template/.aioson/skills/dynamic/README.md +30 -0
- package/template/.aioson/skills/dynamic/cardano-docs.md +16 -0
- package/template/.aioson/skills/dynamic/ethereum-docs.md +17 -0
- package/template/.aioson/skills/dynamic/flux-ui-docs.md +13 -0
- package/template/.aioson/skills/dynamic/laravel-docs.md +41 -0
- package/template/.aioson/skills/dynamic/npm-packages.md +16 -0
- package/template/.aioson/skills/dynamic/solana-docs.md +16 -0
- package/template/.aioson/skills/references/premium-command-center-ui/master-application-prompt.md +79 -0
- package/template/.aioson/skills/references/premium-command-center-ui/operational-ux-playbook.md +253 -0
- package/template/.aioson/skills/references/premium-command-center-ui/quality-validation-checklist.md +82 -0
- package/template/.aioson/skills/references/premium-command-center-ui/visual-system-and-component-patterns.md +270 -0
- package/template/.aioson/skills/static/django-patterns.md +342 -0
- package/template/.aioson/skills/static/fastapi-patterns.md +344 -0
- package/template/.aioson/skills/static/filament-patterns.md +267 -0
- package/template/.aioson/skills/static/flux-ui-components.md +262 -0
- package/template/.aioson/skills/static/git-conventions.md +227 -0
- package/template/.aioson/skills/static/interface-design.md +372 -0
- package/template/.aioson/skills/static/jetstream-setup.md +200 -0
- package/template/.aioson/skills/static/laravel-conventions.md +491 -0
- package/template/.aioson/skills/static/nextjs-patterns.md +321 -0
- package/template/.aioson/skills/static/node-express-patterns.md +317 -0
- package/template/.aioson/skills/static/node-typescript-patterns.md +282 -0
- package/template/.aioson/skills/static/premium-command-center-ui.md +190 -0
- package/template/.aioson/skills/static/rails-conventions.md +307 -0
- package/template/.aioson/skills/static/react-motion-patterns.md +577 -0
- package/template/.aioson/skills/static/static-html-patterns.md +1935 -0
- package/template/.aioson/skills/static/tall-stack-patterns.md +286 -0
- package/template/.aioson/skills/static/ui-ux-modern.md +75 -0
- package/template/.aioson/skills/static/web3-cardano-patterns.md +337 -0
- package/template/.aioson/skills/static/web3-ethereum-patterns.md +310 -0
- package/template/.aioson/skills/static/web3-security-checklist.md +284 -0
- package/template/.aioson/skills/static/web3-solana-patterns.md +324 -0
- package/template/.aioson/squads/.artisan/.gitkeep +0 -0
- package/template/.aioson/squads/.gitkeep +0 -0
- package/template/.aioson/squads/memory.md +5 -0
- package/template/.aioson/tasks/squad-analyze.md +83 -0
- package/template/.aioson/tasks/squad-create.md +99 -0
- package/template/.aioson/tasks/squad-design.md +100 -0
- package/template/.aioson/tasks/squad-export.md +20 -0
- package/template/.aioson/tasks/squad-extend.md +68 -0
- package/template/.aioson/tasks/squad-pipeline.md +122 -0
- package/template/.aioson/tasks/squad-repair.md +85 -0
- package/template/.aioson/tasks/squad-validate.md +58 -0
- package/template/.aioson/templates/squads/content-basic/template.json +21 -0
- package/template/.aioson/templates/squads/media-channel/template.json +24 -0
- package/template/.aioson/templates/squads/research-analysis/template.json +22 -0
- package/template/.aioson/templates/squads/software-delivery/template.json +21 -0
- package/template/.claude/commands/aioson/analyst.md +5 -0
- package/template/.claude/commands/aioson/architect.md +5 -0
- package/template/.claude/commands/aioson/dev.md +5 -0
- package/template/.claude/commands/aioson/orchestrator.md +5 -0
- package/template/.claude/commands/aioson/pm.md +5 -0
- package/template/.claude/commands/aioson/qa.md +5 -0
- package/template/.claude/commands/aioson/setup.md +5 -0
- package/template/.claude/commands/aioson/ux-ui.md +5 -0
- package/template/.gemini/GEMINI.md +10 -0
- package/template/.gemini/commands/aios-analyst.toml +4 -0
- package/template/.gemini/commands/aios-architect.toml +7 -0
- package/template/.gemini/commands/aios-dev.toml +8 -0
- package/template/.gemini/commands/aios-discovery-design-doc.toml +4 -0
- package/template/.gemini/commands/aios-orchestrator.toml +8 -0
- package/template/.gemini/commands/aios-pm.toml +8 -0
- package/template/.gemini/commands/aios-product.toml +4 -0
- package/template/.gemini/commands/aios-qa.toml +6 -0
- package/template/.gemini/commands/aios-setup.toml +3 -0
- package/template/.gemini/commands/aios-ux-ui.toml +8 -0
- package/template/AGENTS.md +67 -0
- package/template/CLAUDE.md +31 -0
- package/template/OPENCODE.md +24 -0
- package/template/aioson-models.json +40 -0
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const {
|
|
6
|
+
resolveRuntimePaths,
|
|
7
|
+
openRuntimeDb,
|
|
8
|
+
runtimeStoreExists,
|
|
9
|
+
startTask,
|
|
10
|
+
updateTask,
|
|
11
|
+
startRun,
|
|
12
|
+
updateRun,
|
|
13
|
+
attachArtifact,
|
|
14
|
+
upsertContentItem,
|
|
15
|
+
getStatusSnapshot,
|
|
16
|
+
logAgentEvent
|
|
17
|
+
} = require('../runtime-store');
|
|
18
|
+
|
|
19
|
+
const ALLOWED_LAYOUTS = new Set(['document', 'tabs', 'accordion', 'stack', 'mixed']);
|
|
20
|
+
const DEFAULT_TEXT_FIELDS = ['content', 'text', 'body', 'lyrics', 'markdown'];
|
|
21
|
+
|
|
22
|
+
function resolveTargetDir(args) {
|
|
23
|
+
return path.resolve(process.cwd(), args[0] || '.');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function requireOption(options, key, t) {
|
|
27
|
+
const value = options[key];
|
|
28
|
+
if (value === undefined || value === null || String(value).trim() === '') {
|
|
29
|
+
throw new Error(t('runtime.option_required', { option: `--${key}` }));
|
|
30
|
+
}
|
|
31
|
+
return String(value).trim();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function readJsonIfExists(filePath) {
|
|
35
|
+
try {
|
|
36
|
+
const raw = await fs.readFile(filePath, 'utf8');
|
|
37
|
+
return JSON.parse(raw);
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function maybeResolveContentPaths(targetDir, outputPath) {
|
|
44
|
+
if (!outputPath) return null;
|
|
45
|
+
|
|
46
|
+
const relative = String(outputPath).replace(/\\/g, '/').trim();
|
|
47
|
+
if (!/\/index\.html?$/i.test(relative)) return null;
|
|
48
|
+
|
|
49
|
+
const absoluteHtmlPath = path.isAbsolute(relative) ? relative : path.join(targetDir, relative);
|
|
50
|
+
const absoluteJsonPath = path.join(path.dirname(absoluteHtmlPath), 'content.json');
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
relativeHtmlPath: path.isAbsolute(relative) ? path.relative(targetDir, absoluteHtmlPath).replace(/\\/g, '/') : relative,
|
|
54
|
+
relativeJsonPath: path.relative(targetDir, absoluteJsonPath).replace(/\\/g, '/'),
|
|
55
|
+
absoluteJsonPath
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function asObject(value) {
|
|
60
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function asString(value) {
|
|
64
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function asArray(value) {
|
|
68
|
+
return Array.isArray(value) ? value : [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeStringArray(value) {
|
|
72
|
+
const values = Array.isArray(value)
|
|
73
|
+
? value
|
|
74
|
+
: typeof value === 'string'
|
|
75
|
+
? value.split(',')
|
|
76
|
+
: [];
|
|
77
|
+
|
|
78
|
+
return Array.from(
|
|
79
|
+
new Set(
|
|
80
|
+
values
|
|
81
|
+
.map((entry) => String(entry || '').trim())
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function parseJsonArray(value) {
|
|
88
|
+
if (!value) return [];
|
|
89
|
+
try {
|
|
90
|
+
return normalizeStringArray(JSON.parse(value));
|
|
91
|
+
} catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function titleize(value) {
|
|
97
|
+
return String(value || '')
|
|
98
|
+
.replace(/\.[^.]+$/, '')
|
|
99
|
+
.replace(/[-_]+/g, ' ')
|
|
100
|
+
.replace(/\s+/g, ' ')
|
|
101
|
+
.trim()
|
|
102
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function makeContentKey(value) {
|
|
106
|
+
return String(value || 'content')
|
|
107
|
+
.toLowerCase()
|
|
108
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
109
|
+
.replace(/^-+|-+$/g, '')
|
|
110
|
+
.slice(0, 80) || `content-${Date.now()}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function truncateText(value, max = 12000) {
|
|
114
|
+
const text = String(value || '').trim();
|
|
115
|
+
if (text.length <= max) return text;
|
|
116
|
+
return `${text.slice(0, max).trim()}\n\n[...]`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function stripHtml(html) {
|
|
120
|
+
return String(html || '')
|
|
121
|
+
.replace(/<script[\s\S]*?<\/script>/gi, ' ')
|
|
122
|
+
.replace(/<style[\s\S]*?<\/style>/gi, ' ')
|
|
123
|
+
.replace(/<[^>]+>/g, ' ')
|
|
124
|
+
.replace(/\s+/g, ' ')
|
|
125
|
+
.trim();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function firstNonEmptyText(record, keys) {
|
|
129
|
+
for (const key of keys) {
|
|
130
|
+
const value = asString(record[key]);
|
|
131
|
+
if (value) return value;
|
|
132
|
+
}
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function normalizeSimpleContentBlocks(content) {
|
|
137
|
+
const text = firstNonEmptyText(content, DEFAULT_TEXT_FIELDS);
|
|
138
|
+
if (text) {
|
|
139
|
+
return [
|
|
140
|
+
{
|
|
141
|
+
type: 'rich-text',
|
|
142
|
+
content: truncateText(text)
|
|
143
|
+
}
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const html = asString(content.html);
|
|
148
|
+
if (html) {
|
|
149
|
+
const preview = stripHtml(html);
|
|
150
|
+
return [
|
|
151
|
+
{
|
|
152
|
+
type: 'callout',
|
|
153
|
+
title: 'Conteudo HTML indexado automaticamente',
|
|
154
|
+
content:
|
|
155
|
+
'Este item foi convertido para o indice de conteudos a partir de um arquivo HTML gerado pelo squad.'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: 'rich-text',
|
|
159
|
+
content: truncateText(preview || 'Nao foi possivel extrair preview textual do HTML.')
|
|
160
|
+
}
|
|
161
|
+
];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function isValidBlock(value) {
|
|
168
|
+
const block = asObject(value);
|
|
169
|
+
if (!block) return false;
|
|
170
|
+
|
|
171
|
+
const type = asString(block.type);
|
|
172
|
+
if (!type) return false;
|
|
173
|
+
|
|
174
|
+
if (type === 'tabs') {
|
|
175
|
+
const items = asArray(block.items);
|
|
176
|
+
return items.every((item) => {
|
|
177
|
+
const tab = asObject(item);
|
|
178
|
+
if (!tab || !asString(tab.label)) return false;
|
|
179
|
+
return asArray(tab.blocks).every(isValidBlock);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (type === 'accordion') {
|
|
184
|
+
const items = asArray(block.items);
|
|
185
|
+
return items.every((item) => {
|
|
186
|
+
const entry = asObject(item);
|
|
187
|
+
if (!entry || !asString(entry.title)) return false;
|
|
188
|
+
const content = asString(entry.content);
|
|
189
|
+
const nestedBlocks = asArray(entry.blocks);
|
|
190
|
+
if (!content && nestedBlocks.length === 0) return false;
|
|
191
|
+
return nestedBlocks.every(isValidBlock);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (type === 'section') {
|
|
196
|
+
return asArray(block.blocks).every(isValidBlock);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function validateContentPayload(payload) {
|
|
203
|
+
const content = asObject(payload);
|
|
204
|
+
if (!content) {
|
|
205
|
+
return { ok: false, reason: 'content.json must be an object' };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const contentKey = asString(content.contentKey || content.content_key);
|
|
209
|
+
if (!contentKey) {
|
|
210
|
+
return { ok: false, reason: 'content.json is missing contentKey' };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const title = asString(content.title);
|
|
214
|
+
if (!title) {
|
|
215
|
+
return { ok: false, reason: 'content.json is missing title' };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const contentType = asString(content.contentType || content.content_type);
|
|
219
|
+
if (!contentType) {
|
|
220
|
+
return { ok: false, reason: 'content.json is missing contentType' };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const layoutType = asString(content.layoutType || content.layout_type || 'document');
|
|
224
|
+
if (!ALLOWED_LAYOUTS.has(layoutType)) {
|
|
225
|
+
return { ok: false, reason: `content.json has unsupported layoutType: ${layoutType}` };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const blocks = asArray(content.blocks);
|
|
229
|
+
const normalizedBlocks = blocks.length > 0 ? blocks : normalizeSimpleContentBlocks(content);
|
|
230
|
+
|
|
231
|
+
if (normalizedBlocks.length === 0) {
|
|
232
|
+
return { ok: false, reason: 'content.json must include blocks or a simple text field' };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!normalizedBlocks.every(isValidBlock)) {
|
|
236
|
+
return { ok: false, reason: 'content.json contains invalid blocks' };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
ok: true,
|
|
241
|
+
normalized: {
|
|
242
|
+
...content,
|
|
243
|
+
contentKey,
|
|
244
|
+
title,
|
|
245
|
+
contentType,
|
|
246
|
+
layoutType,
|
|
247
|
+
blocks: normalizedBlocks,
|
|
248
|
+
blueprint: asString(content.blueprint || content.blueprintSlug || content.blueprint_slug),
|
|
249
|
+
usedSkills: normalizeStringArray(
|
|
250
|
+
content.usedSkills ||
|
|
251
|
+
content.used_skills ||
|
|
252
|
+
content.meta?.usedSkills ||
|
|
253
|
+
content.meta?.used_skills ||
|
|
254
|
+
[]
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function listFilesRecursive(rootDir) {
|
|
261
|
+
const result = [];
|
|
262
|
+
const queue = [rootDir];
|
|
263
|
+
|
|
264
|
+
while (queue.length > 0) {
|
|
265
|
+
const currentDir = queue.shift();
|
|
266
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true }).catch(() => []);
|
|
267
|
+
for (const entry of entries) {
|
|
268
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
269
|
+
if (entry.isDirectory()) {
|
|
270
|
+
queue.push(fullPath);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (entry.isFile()) {
|
|
274
|
+
result.push(fullPath);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function inferSquadSlugFromOutputPath(targetDir, absolutePath) {
|
|
283
|
+
const outputRoot = path.join(targetDir, 'output');
|
|
284
|
+
const relativePath = path.relative(outputRoot, absolutePath).replace(/\\/g, '/');
|
|
285
|
+
const [slug] = relativePath.split('/');
|
|
286
|
+
return slug || '';
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function relativeContentKeyFromOutput(targetDir, absolutePath, squadSlug) {
|
|
290
|
+
const squadRoot = path.join(targetDir, 'output', squadSlug);
|
|
291
|
+
const relativeToSquad = path.relative(squadRoot, absolutePath).replace(/\\/g, '/');
|
|
292
|
+
return makeContentKey(relativeToSquad.replace(/\.[^.]+$/, ''));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function synthesizeContentPayload({ targetDir, absolutePath, squadSlug, rawContent }) {
|
|
296
|
+
const ext = path.extname(absolutePath).toLowerCase();
|
|
297
|
+
const relativePath = path.relative(targetDir, absolutePath).replace(/\\/g, '/');
|
|
298
|
+
const title = titleize(path.basename(absolutePath));
|
|
299
|
+
const contentKey = relativeContentKeyFromOutput(targetDir, absolutePath, squadSlug);
|
|
300
|
+
|
|
301
|
+
if (ext === '.md') {
|
|
302
|
+
return {
|
|
303
|
+
contentKey,
|
|
304
|
+
title,
|
|
305
|
+
contentType: 'text-content',
|
|
306
|
+
layoutType: 'document',
|
|
307
|
+
summary: `Conteudo indexado automaticamente de ${relativePath}.`,
|
|
308
|
+
blocks: [
|
|
309
|
+
{
|
|
310
|
+
type: 'rich-text',
|
|
311
|
+
content: truncateText(rawContent)
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
meta: {
|
|
315
|
+
autoIndexed: true,
|
|
316
|
+
sourceFormat: 'markdown',
|
|
317
|
+
sourcePath: relativePath
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (ext === '.html' || ext === '.htm') {
|
|
323
|
+
const preview = stripHtml(rawContent);
|
|
324
|
+
return {
|
|
325
|
+
contentKey,
|
|
326
|
+
title,
|
|
327
|
+
contentType: 'html-content',
|
|
328
|
+
layoutType: 'document',
|
|
329
|
+
summary: `Conteudo HTML indexado automaticamente de ${relativePath}.`,
|
|
330
|
+
blocks: [
|
|
331
|
+
{
|
|
332
|
+
type: 'callout',
|
|
333
|
+
title: 'Preview indexado automaticamente',
|
|
334
|
+
content: 'O arquivo HTML original continua no output do squad. Este viewer mostra uma versao textual para indexacao e sync.'
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
type: 'rich-text',
|
|
338
|
+
content: truncateText(preview || 'Nao foi possivel gerar preview textual deste HTML.')
|
|
339
|
+
}
|
|
340
|
+
],
|
|
341
|
+
meta: {
|
|
342
|
+
autoIndexed: true,
|
|
343
|
+
sourceFormat: 'html',
|
|
344
|
+
sourcePath: relativePath
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async function resolveIngestCandidates(targetDir, options = {}) {
|
|
353
|
+
const outputRoot = path.join(targetDir, 'output');
|
|
354
|
+
const scopedRoot = options.squad ? path.join(outputRoot, String(options.squad).trim()) : outputRoot;
|
|
355
|
+
const rootExists = await fs.stat(scopedRoot).then((stat) => stat.isDirectory()).catch(() => false);
|
|
356
|
+
|
|
357
|
+
if (!rootExists) {
|
|
358
|
+
return [];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const allFiles = await listFilesRecursive(scopedRoot);
|
|
362
|
+
const contentJsonDirs = new Set(
|
|
363
|
+
allFiles
|
|
364
|
+
.filter((filePath) => path.basename(filePath).toLowerCase() === 'content.json')
|
|
365
|
+
.map((filePath) => path.dirname(filePath))
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
return allFiles.filter((filePath) => {
|
|
369
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
370
|
+
const base = path.basename(filePath).toLowerCase();
|
|
371
|
+
|
|
372
|
+
if (base === 'content.json') return true;
|
|
373
|
+
if (ext !== '.md' && ext !== '.html' && ext !== '.htm') return false;
|
|
374
|
+
if (contentJsonDirs.has(path.dirname(filePath))) return false;
|
|
375
|
+
|
|
376
|
+
return true;
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async function ingestContentCandidate(db, targetDir, absolutePath, options = {}) {
|
|
381
|
+
const baseName = path.basename(absolutePath).toLowerCase();
|
|
382
|
+
const relativePath = path.relative(targetDir, absolutePath).replace(/\\/g, '/');
|
|
383
|
+
const squadSlug = options.squad ? String(options.squad).trim() : inferSquadSlugFromOutputPath(targetDir, absolutePath);
|
|
384
|
+
|
|
385
|
+
if (!squadSlug) {
|
|
386
|
+
return { indexed: false, reason: 'missing_squad' };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (baseName === 'content.json') {
|
|
390
|
+
const payload = await readJsonIfExists(absolutePath);
|
|
391
|
+
const validation = validateContentPayload(payload);
|
|
392
|
+
if (!validation.ok) {
|
|
393
|
+
return { indexed: false, reason: validation.reason };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const content = validation.normalized;
|
|
397
|
+
const siblingIndex = path.join(path.dirname(absolutePath), 'index.html');
|
|
398
|
+
const siblingHtmlExists = await fs.stat(siblingIndex).then((stat) => stat.isFile()).catch(() => false);
|
|
399
|
+
|
|
400
|
+
upsertContentItem(db, {
|
|
401
|
+
contentKey: content.contentKey,
|
|
402
|
+
taskKey: options.task || content.taskKey || content.task_key || null,
|
|
403
|
+
runKey: options.run || content.runKey || content.run_key || null,
|
|
404
|
+
squadSlug,
|
|
405
|
+
sessionKey: options.session || content.sessionKey || content.session_key || null,
|
|
406
|
+
title: content.title,
|
|
407
|
+
contentType: content.contentType,
|
|
408
|
+
layoutType: content.layoutType,
|
|
409
|
+
status: content.status || 'completed',
|
|
410
|
+
summary: content.summary || `Conteudo indexado automaticamente de ${relativePath}.`,
|
|
411
|
+
blueprintSlug: content.blueprint || null,
|
|
412
|
+
usedSkills: normalizeStringArray(options.usedSkills || content.usedSkills),
|
|
413
|
+
payload: content,
|
|
414
|
+
jsonPath: relativePath,
|
|
415
|
+
htmlPath: siblingHtmlExists
|
|
416
|
+
? path.relative(targetDir, siblingIndex).replace(/\\/g, '/')
|
|
417
|
+
: null,
|
|
418
|
+
createdByAgent: options.agent || content.createdByAgent || content.created_by_agent || null
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
return { indexed: true, kind: 'content-json', contentKey: content.contentKey };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const rawContent = await fs.readFile(absolutePath, 'utf8').catch(() => '');
|
|
425
|
+
if (!rawContent.trim()) {
|
|
426
|
+
return { indexed: false, reason: 'empty_file' };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const payload = synthesizeContentPayload({
|
|
430
|
+
targetDir,
|
|
431
|
+
absolutePath,
|
|
432
|
+
squadSlug,
|
|
433
|
+
rawContent
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
if (!payload) {
|
|
437
|
+
return { indexed: false, reason: 'unsupported_file' };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
upsertContentItem(db, {
|
|
441
|
+
contentKey: payload.contentKey,
|
|
442
|
+
taskKey: options.task || null,
|
|
443
|
+
runKey: options.run || null,
|
|
444
|
+
squadSlug,
|
|
445
|
+
sessionKey: options.session || null,
|
|
446
|
+
title: payload.title,
|
|
447
|
+
contentType: payload.contentType,
|
|
448
|
+
layoutType: payload.layoutType,
|
|
449
|
+
status: 'completed',
|
|
450
|
+
summary: payload.summary,
|
|
451
|
+
blueprintSlug: payload.blueprint || null,
|
|
452
|
+
usedSkills: normalizeStringArray(options.usedSkills),
|
|
453
|
+
payload,
|
|
454
|
+
jsonPath: null,
|
|
455
|
+
htmlPath: path.extname(absolutePath).toLowerCase().startsWith('.ht')
|
|
456
|
+
? relativePath
|
|
457
|
+
: null,
|
|
458
|
+
createdByAgent: options.agent || null
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
return { indexed: true, kind: path.extname(absolutePath).toLowerCase(), contentKey: payload.contentKey };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function withRuntimeDb(targetDir, t) {
|
|
465
|
+
const handle = await openRuntimeDb(targetDir, { mustExist: true });
|
|
466
|
+
if (!handle) {
|
|
467
|
+
throw new Error(t('runtime.store_missing', { path: resolveRuntimePaths(targetDir).dbPath }));
|
|
468
|
+
}
|
|
469
|
+
return handle;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function runRuntimeInit({ args, options = {}, logger, t }) {
|
|
473
|
+
const targetDir = resolveTargetDir(args);
|
|
474
|
+
const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
|
|
475
|
+
db.close();
|
|
476
|
+
|
|
477
|
+
if (!options.json) {
|
|
478
|
+
logger.log(t('runtime.init_ok', { path: dbPath }));
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return { ok: true, targetDir, runtimeDir, dbPath };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async function runRuntimeIngest({ args, options = {}, logger, t }) {
|
|
485
|
+
const targetDir = resolveTargetDir(args);
|
|
486
|
+
const { db, dbPath } = await withRuntimeDb(targetDir, t);
|
|
487
|
+
const ingestOptions = {
|
|
488
|
+
...options,
|
|
489
|
+
usedSkills: normalizeStringArray(options['used-skills'] || options.usedSkills)
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
try {
|
|
493
|
+
const candidates = await resolveIngestCandidates(targetDir, ingestOptions);
|
|
494
|
+
let indexed = 0;
|
|
495
|
+
let skipped = 0;
|
|
496
|
+
const reasons = [];
|
|
497
|
+
|
|
498
|
+
for (const candidate of candidates) {
|
|
499
|
+
const result = await ingestContentCandidate(db, targetDir, candidate, ingestOptions);
|
|
500
|
+
if (result.indexed) {
|
|
501
|
+
indexed += 1;
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
skipped += 1;
|
|
505
|
+
if (result.reason) {
|
|
506
|
+
reasons.push(`${path.relative(targetDir, candidate).replace(/\\/g, '/')}: ${result.reason}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (!options.json) {
|
|
511
|
+
logger.log(
|
|
512
|
+
t('runtime.ingest_ok', {
|
|
513
|
+
indexed,
|
|
514
|
+
skipped,
|
|
515
|
+
path: dbPath
|
|
516
|
+
})
|
|
517
|
+
);
|
|
518
|
+
if (reasons.length > 0) {
|
|
519
|
+
for (const reason of reasons.slice(0, 10)) {
|
|
520
|
+
logger.log(`- ${reason}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
ok: true,
|
|
527
|
+
targetDir,
|
|
528
|
+
dbPath,
|
|
529
|
+
indexed,
|
|
530
|
+
skipped,
|
|
531
|
+
reasons
|
|
532
|
+
};
|
|
533
|
+
} finally {
|
|
534
|
+
db.close();
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async function runRuntimeTaskStart({ args, options = {}, logger, t }) {
|
|
539
|
+
const targetDir = resolveTargetDir(args);
|
|
540
|
+
const { db, dbPath } = await withRuntimeDb(targetDir, t);
|
|
541
|
+
|
|
542
|
+
try {
|
|
543
|
+
const taskKey = startTask(db, {
|
|
544
|
+
taskKey: options.task,
|
|
545
|
+
squadSlug: options.squad,
|
|
546
|
+
sessionKey: options.session,
|
|
547
|
+
title: requireOption(options, 'title', t),
|
|
548
|
+
goal: options.goal,
|
|
549
|
+
createdBy: options.by
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
if (!options.json) {
|
|
553
|
+
logger.log(t('runtime.task_start_ok', { task: taskKey, path: dbPath }));
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return {
|
|
557
|
+
ok: true,
|
|
558
|
+
targetDir,
|
|
559
|
+
dbPath,
|
|
560
|
+
taskKey,
|
|
561
|
+
status: 'running'
|
|
562
|
+
};
|
|
563
|
+
} finally {
|
|
564
|
+
db.close();
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async function runRuntimeStart({ args, options = {}, logger, t }) {
|
|
569
|
+
const targetDir = resolveTargetDir(args);
|
|
570
|
+
const { db, dbPath } = await withRuntimeDb(targetDir, t);
|
|
571
|
+
|
|
572
|
+
try {
|
|
573
|
+
const runKey = startRun(db, {
|
|
574
|
+
runKey: options.run,
|
|
575
|
+
taskKey: options.task,
|
|
576
|
+
agentName: requireOption(options, 'agent', t),
|
|
577
|
+
agentKind: options.kind,
|
|
578
|
+
squadSlug: options.squad,
|
|
579
|
+
sessionKey: options.session,
|
|
580
|
+
title: options.title,
|
|
581
|
+
message: options.message,
|
|
582
|
+
summary: options.summary,
|
|
583
|
+
usedSkills: normalizeStringArray(options['used-skills'] || options.usedSkills),
|
|
584
|
+
outputPath: options.output
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
const snapshot = getStatusSnapshot(db);
|
|
588
|
+
if (!options.json) {
|
|
589
|
+
logger.log(t('runtime.start_ok', { run: runKey, path: dbPath }));
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return {
|
|
593
|
+
ok: true,
|
|
594
|
+
targetDir,
|
|
595
|
+
dbPath,
|
|
596
|
+
runKey,
|
|
597
|
+
status: 'running',
|
|
598
|
+
activeCount: snapshot.activeRuns.length
|
|
599
|
+
};
|
|
600
|
+
} finally {
|
|
601
|
+
db.close();
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
async function runRuntimeUpdate({ args, options = {}, logger, t }) {
|
|
606
|
+
const targetDir = resolveTargetDir(args);
|
|
607
|
+
const { db, dbPath } = await withRuntimeDb(targetDir, t);
|
|
608
|
+
|
|
609
|
+
try {
|
|
610
|
+
const runKey = requireOption(options, 'run', t);
|
|
611
|
+
const status = updateRun(db, {
|
|
612
|
+
runKey,
|
|
613
|
+
status: 'running',
|
|
614
|
+
taskKey: options.task,
|
|
615
|
+
eventType: 'progress',
|
|
616
|
+
message: options.message,
|
|
617
|
+
summary: options.summary,
|
|
618
|
+
usedSkills: normalizeStringArray(options['used-skills'] || options.usedSkills),
|
|
619
|
+
outputPath: options.output
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
if (!options.json) {
|
|
623
|
+
logger.log(t('runtime.update_ok', { run: runKey, path: dbPath }));
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return {
|
|
627
|
+
ok: true,
|
|
628
|
+
targetDir,
|
|
629
|
+
dbPath,
|
|
630
|
+
runKey,
|
|
631
|
+
status
|
|
632
|
+
};
|
|
633
|
+
} finally {
|
|
634
|
+
db.close();
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
async function runRuntimeFinish({ args, options = {}, logger, t }) {
|
|
639
|
+
const targetDir = resolveTargetDir(args);
|
|
640
|
+
const { db, dbPath } = await withRuntimeDb(targetDir, t);
|
|
641
|
+
|
|
642
|
+
try {
|
|
643
|
+
const runKey = requireOption(options, 'run', t);
|
|
644
|
+
const status = updateRun(db, {
|
|
645
|
+
runKey,
|
|
646
|
+
status: 'completed',
|
|
647
|
+
taskKey: options.task,
|
|
648
|
+
eventType: 'finish',
|
|
649
|
+
message: options.message || options.summary || 'Run completed',
|
|
650
|
+
summary: options.summary,
|
|
651
|
+
usedSkills: normalizeStringArray(options['used-skills'] || options.usedSkills),
|
|
652
|
+
outputPath: options.output
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
const finishedRun = db
|
|
656
|
+
.prepare('SELECT run_key, task_key, squad_slug, session_key, agent_name, output_path, used_skills_json FROM agent_runs WHERE run_key = ?')
|
|
657
|
+
.get(runKey);
|
|
658
|
+
if (finishedRun && finishedRun.output_path) {
|
|
659
|
+
attachArtifact(db, {
|
|
660
|
+
taskKey: finishedRun.task_key,
|
|
661
|
+
runKey: finishedRun.run_key,
|
|
662
|
+
squadSlug: finishedRun.squad_slug,
|
|
663
|
+
agentName: finishedRun.agent_name,
|
|
664
|
+
filePath: finishedRun.output_path,
|
|
665
|
+
title: options.title || options.summary || 'Artifact generated'
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
const absoluteOutputPath = path.isAbsolute(finishedRun.output_path)
|
|
669
|
+
? finishedRun.output_path
|
|
670
|
+
: path.join(targetDir, finishedRun.output_path);
|
|
671
|
+
const contentPaths = maybeResolveContentPaths(targetDir, finishedRun.output_path);
|
|
672
|
+
const preferredCandidate = contentPaths
|
|
673
|
+
? (await fs
|
|
674
|
+
.stat(contentPaths.absoluteJsonPath)
|
|
675
|
+
.then((stat) => (stat.isFile() ? contentPaths.absoluteJsonPath : null))
|
|
676
|
+
.catch(() => null))
|
|
677
|
+
: absoluteOutputPath;
|
|
678
|
+
|
|
679
|
+
if (preferredCandidate) {
|
|
680
|
+
const ingestion = await ingestContentCandidate(db, targetDir, preferredCandidate, {
|
|
681
|
+
task: finishedRun.task_key,
|
|
682
|
+
run: finishedRun.run_key,
|
|
683
|
+
squad: finishedRun.squad_slug,
|
|
684
|
+
session: options.session || finishedRun.session_key,
|
|
685
|
+
agent: finishedRun.agent_name,
|
|
686
|
+
usedSkills: parseJsonArray(finishedRun.used_skills_json)
|
|
687
|
+
});
|
|
688
|
+
if (!ingestion.indexed && !options.json && logger?.log) {
|
|
689
|
+
logger.log(`[runtime] skipped content indexing for ${finishedRun.run_key}: ${ingestion.reason}`);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (!options.json) {
|
|
695
|
+
logger.log(t('runtime.finish_ok', { run: runKey, path: dbPath }));
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return {
|
|
699
|
+
ok: true,
|
|
700
|
+
targetDir,
|
|
701
|
+
dbPath,
|
|
702
|
+
runKey,
|
|
703
|
+
status
|
|
704
|
+
};
|
|
705
|
+
} finally {
|
|
706
|
+
db.close();
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
async function runRuntimeTaskFinish({ args, options = {}, logger, t }) {
|
|
711
|
+
const targetDir = resolveTargetDir(args);
|
|
712
|
+
const { db, dbPath } = await withRuntimeDb(targetDir, t);
|
|
713
|
+
|
|
714
|
+
try {
|
|
715
|
+
const taskKey = requireOption(options, 'task', t);
|
|
716
|
+
const taskStatus = updateTask(db, {
|
|
717
|
+
taskKey,
|
|
718
|
+
status: 'completed',
|
|
719
|
+
goal: options.goal
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
if (!options.json) {
|
|
723
|
+
logger.log(t('runtime.task_finish_ok', { task: taskKey, path: dbPath }));
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return {
|
|
727
|
+
ok: true,
|
|
728
|
+
targetDir,
|
|
729
|
+
dbPath,
|
|
730
|
+
taskKey,
|
|
731
|
+
status: taskStatus
|
|
732
|
+
};
|
|
733
|
+
} finally {
|
|
734
|
+
db.close();
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
async function runRuntimeFail({ args, options = {}, logger, t }) {
|
|
739
|
+
const targetDir = resolveTargetDir(args);
|
|
740
|
+
const { db, dbPath } = await withRuntimeDb(targetDir, t);
|
|
741
|
+
|
|
742
|
+
try {
|
|
743
|
+
const runKey = requireOption(options, 'run', t);
|
|
744
|
+
const status = updateRun(db, {
|
|
745
|
+
runKey,
|
|
746
|
+
taskKey: options.task,
|
|
747
|
+
status: 'failed',
|
|
748
|
+
eventType: 'fail',
|
|
749
|
+
message: options.message || options.summary || 'Run failed',
|
|
750
|
+
summary: options.summary,
|
|
751
|
+
outputPath: options.output
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
if (!options.json) {
|
|
755
|
+
logger.log(t('runtime.fail_ok', { run: runKey, path: dbPath }));
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
return {
|
|
759
|
+
ok: true,
|
|
760
|
+
targetDir,
|
|
761
|
+
dbPath,
|
|
762
|
+
runKey,
|
|
763
|
+
status
|
|
764
|
+
};
|
|
765
|
+
} finally {
|
|
766
|
+
db.close();
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
async function runRuntimeTaskFail({ args, options = {}, logger, t }) {
|
|
771
|
+
const targetDir = resolveTargetDir(args);
|
|
772
|
+
const { db, dbPath } = await withRuntimeDb(targetDir, t);
|
|
773
|
+
|
|
774
|
+
try {
|
|
775
|
+
const taskKey = requireOption(options, 'task', t);
|
|
776
|
+
const status = updateTask(db, {
|
|
777
|
+
taskKey,
|
|
778
|
+
status: 'failed',
|
|
779
|
+
goal: options.goal
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
if (!options.json) {
|
|
783
|
+
logger.log(t('runtime.task_fail_ok', { task: taskKey, path: dbPath }));
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return {
|
|
787
|
+
ok: true,
|
|
788
|
+
targetDir,
|
|
789
|
+
dbPath,
|
|
790
|
+
taskKey,
|
|
791
|
+
status
|
|
792
|
+
};
|
|
793
|
+
} finally {
|
|
794
|
+
db.close();
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
async function runRuntimeStatus({ args, options = {}, logger, t }) {
|
|
799
|
+
const targetDir = resolveTargetDir(args);
|
|
800
|
+
const { dbPath } = resolveRuntimePaths(targetDir);
|
|
801
|
+
|
|
802
|
+
if (!(await runtimeStoreExists(targetDir))) {
|
|
803
|
+
if (options.json) {
|
|
804
|
+
return { ok: false, error: 'store_missing', dbPath };
|
|
805
|
+
}
|
|
806
|
+
throw new Error(t('runtime.store_missing', { path: dbPath }));
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const { db } = await openRuntimeDb(targetDir, { mustExist: true });
|
|
810
|
+
|
|
811
|
+
try {
|
|
812
|
+
const snapshot = getStatusSnapshot(db);
|
|
813
|
+
const payload = {
|
|
814
|
+
ok: true,
|
|
815
|
+
targetDir,
|
|
816
|
+
dbPath,
|
|
817
|
+
taskCounts: snapshot.taskCounts,
|
|
818
|
+
counts: snapshot.counts,
|
|
819
|
+
activeTasks: snapshot.activeTasks,
|
|
820
|
+
recentTasks: snapshot.recentTasks,
|
|
821
|
+
activeRuns: snapshot.activeRuns,
|
|
822
|
+
recentRuns: snapshot.recentRuns,
|
|
823
|
+
recentArtifacts: snapshot.recentArtifacts,
|
|
824
|
+
recentContentItems: snapshot.recentContentItems,
|
|
825
|
+
recentExecutionEvents: snapshot.recentExecutionEvents
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
if (!options.json) {
|
|
829
|
+
logger.log(t('runtime.status_title', { path: targetDir }));
|
|
830
|
+
logger.log(t('runtime.status_db', { path: dbPath }));
|
|
831
|
+
logger.log(
|
|
832
|
+
t('runtime.status_task_counts', {
|
|
833
|
+
queued: payload.taskCounts.queued,
|
|
834
|
+
running: payload.taskCounts.running,
|
|
835
|
+
completed: payload.taskCounts.completed,
|
|
836
|
+
failed: payload.taskCounts.failed
|
|
837
|
+
})
|
|
838
|
+
);
|
|
839
|
+
logger.log(
|
|
840
|
+
t('runtime.status_counts', {
|
|
841
|
+
queued: payload.counts.queued,
|
|
842
|
+
running: payload.counts.running,
|
|
843
|
+
completed: payload.counts.completed,
|
|
844
|
+
failed: payload.counts.failed
|
|
845
|
+
})
|
|
846
|
+
);
|
|
847
|
+
if (snapshot.activeTasks.length === 0) {
|
|
848
|
+
logger.log(t('runtime.status_no_active_tasks'));
|
|
849
|
+
} else {
|
|
850
|
+
logger.log(t('runtime.status_active_tasks_title'));
|
|
851
|
+
for (const task of snapshot.activeTasks) {
|
|
852
|
+
logger.log(
|
|
853
|
+
t('runtime.status_active_task_line', {
|
|
854
|
+
task: task.task_key,
|
|
855
|
+
squad: task.squad_slug || '—',
|
|
856
|
+
status: task.status,
|
|
857
|
+
title: task.title
|
|
858
|
+
})
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
if (snapshot.activeRuns.length === 0) {
|
|
863
|
+
logger.log(t('runtime.status_no_active'));
|
|
864
|
+
} else {
|
|
865
|
+
logger.log(t('runtime.status_active_title'));
|
|
866
|
+
for (const run of snapshot.activeRuns) {
|
|
867
|
+
logger.log(
|
|
868
|
+
t('runtime.status_active_line', {
|
|
869
|
+
agent: run.agent_name,
|
|
870
|
+
squad: run.squad_slug || '—',
|
|
871
|
+
status: run.status,
|
|
872
|
+
title: run.title || run.summary || '—'
|
|
873
|
+
})
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return payload;
|
|
880
|
+
} finally {
|
|
881
|
+
db.close();
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* aioson runtime-log --agent=<name> --message=<text> [--type=<event>] [--finish] [--status=completed|failed] [--summary=<text>] [--title=<task-title>]
|
|
887
|
+
*
|
|
888
|
+
* Stateful single-command logger for official AIOSON agents.
|
|
889
|
+
* First call creates task + run in SQLite; subsequent calls add events.
|
|
890
|
+
* --finish closes the run and clears the session.
|
|
891
|
+
*/
|
|
892
|
+
async function runRuntimeLog({ args, options = {}, logger, t }) {
|
|
893
|
+
const targetDir = resolveTargetDir(args);
|
|
894
|
+
const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
|
|
895
|
+
|
|
896
|
+
try {
|
|
897
|
+
const agentName = options.agent;
|
|
898
|
+
if (!agentName) {
|
|
899
|
+
throw new Error(t('runtime.log_agent_required'));
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
const { runKey, taskKey } = await logAgentEvent(db, runtimeDir, {
|
|
903
|
+
agentName,
|
|
904
|
+
squadSlug: options.squad || null,
|
|
905
|
+
message: options.message || '',
|
|
906
|
+
type: options.type || 'status',
|
|
907
|
+
taskTitle: options.title,
|
|
908
|
+
finish: Boolean(options.finish),
|
|
909
|
+
status: options.status,
|
|
910
|
+
summary: options.summary,
|
|
911
|
+
meta: options.meta ? (() => { try { return JSON.parse(options.meta); } catch { return { raw: options.meta }; } })() : undefined
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
if (!options.json) {
|
|
915
|
+
const isFinish = Boolean(options.finish);
|
|
916
|
+
logger.log(isFinish
|
|
917
|
+
? t('runtime.log_finish_ok', { agent: agentName, run: runKey, path: dbPath })
|
|
918
|
+
: t('runtime.log_ok', { agent: agentName, run: runKey, path: dbPath })
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
return {
|
|
923
|
+
ok: true,
|
|
924
|
+
targetDir,
|
|
925
|
+
dbPath,
|
|
926
|
+
runKey,
|
|
927
|
+
taskKey,
|
|
928
|
+
agent: agentName,
|
|
929
|
+
finished: Boolean(options.finish)
|
|
930
|
+
};
|
|
931
|
+
} finally {
|
|
932
|
+
db.close();
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
module.exports = {
|
|
937
|
+
runRuntimeInit,
|
|
938
|
+
runRuntimeIngest,
|
|
939
|
+
runRuntimeTaskStart,
|
|
940
|
+
runRuntimeStart,
|
|
941
|
+
runRuntimeUpdate,
|
|
942
|
+
runRuntimeTaskFinish,
|
|
943
|
+
runRuntimeFinish,
|
|
944
|
+
runRuntimeTaskFail,
|
|
945
|
+
runRuntimeFail,
|
|
946
|
+
runRuntimeStatus,
|
|
947
|
+
runRuntimeLog
|
|
948
|
+
};
|