oxe-cc 0.6.6 → 0.7.1
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/.cursor/commands/oxe-capabilities.md +11 -0
- package/.cursor/commands/oxe-dashboard.md +11 -0
- package/.github/copilot-instructions.md +13 -1
- package/.github/prompts/oxe-capabilities.prompt.md +12 -0
- package/.github/prompts/oxe-dashboard.prompt.md +12 -0
- package/CHANGELOG.md +33 -0
- package/README.md +147 -11
- package/assets/oxe-framework-artifacts-paper.png +0 -0
- package/bin/banner.txt +1 -1
- package/bin/lib/oxe-azure.cjs +1445 -0
- package/bin/lib/oxe-dashboard.cjs +588 -0
- package/bin/lib/oxe-install-resolve.cjs +4 -1
- package/bin/lib/oxe-operational.cjs +670 -0
- package/bin/lib/oxe-project-health.cjs +372 -28
- package/bin/oxe-cc.js +1517 -312
- package/commands/oxe/capabilities.md +13 -0
- package/commands/oxe/dashboard.md +14 -0
- package/lib/sdk/README.md +9 -7
- package/lib/sdk/index.cjs +56 -0
- package/lib/sdk/index.d.ts +73 -0
- package/oxe/templates/ACTIVE-RUN.template.json +32 -0
- package/oxe/templates/CAPABILITIES.template.md +7 -0
- package/oxe/templates/CAPABILITY.template.md +45 -0
- package/oxe/templates/CHECKPOINTS.template.md +7 -0
- package/oxe/templates/EXECUTION-RUNTIME.template.md +68 -0
- package/oxe/templates/INVESTIGATION.template.md +38 -0
- package/oxe/templates/NOTES.template.md +16 -0
- package/oxe/templates/PLAN-REVIEW.template.md +31 -0
- package/oxe/templates/RESEARCH.template.md +11 -4
- package/oxe/templates/SPEC.template.md +6 -4
- package/oxe/templates/STATE.md +45 -7
- package/oxe/templates/config.template.json +11 -3
- package/oxe/workflows/ask.md +10 -1
- package/oxe/workflows/capabilities.md +23 -0
- package/oxe/workflows/dashboard.md +23 -0
- package/oxe/workflows/discuss.md +11 -9
- package/oxe/workflows/execute.md +57 -35
- package/oxe/workflows/help.md +256 -225
- package/oxe/workflows/obs.md +70 -20
- package/oxe/workflows/plan.md +83 -74
- package/oxe/workflows/quick.md +16 -11
- package/oxe/workflows/references/adaptive-discovery.md +27 -0
- package/oxe/workflows/research.md +12 -8
- package/oxe/workflows/retro.md +30 -5
- package/oxe/workflows/scan.md +1 -0
- package/oxe/workflows/spec.md +65 -48
- package/oxe/workflows/verify.md +52 -37
- package/package.json +2 -2
package/bin/oxe-cc.js
CHANGED
|
@@ -18,6 +18,9 @@ const oxeAgentInstall = require(path.join(__dirname, 'lib', 'oxe-agent-install.c
|
|
|
18
18
|
const oxeWorkflows = require(path.join(__dirname, 'lib', 'oxe-workflows.cjs'));
|
|
19
19
|
const oxeInstallResolve = require(path.join(__dirname, 'lib', 'oxe-install-resolve.cjs'));
|
|
20
20
|
const oxeNpmVersion = require(path.join(__dirname, 'lib', 'oxe-npm-version.cjs'));
|
|
21
|
+
const oxeDashboard = require(path.join(__dirname, 'lib', 'oxe-dashboard.cjs'));
|
|
22
|
+
const oxeOperational = require(path.join(__dirname, 'lib', 'oxe-operational.cjs'));
|
|
23
|
+
const oxeAzure = require(path.join(__dirname, 'lib', 'oxe-azure.cjs'));
|
|
21
24
|
|
|
22
25
|
/** Merge markers for ~/.copilot/copilot-instructions.md (bloco OXE). */
|
|
23
26
|
const OXE_INST_BEGIN = '<!-- oxe-cc:install-begin -->';
|
|
@@ -254,13 +257,13 @@ function buildUninstallFooter(u) {
|
|
|
254
257
|
`${p}${rm} extensões multi-agente marcadas oxe-cc (OpenCode, Gemini TOML, Windsurf workflows, Codex prompts/skills, Antigravity), se existirem.`
|
|
255
258
|
);
|
|
256
259
|
}
|
|
257
|
-
if (u.ideLocal) {
|
|
258
|
-
bullets.push(
|
|
259
|
-
`${p}${rm} integrações OXE no repositório (.cursor, .github, .claude, .copilot, .opencode, … conforme flags).`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
if (u.globalCli) bullets.push(`${p}${rm} também o pacote npm global oxe-cc do PATH.`);
|
|
263
|
-
if (!u.noProject) {
|
|
260
|
+
if (u.ideLocal) {
|
|
261
|
+
bullets.push(
|
|
262
|
+
`${p}${rm} integrações OXE no repositório (.cursor, .github, .claude, .copilot, .opencode, … conforme flags).`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
if (u.globalCli) bullets.push(`${p}${rm} também o pacote npm global oxe-cc do PATH.`);
|
|
266
|
+
if (!u.noProject) {
|
|
264
267
|
bullets.push(
|
|
265
268
|
`${p}${u.dryRun ? 'Seriam removidas' : 'Removidas'} no repositório: .oxe/workflows, .oxe/templates, oxe/ e commands/oxe (o que existir).`
|
|
266
269
|
);
|
|
@@ -329,6 +332,8 @@ function parseInstallArgs(argv) {
|
|
|
329
332
|
jsonOutput: false,
|
|
330
333
|
/** Lembretes agregados scan/compact em `status`. */
|
|
331
334
|
statusHints: false,
|
|
335
|
+
/** Visão extendida CLI-first: coverage matrix + readiness gate no terminal. */
|
|
336
|
+
statusFull: false,
|
|
332
337
|
restPositional: [],
|
|
333
338
|
};
|
|
334
339
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -373,6 +378,7 @@ function parseInstallArgs(argv) {
|
|
|
373
378
|
out.dir = path.resolve(argv[++i]);
|
|
374
379
|
} else if (a === '--json') out.jsonOutput = true;
|
|
375
380
|
else if (a === '--hints') out.statusHints = true;
|
|
381
|
+
else if (a === '--full') out.statusFull = true;
|
|
376
382
|
else if (!a.startsWith('-')) out.restPositional.push(a);
|
|
377
383
|
else {
|
|
378
384
|
out.parseError = true;
|
|
@@ -1015,6 +1021,9 @@ function ensureGitignoreIgnoresOxeDir(projectRoot, opts = {}) {
|
|
|
1015
1021
|
function bootstrapOxe(target, opts) {
|
|
1016
1022
|
const oxeDir = path.join(target, '.oxe');
|
|
1017
1023
|
const codebaseDir = path.join(oxeDir, 'codebase');
|
|
1024
|
+
const capabilitiesDir = path.join(oxeDir, 'capabilities');
|
|
1025
|
+
const investigationsDir = path.join(oxeDir, 'investigations');
|
|
1026
|
+
const dashboardDir = path.join(oxeDir, 'dashboard');
|
|
1018
1027
|
const stateSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'STATE.md');
|
|
1019
1028
|
const stateDest = path.join(oxeDir, 'STATE.md');
|
|
1020
1029
|
const configSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'config.template.json');
|
|
@@ -1026,12 +1035,15 @@ function bootstrapOxe(target, opts) {
|
|
|
1026
1035
|
}
|
|
1027
1036
|
|
|
1028
1037
|
if (opts.dryRun) {
|
|
1029
|
-
console.log(`${dim}init${reset} ${oxeDir}/ (STATE.md, config.json, codebase
|
|
1038
|
+
console.log(`${dim}init${reset} ${oxeDir}/ (STATE.md, config.json, codebase/, capabilities/, investigations/, dashboard/, runs/, OXE-EVENTS.ndjson, ACTIVE-RUN.json)`);
|
|
1030
1039
|
ensureGitignoreIgnoresOxeDir(target, { dryRun: true });
|
|
1031
1040
|
return;
|
|
1032
1041
|
}
|
|
1033
1042
|
|
|
1034
1043
|
ensureDir(codebaseDir);
|
|
1044
|
+
ensureDir(capabilitiesDir);
|
|
1045
|
+
ensureDir(investigationsDir);
|
|
1046
|
+
ensureDir(dashboardDir);
|
|
1035
1047
|
|
|
1036
1048
|
if (!fs.existsSync(stateDest) || opts.force) {
|
|
1037
1049
|
copyFile(stateSrc, stateDest, { dryRun: false });
|
|
@@ -1050,9 +1062,9 @@ function bootstrapOxe(target, opts) {
|
|
|
1050
1062
|
}
|
|
1051
1063
|
|
|
1052
1064
|
// Criar estruturas opcionais: plugins/, workstreams/, memory/
|
|
1053
|
-
const pluginsDir = path.join(oxeDir, 'plugins');
|
|
1054
|
-
if (!fs.existsSync(pluginsDir)) {
|
|
1055
|
-
ensureDir(pluginsDir);
|
|
1065
|
+
const pluginsDir = path.join(oxeDir, 'plugins');
|
|
1066
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
1067
|
+
ensureDir(pluginsDir);
|
|
1056
1068
|
const pluginsReadme = path.join(PKG_ROOT, 'oxe', 'templates', 'PLUGINS.md');
|
|
1057
1069
|
if (fs.existsSync(pluginsReadme)) {
|
|
1058
1070
|
const destPluginsReadme = path.join(pluginsDir, 'README.md');
|
|
@@ -1063,48 +1075,112 @@ function bootstrapOxe(target, opts) {
|
|
|
1063
1075
|
}
|
|
1064
1076
|
}
|
|
1065
1077
|
|
|
1066
|
-
const workstreamsDir = path.join(oxeDir, 'workstreams');
|
|
1067
|
-
if (!fs.existsSync(workstreamsDir)) {
|
|
1068
|
-
ensureDir(workstreamsDir);
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
const sessionsDir = path.join(oxeDir, 'sessions');
|
|
1072
|
-
if (!fs.existsSync(sessionsDir)) {
|
|
1073
|
-
ensureDir(sessionsDir);
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
const globalDir = path.join(oxeDir, 'global');
|
|
1077
|
-
if (!fs.existsSync(globalDir)) {
|
|
1078
|
-
ensureDir(globalDir);
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
const globalMilestonesDir = path.join(globalDir, 'milestones');
|
|
1082
|
-
if (!fs.existsSync(globalMilestonesDir)) {
|
|
1083
|
-
ensureDir(globalMilestonesDir);
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
const lessonsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'LESSONS.template.md');
|
|
1087
|
-
const lessonsDest = path.join(globalDir, 'LESSONS.md');
|
|
1088
|
-
if (fs.existsSync(lessonsSrc) && !fs.existsSync(lessonsDest)) {
|
|
1089
|
-
copyFile(lessonsSrc, lessonsDest, { dryRun: false });
|
|
1090
|
-
console.log(`${green}init${reset} ${lessonsDest}`);
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
const milestonesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'MILESTONES.template.md');
|
|
1094
|
-
const milestonesDest = path.join(globalDir, 'MILESTONES.md');
|
|
1095
|
-
if (fs.existsSync(milestonesSrc) && !fs.existsSync(milestonesDest)) {
|
|
1096
|
-
copyFile(milestonesSrc, milestonesDest, { dryRun: false });
|
|
1097
|
-
console.log(`${green}init${reset} ${milestonesDest}`);
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
const memoryDir = path.join(oxeDir, 'memory');
|
|
1101
|
-
if (!fs.existsSync(memoryDir)) {
|
|
1102
|
-
ensureDir(memoryDir);
|
|
1103
|
-
}
|
|
1078
|
+
const workstreamsDir = path.join(oxeDir, 'workstreams');
|
|
1079
|
+
if (!fs.existsSync(workstreamsDir)) {
|
|
1080
|
+
ensureDir(workstreamsDir);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
const sessionsDir = path.join(oxeDir, 'sessions');
|
|
1084
|
+
if (!fs.existsSync(sessionsDir)) {
|
|
1085
|
+
ensureDir(sessionsDir);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
const globalDir = path.join(oxeDir, 'global');
|
|
1089
|
+
if (!fs.existsSync(globalDir)) {
|
|
1090
|
+
ensureDir(globalDir);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const globalMilestonesDir = path.join(globalDir, 'milestones');
|
|
1094
|
+
if (!fs.existsSync(globalMilestonesDir)) {
|
|
1095
|
+
ensureDir(globalMilestonesDir);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const lessonsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'LESSONS.template.md');
|
|
1099
|
+
const lessonsDest = path.join(globalDir, 'LESSONS.md');
|
|
1100
|
+
if (fs.existsSync(lessonsSrc) && !fs.existsSync(lessonsDest)) {
|
|
1101
|
+
copyFile(lessonsSrc, lessonsDest, { dryRun: false });
|
|
1102
|
+
console.log(`${green}init${reset} ${lessonsDest}`);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
const milestonesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'MILESTONES.template.md');
|
|
1106
|
+
const milestonesDest = path.join(globalDir, 'MILESTONES.md');
|
|
1107
|
+
if (fs.existsSync(milestonesSrc) && !fs.existsSync(milestonesDest)) {
|
|
1108
|
+
copyFile(milestonesSrc, milestonesDest, { dryRun: false });
|
|
1109
|
+
console.log(`${green}init${reset} ${milestonesDest}`);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
const memoryDir = path.join(oxeDir, 'memory');
|
|
1113
|
+
if (!fs.existsSync(memoryDir)) {
|
|
1114
|
+
ensureDir(memoryDir);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
const runsDir = path.join(oxeDir, 'runs');
|
|
1118
|
+
if (!fs.existsSync(runsDir)) {
|
|
1119
|
+
ensureDir(runsDir);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
const runtimeSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'EXECUTION-RUNTIME.template.md');
|
|
1123
|
+
const runtimeDest = path.join(oxeDir, 'EXECUTION-RUNTIME.md');
|
|
1124
|
+
if (fs.existsSync(runtimeSrc) && !fs.existsSync(runtimeDest)) {
|
|
1125
|
+
copyFile(runtimeSrc, runtimeDest, { dryRun: false });
|
|
1126
|
+
console.log(`${green}init${reset} ${runtimeDest}`);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
const activeRunSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'ACTIVE-RUN.template.json');
|
|
1130
|
+
const activeRunDest = path.join(oxeDir, 'ACTIVE-RUN.json');
|
|
1131
|
+
if (fs.existsSync(activeRunSrc) && !fs.existsSync(activeRunDest)) {
|
|
1132
|
+
copyFile(activeRunSrc, activeRunDest, { dryRun: false });
|
|
1133
|
+
console.log(`${green}init${reset} ${activeRunDest}`);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
const eventsDest = path.join(oxeDir, 'OXE-EVENTS.ndjson');
|
|
1137
|
+
if (!fs.existsSync(eventsDest)) {
|
|
1138
|
+
fs.writeFileSync(eventsDest, '', 'utf8');
|
|
1139
|
+
console.log(`${green}init${reset} ${eventsDest}`);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
const checkpointsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'CHECKPOINTS.template.md');
|
|
1143
|
+
const checkpointsDest = path.join(oxeDir, 'CHECKPOINTS.md');
|
|
1144
|
+
if (fs.existsSync(checkpointsSrc) && !fs.existsSync(checkpointsDest)) {
|
|
1145
|
+
copyFile(checkpointsSrc, checkpointsDest, { dryRun: false });
|
|
1146
|
+
console.log(`${green}init${reset} ${checkpointsDest}`);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
const capabilitiesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'CAPABILITIES.template.md');
|
|
1150
|
+
const capabilitiesDest = path.join(oxeDir, 'CAPABILITIES.md');
|
|
1151
|
+
if (fs.existsSync(capabilitiesSrc) && !fs.existsSync(capabilitiesDest)) {
|
|
1152
|
+
copyFile(capabilitiesSrc, capabilitiesDest, { dryRun: false });
|
|
1153
|
+
console.log(`${green}init${reset} ${capabilitiesDest}`);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
const investigationsIndexDest = path.join(oxeDir, 'INVESTIGATIONS.md');
|
|
1157
|
+
if (!fs.existsSync(investigationsIndexDest)) {
|
|
1158
|
+
fs.writeFileSync(
|
|
1159
|
+
investigationsIndexDest,
|
|
1160
|
+
'# OXE — Investigações\n\n| Data | Ficheiro | Objetivo | Modo | Estado |\n|------|----------|----------|------|--------|\n',
|
|
1161
|
+
'utf8'
|
|
1162
|
+
);
|
|
1163
|
+
console.log(`${green}init${reset} ${investigationsIndexDest}`);
|
|
1164
|
+
}
|
|
1104
1165
|
|
|
1105
1166
|
ensureGitignoreIgnoresOxeDir(target, { dryRun: false });
|
|
1106
1167
|
}
|
|
1107
1168
|
|
|
1169
|
+
/**
|
|
1170
|
+
* @param {string} url
|
|
1171
|
+
*/
|
|
1172
|
+
function openUrlInBrowser(url) {
|
|
1173
|
+
if (process.platform === 'win32') {
|
|
1174
|
+
spawnSync('cmd', ['/c', 'start', '', url], { stdio: 'ignore', detached: true, shell: false });
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
if (process.platform === 'darwin') {
|
|
1178
|
+
spawnSync('open', [url], { stdio: 'ignore', detached: true, shell: false });
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
spawnSync('xdg-open', [url], { stdio: 'ignore', detached: true, shell: false });
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1108
1184
|
/**
|
|
1109
1185
|
* Lembretes de rotina (scan/compact antigos) para `status --hints` ou JSON.
|
|
1110
1186
|
* @param {string} target
|
|
@@ -1144,13 +1220,13 @@ function collectOxeRoutineHints(target, r, config) {
|
|
|
1144
1220
|
* @param {boolean} c
|
|
1145
1221
|
* @param {{ skipScanCompactAgeWarnings?: boolean }} [diagOpts]
|
|
1146
1222
|
*/
|
|
1147
|
-
function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
1148
|
-
const skipAge = Boolean(diagOpts.skipScanCompactAgeWarnings);
|
|
1149
|
-
const r = oxeHealth.buildHealthReport(target);
|
|
1150
|
-
const { config } = oxeHealth.loadOxeConfigMerged(target);
|
|
1151
|
-
|
|
1152
|
-
console.log(`\n ${c ? cyan : ''}▸ Coerência .oxe/ e config${reset}`);
|
|
1153
|
-
console.log(` ${c ? dim : ''}Saúde lógica:${c ? reset : ''} ${r.healthStatus}`);
|
|
1223
|
+
function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
1224
|
+
const skipAge = Boolean(diagOpts.skipScanCompactAgeWarnings);
|
|
1225
|
+
const r = oxeHealth.buildHealthReport(target);
|
|
1226
|
+
const { config } = oxeHealth.loadOxeConfigMerged(target);
|
|
1227
|
+
|
|
1228
|
+
console.log(`\n ${c ? cyan : ''}▸ Coerência .oxe/ e config${reset}`);
|
|
1229
|
+
console.log(` ${c ? dim : ''}Saúde lógica:${c ? reset : ''} ${r.healthStatus}`);
|
|
1154
1230
|
|
|
1155
1231
|
if (r.configParseError) {
|
|
1156
1232
|
console.log(` ${red}FALHA${reset} config.json: ${r.configParseError}`);
|
|
@@ -1166,18 +1242,37 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
|
1166
1242
|
);
|
|
1167
1243
|
}
|
|
1168
1244
|
|
|
1169
|
-
if (r.phase) {
|
|
1170
|
-
console.log(` ${c ? dim : ''}Fase (STATE.md):${c ? reset : ''} ${r.phase}`);
|
|
1171
|
-
}
|
|
1172
|
-
if (r.activeSession) {
|
|
1173
|
-
console.log(` ${c ? dim : ''}Sessão ativa:${c ? reset : ''} ${r.activeSession}`);
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1245
|
+
if (r.phase) {
|
|
1246
|
+
console.log(` ${c ? dim : ''}Fase (STATE.md):${c ? reset : ''} ${r.phase}`);
|
|
1247
|
+
}
|
|
1248
|
+
if (r.activeSession) {
|
|
1249
|
+
console.log(` ${c ? dim : ''}Sessão ativa:${c ? reset : ''} ${r.activeSession}`);
|
|
1250
|
+
}
|
|
1251
|
+
if (r.planReviewStatus) {
|
|
1252
|
+
console.log(` ${c ? dim : ''}Revisão do plano:${c ? reset : ''} ${r.planReviewStatus}`);
|
|
1253
|
+
}
|
|
1254
|
+
if (r.activeRun && r.activeRun.run_id) {
|
|
1255
|
+
console.log(` ${c ? dim : ''}Run ativo:${c ? reset : ''} ${r.activeRun.run_id} (${r.activeRun.status || 'planned'})`);
|
|
1256
|
+
}
|
|
1257
|
+
if (r.eventsSummary) {
|
|
1258
|
+
console.log(` ${c ? dim : ''}Tracing:${c ? reset : ''} ${r.eventsSummary.total} evento(s)`);
|
|
1259
|
+
}
|
|
1260
|
+
if (r.planSelfEvaluation && r.planSelfEvaluation.hasSection) {
|
|
1261
|
+
const best = r.planSelfEvaluation.bestPlan || '—';
|
|
1262
|
+
const conf =
|
|
1263
|
+
typeof r.planSelfEvaluation.confidence === 'number' ? `${r.planSelfEvaluation.confidence}%` : '—';
|
|
1264
|
+
console.log(` ${c ? dim : ''}Plano (autoavaliação):${c ? reset : ''} melhor=${best} | confiança=${conf}`);
|
|
1265
|
+
}
|
|
1266
|
+
if (r.azureActive && r.azure) {
|
|
1267
|
+
console.log(` ${c ? dim : ''}Azure:${c ? reset : ''} ${r.azure.authStatus && r.azure.authStatus.login_active ? 'login ativo' : 'sem login'} | subscription=${r.azure.profile && (r.azure.profile.subscription_name || r.azure.profile.subscription_id) || '—'}`);
|
|
1268
|
+
console.log(` ${c ? dim : ''}Azure inventory:${c ? reset : ''} total=${r.azure.inventorySummary ? r.azure.inventorySummary.total : 0} | pendências=${r.azure.pendingOperations || 0}`);
|
|
1269
|
+
if (r.azure.inventoryStale && r.azure.inventoryStale.stale) {
|
|
1270
|
+
console.log(` ${yellow}AVISO${reset} Inventário Azure stale — rode ${cyan}npx oxe-cc azure sync${reset}`);
|
|
1271
|
+
}
|
|
1272
|
+
for (const warning of r.azure.warnings || []) {
|
|
1273
|
+
console.log(` ${yellow}AVISO${reset} ${warning}`);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1181
1276
|
|
|
1182
1277
|
if (!skipAge) {
|
|
1183
1278
|
if (config.scan_max_age_days > 0 && r.scanDate && r.stale.stale) {
|
|
@@ -1212,18 +1307,30 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
|
1212
1307
|
console.log(` ${c ? dim : ''}Scan (ignorar):${c ? reset : ''} ${config.scan_ignore_globs.join(', ')}`);
|
|
1213
1308
|
}
|
|
1214
1309
|
|
|
1215
|
-
for (const w of r.phaseWarn) {
|
|
1216
|
-
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1217
|
-
}
|
|
1218
|
-
for (const w of r.
|
|
1219
|
-
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1220
|
-
}
|
|
1221
|
-
for (const w of r.
|
|
1222
|
-
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
console.log(` ${yellow}AVISO${reset} ${
|
|
1226
|
-
}
|
|
1310
|
+
for (const w of r.phaseWarn) {
|
|
1311
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1312
|
+
}
|
|
1313
|
+
for (const w of r.runtimeWarn) {
|
|
1314
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1315
|
+
}
|
|
1316
|
+
for (const w of r.reviewWarn) {
|
|
1317
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1318
|
+
}
|
|
1319
|
+
for (const w of r.capabilityWarn) {
|
|
1320
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1321
|
+
}
|
|
1322
|
+
for (const w of r.investigationWarn) {
|
|
1323
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1324
|
+
}
|
|
1325
|
+
for (const w of r.sessionWarn) {
|
|
1326
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1327
|
+
}
|
|
1328
|
+
for (const w of r.installWarn) {
|
|
1329
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1330
|
+
}
|
|
1331
|
+
if (r.summaryGapWarn) {
|
|
1332
|
+
console.log(` ${yellow}AVISO${reset} ${r.summaryGapWarn}`);
|
|
1333
|
+
}
|
|
1227
1334
|
for (const w of r.specWarn) {
|
|
1228
1335
|
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1229
1336
|
}
|
|
@@ -1232,15 +1339,100 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
|
1232
1339
|
}
|
|
1233
1340
|
}
|
|
1234
1341
|
|
|
1342
|
+
/**
|
|
1343
|
+
* Imprime uma célula de coverage com cor ANSI.
|
|
1344
|
+
* @param {boolean} exists
|
|
1345
|
+
* @param {string} label
|
|
1346
|
+
*/
|
|
1347
|
+
function coverageCell(exists, label) {
|
|
1348
|
+
const c = useAnsiColors();
|
|
1349
|
+
return exists ? `${c ? green : ''}✓ ${label}${c ? reset : ''}` : `${c ? dim : ''}✗ ${label}${c ? reset : ''}`;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
/**
|
|
1353
|
+
* Visão CLI-first: health + coverage matrix + readiness gate no terminal.
|
|
1354
|
+
* @param {string} target
|
|
1355
|
+
*/
|
|
1356
|
+
function runStatusFull(target) {
|
|
1357
|
+
const c = useAnsiColors();
|
|
1358
|
+
const report = oxeHealth.buildHealthReport(target);
|
|
1359
|
+
const p = oxeHealth.oxePaths(target);
|
|
1360
|
+
const activeSession = report.activeSession || null;
|
|
1361
|
+
let sp = p;
|
|
1362
|
+
if (activeSession) {
|
|
1363
|
+
sp = oxeHealth.scopedOxePaths(target, activeSession);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
printSection('OXE ▸ status --full');
|
|
1367
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${target}${c ? reset : ''}`);
|
|
1368
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
1369
|
+
console.log(` ${c ? green : ''}Fase:${c ? reset : ''} ${report.phase || '—'}`);
|
|
1370
|
+
|
|
1371
|
+
const healthColor = report.healthStatus === 'healthy' ? green : report.healthStatus === 'warning' ? yellow : red;
|
|
1372
|
+
console.log(` ${c ? green : ''}Saúde:${c ? reset : ''} ${c ? healthColor : ''}${report.healthStatus}${c ? reset : ''}`);
|
|
1373
|
+
|
|
1374
|
+
// Coverage matrix
|
|
1375
|
+
const specPath = activeSession && sp.spec ? sp.spec : p.spec;
|
|
1376
|
+
const planPath = activeSession && sp.plan ? sp.plan : p.plan;
|
|
1377
|
+
const verifyPath = activeSession && sp.verify ? sp.verify : p.verify;
|
|
1378
|
+
const specExists = fs.existsSync(specPath);
|
|
1379
|
+
const planExists = fs.existsSync(planPath);
|
|
1380
|
+
const verifyExists = fs.existsSync(verifyPath);
|
|
1381
|
+
const lessonsExists = fs.existsSync(p.globalLessons || p.lessons);
|
|
1382
|
+
const codebaseExists = fs.existsSync(p.codebase);
|
|
1383
|
+
|
|
1384
|
+
console.log(`\n ${c ? yellow : ''}Coverage matrix${c ? reset : ''}`);
|
|
1385
|
+
console.log(` ${coverageCell(codebaseExists, 'codebase scan')} ${coverageCell(specExists, 'SPEC.md')} ${coverageCell(planExists, 'PLAN.md')} ${coverageCell(verifyExists, 'VERIFY.md')} ${coverageCell(lessonsExists, 'LESSONS.md')}`);
|
|
1386
|
+
|
|
1387
|
+
// Readiness gate
|
|
1388
|
+
const ready = specExists && planExists && !report.planWarn.length && !report.runtimeWarn.length;
|
|
1389
|
+
const gateColor = ready ? green : yellow;
|
|
1390
|
+
console.log(`\n ${c ? yellow : ''}Readiness gate${c ? reset : ''}`);
|
|
1391
|
+
console.log(` ${c ? gateColor : ''}${ready ? '✓ Pronto para executar' : '✗ Não pronto para executar'}${c ? reset : ''}`);
|
|
1392
|
+
if (!specExists) console.log(` ${c ? dim : ''} • SPEC.md ausente — rode /oxe-spec${c ? reset : ''}`);
|
|
1393
|
+
if (!planExists) console.log(` ${c ? dim : ''} • PLAN.md ausente — rode /oxe-plan${c ? reset : ''}`);
|
|
1394
|
+
if (report.planWarn.length) {
|
|
1395
|
+
for (const w of report.planWarn) {
|
|
1396
|
+
console.log(` ${c ? yellow : ''} • ${w}${c ? reset : ''}`);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// Active run summary
|
|
1401
|
+
if (report.activeRun) {
|
|
1402
|
+
const ar = report.activeRun;
|
|
1403
|
+
console.log(`\n ${c ? yellow : ''}Active run${c ? reset : ''}`);
|
|
1404
|
+
console.log(` ${c ? dim : ''}Run:${c ? reset : ''} ${ar.run_id || '—'} ${c ? dim : ''}Estado:${c ? reset : ''} ${ar.status || '—'} ${c ? dim : ''}Onda:${c ? reset : ''} ${ar.current_wave != null ? ar.current_wave : '—'}`);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// Plan self-evaluation
|
|
1408
|
+
if (report.planSelfEvaluation) {
|
|
1409
|
+
const pse = report.planSelfEvaluation;
|
|
1410
|
+
console.log(`\n ${c ? yellow : ''}Autoavaliação do plano${c ? reset : ''}`);
|
|
1411
|
+
if (pse.best_plan_current != null) {
|
|
1412
|
+
const bestColor = pse.best_plan_current ? green : red;
|
|
1413
|
+
console.log(` ${c ? dim : ''}Melhor plano atual:${c ? reset : ''} ${c ? bestColor : ''}${pse.best_plan_current ? 'sim' : 'não'}${c ? reset : ''}`);
|
|
1414
|
+
}
|
|
1415
|
+
if (pse.confidence != null) {
|
|
1416
|
+
const confColor = Number(pse.confidence) >= 70 ? green : Number(pse.confidence) >= 50 ? yellow : red;
|
|
1417
|
+
console.log(` ${c ? dim : ''}Confiança:${c ? reset : ''} ${c ? confColor : ''}${pse.confidence}%${c ? reset : ''}`);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
console.log(`\n ${c ? dim : ''}Próximo passo:${c ? reset : ''} ${c ? cyan : ''}${report.next && report.next.cursorCmd ? report.next.cursorCmd : '—'}${c ? reset : ''}`);
|
|
1422
|
+
console.log(` ${c ? dim : ''}Motivo:${c ? reset : ''} ${report.next && report.next.reason ? report.next.reason : '—'}`);
|
|
1423
|
+
console.log(`\n ${c ? dim : ''}Para visão operacional completa (web): ${cyan}oxe-cc dashboard${c ? reset : ''}`);
|
|
1424
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} status --full concluído.\n`);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1235
1427
|
/**
|
|
1236
1428
|
* @param {string} target
|
|
1237
|
-
* @param {{ json?: boolean, hints?: boolean }} [opts]
|
|
1429
|
+
* @param {{ json?: boolean, hints?: boolean, full?: boolean }} [opts]
|
|
1238
1430
|
*/
|
|
1239
1431
|
function runStatus(target, opts = {}) {
|
|
1240
1432
|
const { config } = oxeHealth.loadOxeConfigMerged(target);
|
|
1241
|
-
const next = oxeHealth.suggestNextStep(target, { discuss_before_plan: config.discuss_before_plan });
|
|
1242
1433
|
const report = oxeHealth.buildHealthReport(target);
|
|
1243
1434
|
const routineHints = collectOxeRoutineHints(target, report, config);
|
|
1435
|
+
const next = report.next;
|
|
1244
1436
|
|
|
1245
1437
|
if (opts.json) {
|
|
1246
1438
|
/** @type {Record<string, unknown>} */
|
|
@@ -1250,25 +1442,35 @@ function runStatus(target, opts = {}) {
|
|
|
1250
1442
|
nextStep: report.next.step,
|
|
1251
1443
|
cursorCmd: report.next.cursorCmd,
|
|
1252
1444
|
reason: report.next.reason,
|
|
1253
|
-
artifacts: report.next.artifacts,
|
|
1254
|
-
phase: report.phase,
|
|
1255
|
-
healthStatus: report.healthStatus,
|
|
1256
|
-
activeSession: report.activeSession,
|
|
1257
|
-
scanDate: report.scanDate,
|
|
1258
|
-
staleScan: report.stale,
|
|
1259
|
-
compactDate: report.compactDate,
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1445
|
+
artifacts: report.next.artifacts,
|
|
1446
|
+
phase: report.phase,
|
|
1447
|
+
healthStatus: report.healthStatus,
|
|
1448
|
+
activeSession: report.activeSession,
|
|
1449
|
+
scanDate: report.scanDate,
|
|
1450
|
+
staleScan: report.stale,
|
|
1451
|
+
compactDate: report.compactDate,
|
|
1452
|
+
staleCompact: report.staleCompact,
|
|
1453
|
+
planSelfEvaluation: report.planSelfEvaluation,
|
|
1454
|
+
planReviewStatus: report.planReviewStatus,
|
|
1455
|
+
activeRun: report.activeRun,
|
|
1456
|
+
eventsSummary: report.eventsSummary,
|
|
1457
|
+
memoryLayers: report.memoryLayers,
|
|
1458
|
+
azureActive: report.azureActive,
|
|
1459
|
+
azure: report.azure,
|
|
1460
|
+
diagnostics: {
|
|
1461
|
+
configParseError: report.configParseError,
|
|
1462
|
+
typeErrors: report.typeErrors,
|
|
1463
|
+
unknownConfigKeys: report.unknownConfigKeys,
|
|
1464
|
+
phaseWarnings: report.phaseWarn,
|
|
1465
|
+
runtimeWarnings: report.runtimeWarn,
|
|
1466
|
+
reviewWarnings: report.reviewWarn,
|
|
1467
|
+
capabilityWarnings: report.capabilityWarn,
|
|
1468
|
+
investigationWarnings: report.investigationWarn,
|
|
1469
|
+
sessionWarnings: report.sessionWarn,
|
|
1470
|
+
installWarnings: report.installWarn,
|
|
1471
|
+
summaryGapWarning: report.summaryGapWarn,
|
|
1472
|
+
specWarnings: report.specWarn,
|
|
1473
|
+
planWarnings: report.planWarn,
|
|
1272
1474
|
},
|
|
1273
1475
|
};
|
|
1274
1476
|
if (opts.hints) {
|
|
@@ -1305,12 +1507,12 @@ function runStatus(target, opts = {}) {
|
|
|
1305
1507
|
console.log(` ${c ? dim : ''}No Cursor (referência):${c ? reset : ''} ${c ? cyan : ''}${next.cursorCmd}${reset}`);
|
|
1306
1508
|
console.log(` ${c ? dim : ''}Motivo:${c ? reset : ''} ${next.reason}`);
|
|
1307
1509
|
|
|
1308
|
-
printSummaryAndNextSteps(c, {
|
|
1309
|
-
bullets: [
|
|
1310
|
-
`Saúde lógica: ${report.healthStatus}`,
|
|
1311
|
-
`Artefatos em jogo: ${next.artifacts.join(', ')}`,
|
|
1312
|
-
],
|
|
1313
|
-
nextSteps: [
|
|
1510
|
+
printSummaryAndNextSteps(c, {
|
|
1511
|
+
bullets: [
|
|
1512
|
+
`Saúde lógica: ${report.healthStatus}`,
|
|
1513
|
+
`Artefatos em jogo: ${next.artifacts.join(', ')}`,
|
|
1514
|
+
],
|
|
1515
|
+
nextSteps: [
|
|
1314
1516
|
{ desc: 'Diagnóstico completo (inclui pacote de workflows):', cmd: 'npx oxe-cc doctor' },
|
|
1315
1517
|
{ desc: 'Ação sugerida no agente:', cmd: next.cursorCmd },
|
|
1316
1518
|
],
|
|
@@ -1409,7 +1611,7 @@ function runDoctor(target) {
|
|
|
1409
1611
|
'CONVENTIONS.md',
|
|
1410
1612
|
'CONCERNS.md',
|
|
1411
1613
|
];
|
|
1412
|
-
if (fs.existsSync(cbDir)) {
|
|
1614
|
+
if (fs.existsSync(cbDir)) {
|
|
1413
1615
|
const missingMaps = expectedMaps.filter((f) => !fs.existsSync(path.join(cbDir, f)));
|
|
1414
1616
|
if (missingMaps.length) {
|
|
1415
1617
|
console.log(
|
|
@@ -1418,21 +1620,21 @@ function runDoctor(target) {
|
|
|
1418
1620
|
} else {
|
|
1419
1621
|
console.log(`${green}OK${reset} .oxe/codebase/ com os ${expectedMaps.length} mapas esperados`);
|
|
1420
1622
|
}
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
printOxeHealthDiagnostics(target, c);
|
|
1424
|
-
const report = oxeHealth.buildHealthReport(target);
|
|
1425
|
-
const statusColor = report.healthStatus === 'healthy' ? green : report.healthStatus === 'warning' ? yellow : red;
|
|
1426
|
-
console.log(`\n ${statusColor}Diagnóstico ${report.healthStatus}${reset}`);
|
|
1427
|
-
if (report.healthStatus === 'broken') {
|
|
1428
|
-
process.exitCode = 1;
|
|
1429
|
-
}
|
|
1430
|
-
printSummaryAndNextSteps(c, {
|
|
1431
|
-
bullets: [
|
|
1432
|
-
`Projeto em ${target}`,
|
|
1433
|
-
`Workflows conferidos em ${wfLabel}`,
|
|
1434
|
-
`Saúde lógica: ${report.healthStatus}`,
|
|
1435
|
-
],
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
printOxeHealthDiagnostics(target, c);
|
|
1626
|
+
const report = oxeHealth.buildHealthReport(target);
|
|
1627
|
+
const statusColor = report.healthStatus === 'healthy' ? green : report.healthStatus === 'warning' ? yellow : red;
|
|
1628
|
+
console.log(`\n ${statusColor}Diagnóstico ${report.healthStatus}${reset}`);
|
|
1629
|
+
if (report.healthStatus === 'broken') {
|
|
1630
|
+
process.exitCode = 1;
|
|
1631
|
+
}
|
|
1632
|
+
printSummaryAndNextSteps(c, {
|
|
1633
|
+
bullets: [
|
|
1634
|
+
`Projeto em ${target}`,
|
|
1635
|
+
`Workflows conferidos em ${wfLabel}`,
|
|
1636
|
+
`Saúde lógica: ${report.healthStatus}`,
|
|
1637
|
+
],
|
|
1436
1638
|
nextSteps: [
|
|
1437
1639
|
{ desc: 'Mapear ou atualizar o codebase no agente:', cmd: '/oxe-scan' },
|
|
1438
1640
|
{ desc: 'Ver ajuda e ordem dos passos OXE:', cmd: '/oxe-help' },
|
|
@@ -1446,7 +1648,7 @@ function runDoctor(target) {
|
|
|
1446
1648
|
* npm install -g oxe-cc@version (same version as this running CLI).
|
|
1447
1649
|
* @returns {boolean}
|
|
1448
1650
|
*/
|
|
1449
|
-
function installGlobalCliPackage() {
|
|
1651
|
+
function installGlobalCliPackage() {
|
|
1450
1652
|
const name = readPkgName();
|
|
1451
1653
|
const ver = readPkgVersion();
|
|
1452
1654
|
const spec = `${name}@${ver}`;
|
|
@@ -1469,99 +1671,99 @@ function installGlobalCliPackage() {
|
|
|
1469
1671
|
`\n ${c ? yellow : ''}⚠${c ? reset : ''} npm install -g falhou. Tente manualmente: ${c ? cyan : ''}npm install -g ${spec}${c ? reset : ''}\n`
|
|
1470
1672
|
);
|
|
1471
1673
|
return false;
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
/**
|
|
1475
|
-
* `npm uninstall -g oxe-cc` com a mesma semântica cross-platform do instalador.
|
|
1476
|
-
* @returns {boolean}
|
|
1477
|
-
*/
|
|
1478
|
-
function uninstallGlobalCliPackage() {
|
|
1479
|
-
const name = readPkgName();
|
|
1480
|
-
const c = useAnsiColors();
|
|
1481
|
-
const dimOrEmpty = c ? dim : '';
|
|
1482
|
-
const resetOrEmpty = c ? reset : '';
|
|
1483
|
-
console.log(`\n ${dimOrEmpty}npm uninstall -g ${name}${resetOrEmpty}\n`);
|
|
1484
|
-
const r = spawnSync('npm', ['uninstall', '-g', name], {
|
|
1485
|
-
stdio: 'inherit',
|
|
1486
|
-
shell: true,
|
|
1487
|
-
env: process.env,
|
|
1488
|
-
});
|
|
1489
|
-
if (r.status === 0) {
|
|
1490
|
-
console.log(
|
|
1491
|
-
`\n ${c ? green : ''}✓${c ? reset : ''} pacote global ${c ? cyan : ''}${name}${c ? reset : ''} removido do npm global.\n`
|
|
1492
|
-
);
|
|
1493
|
-
return true;
|
|
1494
|
-
}
|
|
1495
|
-
console.log(
|
|
1496
|
-
`\n ${c ? yellow : ''}⚠${c ? reset : ''} npm uninstall -g falhou. Remova manualmente: ${c ? cyan : ''}npm uninstall -g ${name}${c ? reset : ''}\n`
|
|
1497
|
-
);
|
|
1498
|
-
return false;
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
/**
|
|
1502
|
-
* Best-effort: detecta se esta execução vem de uma instalação global do npm.
|
|
1503
|
-
* Usa `npm root -g` para evitar confundir execução local do repositório com pacote global.
|
|
1504
|
-
* @returns {boolean}
|
|
1505
|
-
*/
|
|
1506
|
-
function isRunningFromGlobalNpmInstall() {
|
|
1507
|
-
try {
|
|
1508
|
-
const r = spawnSync('npm', ['root', '-g'], {
|
|
1509
|
-
encoding: 'utf8',
|
|
1510
|
-
shell: true,
|
|
1511
|
-
env: process.env,
|
|
1512
|
-
});
|
|
1513
|
-
if (r.status !== 0) return false;
|
|
1514
|
-
const root = String(r.stdout || '').trim();
|
|
1515
|
-
if (!root) return false;
|
|
1516
|
-
const rel = path.relative(path.resolve(root), PKG_ROOT);
|
|
1517
|
-
return rel && !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
1518
|
-
} catch {
|
|
1519
|
-
return false;
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
/** @returns {string[]} */
|
|
1524
|
-
function updateForwardedInstallFlags() {
|
|
1525
|
-
return [
|
|
1526
|
-
'--cursor',
|
|
1527
|
-
'--copilot',
|
|
1528
|
-
'--copilot-cli',
|
|
1529
|
-
'--all-agents',
|
|
1530
|
-
'--opencode',
|
|
1531
|
-
'--gemini',
|
|
1532
|
-
'--codex',
|
|
1533
|
-
'--windsurf',
|
|
1534
|
-
'--antigravity',
|
|
1535
|
-
'--vscode',
|
|
1536
|
-
'--no-commands',
|
|
1537
|
-
'--no-agents',
|
|
1538
|
-
'--no-init-oxe',
|
|
1539
|
-
'--oxe-only',
|
|
1540
|
-
'--global',
|
|
1541
|
-
'--local',
|
|
1542
|
-
'--ide-global',
|
|
1543
|
-
'--ide-local',
|
|
1544
|
-
'--global-cli',
|
|
1545
|
-
'-g',
|
|
1546
|
-
'--no-global-cli',
|
|
1547
|
-
'-l',
|
|
1548
|
-
'--no-install-config',
|
|
1549
|
-
'--force',
|
|
1550
|
-
'-f',
|
|
1551
|
-
'--all',
|
|
1552
|
-
'-a',
|
|
1553
|
-
'--config-dir',
|
|
1554
|
-
'-c',
|
|
1555
|
-
];
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
|
-
/**
|
|
1559
|
-
* @param {string[]} rest
|
|
1560
|
-
* @returns {boolean}
|
|
1561
|
-
*/
|
|
1562
|
-
function updateArgsExplicitlyControlGlobalCli(rest) {
|
|
1563
|
-
return rest.includes('--global-cli') || rest.includes('-g') || rest.includes('--no-global-cli') || rest.includes('-l');
|
|
1564
|
-
}
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
/**
|
|
1677
|
+
* `npm uninstall -g oxe-cc` com a mesma semântica cross-platform do instalador.
|
|
1678
|
+
* @returns {boolean}
|
|
1679
|
+
*/
|
|
1680
|
+
function uninstallGlobalCliPackage() {
|
|
1681
|
+
const name = readPkgName();
|
|
1682
|
+
const c = useAnsiColors();
|
|
1683
|
+
const dimOrEmpty = c ? dim : '';
|
|
1684
|
+
const resetOrEmpty = c ? reset : '';
|
|
1685
|
+
console.log(`\n ${dimOrEmpty}npm uninstall -g ${name}${resetOrEmpty}\n`);
|
|
1686
|
+
const r = spawnSync('npm', ['uninstall', '-g', name], {
|
|
1687
|
+
stdio: 'inherit',
|
|
1688
|
+
shell: true,
|
|
1689
|
+
env: process.env,
|
|
1690
|
+
});
|
|
1691
|
+
if (r.status === 0) {
|
|
1692
|
+
console.log(
|
|
1693
|
+
`\n ${c ? green : ''}✓${c ? reset : ''} pacote global ${c ? cyan : ''}${name}${c ? reset : ''} removido do npm global.\n`
|
|
1694
|
+
);
|
|
1695
|
+
return true;
|
|
1696
|
+
}
|
|
1697
|
+
console.log(
|
|
1698
|
+
`\n ${c ? yellow : ''}⚠${c ? reset : ''} npm uninstall -g falhou. Remova manualmente: ${c ? cyan : ''}npm uninstall -g ${name}${c ? reset : ''}\n`
|
|
1699
|
+
);
|
|
1700
|
+
return false;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
/**
|
|
1704
|
+
* Best-effort: detecta se esta execução vem de uma instalação global do npm.
|
|
1705
|
+
* Usa `npm root -g` para evitar confundir execução local do repositório com pacote global.
|
|
1706
|
+
* @returns {boolean}
|
|
1707
|
+
*/
|
|
1708
|
+
function isRunningFromGlobalNpmInstall() {
|
|
1709
|
+
try {
|
|
1710
|
+
const r = spawnSync('npm', ['root', '-g'], {
|
|
1711
|
+
encoding: 'utf8',
|
|
1712
|
+
shell: true,
|
|
1713
|
+
env: process.env,
|
|
1714
|
+
});
|
|
1715
|
+
if (r.status !== 0) return false;
|
|
1716
|
+
const root = String(r.stdout || '').trim();
|
|
1717
|
+
if (!root) return false;
|
|
1718
|
+
const rel = path.relative(path.resolve(root), PKG_ROOT);
|
|
1719
|
+
return rel && !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
1720
|
+
} catch {
|
|
1721
|
+
return false;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/** @returns {string[]} */
|
|
1726
|
+
function updateForwardedInstallFlags() {
|
|
1727
|
+
return [
|
|
1728
|
+
'--cursor',
|
|
1729
|
+
'--copilot',
|
|
1730
|
+
'--copilot-cli',
|
|
1731
|
+
'--all-agents',
|
|
1732
|
+
'--opencode',
|
|
1733
|
+
'--gemini',
|
|
1734
|
+
'--codex',
|
|
1735
|
+
'--windsurf',
|
|
1736
|
+
'--antigravity',
|
|
1737
|
+
'--vscode',
|
|
1738
|
+
'--no-commands',
|
|
1739
|
+
'--no-agents',
|
|
1740
|
+
'--no-init-oxe',
|
|
1741
|
+
'--oxe-only',
|
|
1742
|
+
'--global',
|
|
1743
|
+
'--local',
|
|
1744
|
+
'--ide-global',
|
|
1745
|
+
'--ide-local',
|
|
1746
|
+
'--global-cli',
|
|
1747
|
+
'-g',
|
|
1748
|
+
'--no-global-cli',
|
|
1749
|
+
'-l',
|
|
1750
|
+
'--no-install-config',
|
|
1751
|
+
'--force',
|
|
1752
|
+
'-f',
|
|
1753
|
+
'--all',
|
|
1754
|
+
'-a',
|
|
1755
|
+
'--config-dir',
|
|
1756
|
+
'-c',
|
|
1757
|
+
];
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/**
|
|
1761
|
+
* @param {string[]} rest
|
|
1762
|
+
* @returns {boolean}
|
|
1763
|
+
*/
|
|
1764
|
+
function updateArgsExplicitlyControlGlobalCli(rest) {
|
|
1765
|
+
return rest.includes('--global-cli') || rest.includes('-g') || rest.includes('--no-global-cli') || rest.includes('-l');
|
|
1766
|
+
}
|
|
1565
1767
|
|
|
1566
1768
|
/**
|
|
1567
1769
|
* After copying OXE into the project: optionally install the CLI globally (pergunta interativa ou flags).
|
|
@@ -1646,27 +1848,92 @@ ${green}Uso:${reset}
|
|
|
1646
1848
|
npx oxe-cc doctor [opções] [pasta-do-projeto]
|
|
1647
1849
|
npx oxe-cc status [opções] [pasta-do-projeto]
|
|
1648
1850
|
npx oxe-cc init-oxe [opções] [pasta-do-projeto]
|
|
1851
|
+
npx oxe-cc dashboard [opções] [pasta-do-projeto]
|
|
1852
|
+
npx oxe-cc runtime <status|start|pause|resume|replay> [opções] [pasta-do-projeto]
|
|
1853
|
+
npx oxe-cc azure <status|doctor|auth|sync|find|servicebus|eventgrid|sql|operations> [opções] [pasta-do-projeto]
|
|
1854
|
+
npx oxe-cc capabilities <list|install|remove|update> [opções] [id]
|
|
1649
1855
|
npx oxe-cc uninstall [opções] [pasta-do-projeto]
|
|
1650
1856
|
npx oxe-cc update [opções] [argumentos extras…]
|
|
1651
1857
|
|
|
1652
|
-
${green}uninstall${reset} (remove OXE da pasta do usuário + pastas de workflows no repo)
|
|
1653
|
-
--cursor / --copilot / --copilot-cli só essa integração (omissão = todas)
|
|
1654
|
-
--all-agents também remove ficheiros multi-plataforma (com --copilot-cli implícito)
|
|
1655
|
-
--ide-local remove integrações IDE neste repositório (.cursor, .github, .claude, .copilot, …)
|
|
1656
|
-
--ide-only não apagar .oxe/workflows, oxe/, etc. no projeto
|
|
1657
|
-
--global-cli, -g também executa npm uninstall -g oxe-cc
|
|
1658
|
-
--config-dir <caminho> com exatamente uma flag IDE acima (não combina com --ide-local)
|
|
1659
|
-
--dry-run
|
|
1660
|
-
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1858
|
+
${green}uninstall${reset} (remove OXE da pasta do usuário + pastas de workflows no repo)
|
|
1859
|
+
--cursor / --copilot / --copilot-cli só essa integração (omissão = todas)
|
|
1860
|
+
--all-agents também remove ficheiros multi-plataforma (com --copilot-cli implícito)
|
|
1861
|
+
--ide-local remove integrações IDE neste repositório (.cursor, .github, .claude, .copilot, …)
|
|
1862
|
+
--ide-only não apagar .oxe/workflows, oxe/, etc. no projeto
|
|
1863
|
+
--global-cli, -g também executa npm uninstall -g oxe-cc
|
|
1864
|
+
--config-dir <caminho> com exatamente uma flag IDE acima (não combina com --ide-local)
|
|
1865
|
+
--dry-run
|
|
1866
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1661
1867
|
|
|
1662
1868
|
${green}update${reset} (executa npx oxe-cc@latest --force na pasta do projeto)
|
|
1663
1869
|
--check só consulta npm: compara versão em execução com latest (saída 0=ok, 1=há mais nova, 2=erro; incompatível com --dry-run)
|
|
1664
1870
|
--if-newer só executa o npx se existir versão mais nova no npm (falha de rede/registry: saída 2, sem npx)
|
|
1665
1871
|
--dir <pasta> pasta em que o npx roda (padrão: atual; ignorada com --check)
|
|
1666
1872
|
--dry-run mostra o comando sem executar
|
|
1667
|
-
[argumentos extras…] repassados ao oxe-cc (ex.: --cursor --global, --ide-local, --global-cli)
|
|
1873
|
+
[argumentos extras…] repassados ao oxe-cc (ex.: --cursor --global, --ide-local, --global-cli)
|
|
1668
1874
|
${dim}CI / sem rede:${reset} OXE_UPDATE_SKIP_REGISTRY=1 desativa consultas (--check sai 2; --if-newer sai 2 sem npx)
|
|
1669
1875
|
|
|
1876
|
+
${green}dashboard${reset} (interface web local para revisão e aprovação do plano)
|
|
1877
|
+
--port <número> porta local (padrão: 4173)
|
|
1878
|
+
--no-open não abre o browser automaticamente
|
|
1879
|
+
--session <sessions/sNNN-slug> força visualização de uma sessão específica
|
|
1880
|
+
--dump-context imprime JSON consolidado e sai
|
|
1881
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1882
|
+
|
|
1883
|
+
${green}runtime${reset} (controle operacional explícito do ACTIVE-RUN)
|
|
1884
|
+
status mostra o run ativo resolvido para a sessão atual
|
|
1885
|
+
start cria um novo run com tracing inicial
|
|
1886
|
+
pause pausa o run ativo e preserva o cursor
|
|
1887
|
+
resume retoma o run ativo
|
|
1888
|
+
replay marca replay parcial por onda ou tarefa
|
|
1889
|
+
--session <sessions/sNNN-slug> força sessão específica
|
|
1890
|
+
--wave <número> fixa onda atual/cursor
|
|
1891
|
+
--task <Tn> fixa tarefa atual/cursor
|
|
1892
|
+
--mode <complete|wave|task> modo operacional do cursor
|
|
1893
|
+
--reason <texto> motivo explícito da transição
|
|
1894
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1895
|
+
|
|
1896
|
+
${green}azure${reset} (provider Azure nativo via Azure CLI no Windows)
|
|
1897
|
+
status estado compacto: CLI, login, subscription, inventário, pendências
|
|
1898
|
+
doctor valida Azure CLI, login, subscription e inventário
|
|
1899
|
+
auth login [--tenant <id>] login interativo via Azure CLI (Entra ID: use --tenant <tenant-id>)
|
|
1900
|
+
auth whoami mostra identidade, tenant, subscription e cloud
|
|
1901
|
+
auth set-subscription --subscription <id|nome>
|
|
1902
|
+
fixa a subscription operacional do projeto
|
|
1903
|
+
Fluxo corporativo: auth login --tenant <tenant-id> → auth set-subscription --subscription <dev-sub-id>
|
|
1904
|
+
sync sincroniza inventário via Azure Resource Graph
|
|
1905
|
+
sync --diff sincroniza e mostra recursos adicionados/removidos
|
|
1906
|
+
find <texto> busca recursos no inventário local materializado
|
|
1907
|
+
find <texto> --type <tipo> filtra por tipo de serviço (ex.: servicebus, eventgrid, sql)
|
|
1908
|
+
find <texto> --filter-rg <rg> filtra por resource group
|
|
1909
|
+
servicebus <list|show|plan|apply> namespace, queue, topic, subscription
|
|
1910
|
+
eventgrid <list|show|plan|apply> topic, system-topic, event-subscription
|
|
1911
|
+
sql <list|show|plan|apply> server, database, firewall-rule
|
|
1912
|
+
operations list histórico de operações planejadas/aplicadas/pendentes
|
|
1913
|
+
--kind <tipo> ex.: namespace, queue, topic, system-topic, database
|
|
1914
|
+
--resource-group <rg> resource group alvo
|
|
1915
|
+
--name <nome> nome principal do recurso
|
|
1916
|
+
--namespace <nome> namespace Service Bus
|
|
1917
|
+
--topic-name <nome> topic Service Bus
|
|
1918
|
+
--subscription-name <nome> subscription de topic Service Bus
|
|
1919
|
+
--source-resource-id <id> origem de event subscription
|
|
1920
|
+
--endpoint <url|id> endpoint de Event Grid
|
|
1921
|
+
--server <nome> / --database <nome> recursos Azure SQL
|
|
1922
|
+
--location <região> localização alvo
|
|
1923
|
+
--admin-user <user> admin do SQL server (plan/apply)
|
|
1924
|
+
--admin-password-env <ENV> variável de ambiente com password do SQL admin
|
|
1925
|
+
--start-ip-address / --end-ip-address faixa de firewall rule para Azure SQL
|
|
1926
|
+
--approve aplica mutação já planejada após checkpoint formal
|
|
1927
|
+
--dry-run pré-visualiza comando sem executar nem criar artefatos
|
|
1928
|
+
--diff (sync) exibe diff de recursos adicionados/removidos
|
|
1929
|
+
--type <tipo> (find) filtra por família de serviço
|
|
1930
|
+
--filter-rg <rg> (find) filtra por resource group
|
|
1931
|
+
--tenant <id> (auth login) tenant Entra ID para contas corporativas
|
|
1932
|
+
--vpn-confirmed confirma conexão VPN quando vpn_required está configurado
|
|
1933
|
+
--override-policy override explícito para policy deny_unless_overridden
|
|
1934
|
+
--session <sessions/sNNN-slug> associa a operação ao runtime da sessão ativa
|
|
1935
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1936
|
+
|
|
1670
1937
|
${green}Opções da instalação:${reset}
|
|
1671
1938
|
--cursor Copia comandos e regras para ~/.cursor (padrão com --all)
|
|
1672
1939
|
--copilot Mescla instruções + prompts em ~/.copilot (global) ou .github/ (com --ide-local)
|
|
@@ -1706,6 +1973,13 @@ ${green}status${reset} (coerência .oxe/ + um próximo passo sugerido; não exig
|
|
|
1706
1973
|
--json imprime um único objeto JSON (próximo passo + diagnósticos) em stdout; adequado a CI
|
|
1707
1974
|
--hints lembretes de rotina (idade scan/compact quando configurado em config.json); com --json inclui array \`hints\`
|
|
1708
1975
|
|
|
1976
|
+
${green}capabilities${reset} (catálogo nativo de extensões do projeto)
|
|
1977
|
+
list lista capabilities instaladas em .oxe/capabilities/
|
|
1978
|
+
install <id> cria capability local a partir do template nativo
|
|
1979
|
+
remove <id> remove capability do catálogo local
|
|
1980
|
+
update regera .oxe/CAPABILITIES.md a partir dos manifestos locais
|
|
1981
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1982
|
+
|
|
1709
1983
|
${green}Atualizar (projeto já tem OXE):${reset}
|
|
1710
1984
|
/oxe-update no Cursor (outras IDEs: mesmo fluxo pelo terminal)
|
|
1711
1985
|
npx oxe-cc update --check só ver se há versão nova no npm
|
|
@@ -2007,7 +2281,7 @@ function runInstall(opts) {
|
|
|
2007
2281
|
console.log(` ${c ? green : ''}✓${c ? reset : ''} Instalação concluída com sucesso.\n`);
|
|
2008
2282
|
}
|
|
2009
2283
|
|
|
2010
|
-
/** @typedef {{ help: boolean, dryRun: boolean, cursor: boolean, copilot: boolean, copilotCli: boolean, allAgents: boolean, globalCli: boolean, ideLocal: boolean, ideExplicit: boolean, noProject: boolean, dir: string, explicitConfigDir: string | null, parseError: boolean, unknownFlag: string, conflictFlags: string }} UninstallOpts */
|
|
2284
|
+
/** @typedef {{ help: boolean, dryRun: boolean, cursor: boolean, copilot: boolean, copilotCli: boolean, allAgents: boolean, globalCli: boolean, ideLocal: boolean, ideExplicit: boolean, noProject: boolean, dir: string, explicitConfigDir: string | null, parseError: boolean, unknownFlag: string, conflictFlags: string }} UninstallOpts */
|
|
2011
2285
|
|
|
2012
2286
|
/**
|
|
2013
2287
|
* @param {UninstallOpts} u
|
|
@@ -2104,10 +2378,10 @@ function uninstallLocalIdeFromProject(u, removedPaths) {
|
|
|
2104
2378
|
}
|
|
2105
2379
|
|
|
2106
2380
|
/**
|
|
2107
|
-
* @param {string[]} argv
|
|
2108
|
-
* @returns {UninstallOpts}
|
|
2109
|
-
*/
|
|
2110
|
-
function parseUninstallArgs(argv) {
|
|
2381
|
+
* @param {string[]} argv
|
|
2382
|
+
* @returns {UninstallOpts}
|
|
2383
|
+
*/
|
|
2384
|
+
function parseUninstallArgs(argv) {
|
|
2111
2385
|
/** @type {UninstallOpts} */
|
|
2112
2386
|
const out = {
|
|
2113
2387
|
help: false,
|
|
@@ -2115,11 +2389,11 @@ function parseUninstallArgs(argv) {
|
|
|
2115
2389
|
cursor: false,
|
|
2116
2390
|
copilot: false,
|
|
2117
2391
|
copilotCli: false,
|
|
2118
|
-
allAgents: false,
|
|
2119
|
-
globalCli: false,
|
|
2120
|
-
ideLocal: false,
|
|
2121
|
-
ideExplicit: false,
|
|
2122
|
-
noProject: false,
|
|
2392
|
+
allAgents: false,
|
|
2393
|
+
globalCli: false,
|
|
2394
|
+
ideLocal: false,
|
|
2395
|
+
ideExplicit: false,
|
|
2396
|
+
noProject: false,
|
|
2123
2397
|
dir: process.cwd(),
|
|
2124
2398
|
explicitConfigDir: null,
|
|
2125
2399
|
parseError: false,
|
|
@@ -2142,15 +2416,15 @@ function parseUninstallArgs(argv) {
|
|
|
2142
2416
|
} else if (a === '--copilot-cli') {
|
|
2143
2417
|
out.copilotCli = true;
|
|
2144
2418
|
out.ideExplicit = true;
|
|
2145
|
-
} else if (a === '--all-agents') {
|
|
2146
|
-
out.allAgents = true;
|
|
2147
|
-
out.copilotCli = true;
|
|
2148
|
-
out.ideExplicit = true;
|
|
2149
|
-
} else if (a === '--global-cli' || a === '-g') {
|
|
2150
|
-
out.globalCli = true;
|
|
2151
|
-
} else if (a === '--ide-local') out.ideLocal = true;
|
|
2152
|
-
else if (a === '--ide-only') out.noProject = true;
|
|
2153
|
-
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
2419
|
+
} else if (a === '--all-agents') {
|
|
2420
|
+
out.allAgents = true;
|
|
2421
|
+
out.copilotCli = true;
|
|
2422
|
+
out.ideExplicit = true;
|
|
2423
|
+
} else if (a === '--global-cli' || a === '-g') {
|
|
2424
|
+
out.globalCli = true;
|
|
2425
|
+
} else if (a === '--ide-local') out.ideLocal = true;
|
|
2426
|
+
else if (a === '--ide-only') out.noProject = true;
|
|
2427
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
2154
2428
|
else if (!a.startsWith('-')) rest.push(path.resolve(a));
|
|
2155
2429
|
else {
|
|
2156
2430
|
out.parseError = true;
|
|
@@ -2230,7 +2504,7 @@ function rmDirIfEmpty(dirPath, opts) {
|
|
|
2230
2504
|
/**
|
|
2231
2505
|
* @param {UninstallOpts} u
|
|
2232
2506
|
*/
|
|
2233
|
-
function runUninstall(u) {
|
|
2507
|
+
function runUninstall(u) {
|
|
2234
2508
|
assertNotWslWindowsNode();
|
|
2235
2509
|
const c = useAnsiColors();
|
|
2236
2510
|
const home = os.homedir();
|
|
@@ -2387,7 +2661,7 @@ function runUninstall(u) {
|
|
|
2387
2661
|
}
|
|
2388
2662
|
}
|
|
2389
2663
|
|
|
2390
|
-
if (!u.dryRun && (u.cursor || u.copilot || u.copilotCli)) {
|
|
2664
|
+
if (!u.dryRun && (u.cursor || u.copilot || u.copilotCli)) {
|
|
2391
2665
|
const prev = oxeManifest.loadFileManifest(home);
|
|
2392
2666
|
const next = { ...prev };
|
|
2393
2667
|
for (const p of removedPaths) delete next[p];
|
|
@@ -2403,31 +2677,31 @@ function runUninstall(u) {
|
|
|
2403
2677
|
delete next[instPath];
|
|
2404
2678
|
}
|
|
2405
2679
|
}
|
|
2406
|
-
oxeManifest.writeFileManifest(home, next, readPkgVersion());
|
|
2407
|
-
}
|
|
2408
|
-
|
|
2409
|
-
if (u.globalCli && !u.dryRun) {
|
|
2410
|
-
uninstallGlobalCliPackage();
|
|
2411
|
-
} else if (u.globalCli && u.dryRun) {
|
|
2412
|
-
console.log(`${dim}npm${reset} npm uninstall -g ${readPkgName()}`);
|
|
2413
|
-
}
|
|
2414
|
-
|
|
2415
|
-
printSummaryAndNextSteps(c, buildUninstallFooter(u));
|
|
2416
|
-
if (!u.globalCli) {
|
|
2417
|
-
console.log(
|
|
2418
|
-
` ${c ? yellow : ''}Nota:${c ? reset : ''} o pacote npm global ${c ? cyan : ''}${readPkgName()}${c ? reset : ''} não é removido por padrão. Use ${c ? cyan : ''}oxe-cc uninstall --global-cli${c ? reset : ''} ou ${c ? cyan : ''}npm uninstall -g ${readPkgName()}${c ? reset : ''}.\n`
|
|
2419
|
-
);
|
|
2420
|
-
}
|
|
2421
|
-
console.log(` ${c ? green : ''}✓${c ? reset : ''} Desinstalação concluída com sucesso.\n`);
|
|
2422
|
-
}
|
|
2423
|
-
|
|
2424
|
-
/** @typedef {{ help: boolean, dryRun: boolean, check: boolean, ifNewer: boolean, dir: string, rest: string[], parseError: boolean, unknownFlag: string, conflictFlags: string|null }} UpdateOpts */
|
|
2680
|
+
oxeManifest.writeFileManifest(home, next, readPkgVersion());
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
if (u.globalCli && !u.dryRun) {
|
|
2684
|
+
uninstallGlobalCliPackage();
|
|
2685
|
+
} else if (u.globalCli && u.dryRun) {
|
|
2686
|
+
console.log(`${dim}npm${reset} npm uninstall -g ${readPkgName()}`);
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
printSummaryAndNextSteps(c, buildUninstallFooter(u));
|
|
2690
|
+
if (!u.globalCli) {
|
|
2691
|
+
console.log(
|
|
2692
|
+
` ${c ? yellow : ''}Nota:${c ? reset : ''} o pacote npm global ${c ? cyan : ''}${readPkgName()}${c ? reset : ''} não é removido por padrão. Use ${c ? cyan : ''}oxe-cc uninstall --global-cli${c ? reset : ''} ou ${c ? cyan : ''}npm uninstall -g ${readPkgName()}${c ? reset : ''}.\n`
|
|
2693
|
+
);
|
|
2694
|
+
}
|
|
2695
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Desinstalação concluída com sucesso.\n`);
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
/** @typedef {{ help: boolean, dryRun: boolean, check: boolean, ifNewer: boolean, dir: string, rest: string[], parseError: boolean, unknownFlag: string, conflictFlags: string|null }} UpdateOpts */
|
|
2425
2699
|
|
|
2426
2700
|
/**
|
|
2427
2701
|
* @param {string[]} argv
|
|
2428
2702
|
* @returns {UpdateOpts}
|
|
2429
2703
|
*/
|
|
2430
|
-
function parseUpdateArgs(argv) {
|
|
2704
|
+
function parseUpdateArgs(argv) {
|
|
2431
2705
|
/** @type {UpdateOpts} */
|
|
2432
2706
|
const out = {
|
|
2433
2707
|
help: false,
|
|
@@ -2440,31 +2714,31 @@ function parseUpdateArgs(argv) {
|
|
|
2440
2714
|
unknownFlag: '',
|
|
2441
2715
|
conflictFlags: null,
|
|
2442
2716
|
};
|
|
2443
|
-
let dirExplicit = false;
|
|
2444
|
-
let firstPositionalConsumed = false;
|
|
2445
|
-
const passthroughFlags = new Set(updateForwardedInstallFlags());
|
|
2446
|
-
for (let i = 0; i < argv.length; i++) {
|
|
2447
|
-
const a = argv[i];
|
|
2448
|
-
if (a === '-h' || a === '--help') out.help = true;
|
|
2717
|
+
let dirExplicit = false;
|
|
2718
|
+
let firstPositionalConsumed = false;
|
|
2719
|
+
const passthroughFlags = new Set(updateForwardedInstallFlags());
|
|
2720
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2721
|
+
const a = argv[i];
|
|
2722
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
2449
2723
|
else if (a === '--dry-run') out.dryRun = true;
|
|
2450
2724
|
else if (a === '--check') out.check = true;
|
|
2451
2725
|
else if (a === '--if-newer') out.ifNewer = true;
|
|
2452
2726
|
else if (a === '--dir' && argv[i + 1]) {
|
|
2453
2727
|
out.dir = path.resolve(argv[++i]);
|
|
2454
2728
|
dirExplicit = true;
|
|
2455
|
-
} else if (!a.startsWith('-')) {
|
|
2456
|
-
if (!dirExplicit && !firstPositionalConsumed) {
|
|
2457
|
-
out.dir = path.resolve(a);
|
|
2458
|
-
firstPositionalConsumed = true;
|
|
2459
|
-
} else out.rest.push(a);
|
|
2460
|
-
} else if (passthroughFlags.has(a)) {
|
|
2461
|
-
out.rest.push(a);
|
|
2462
|
-
if ((a === '--config-dir' || a === '-c') && argv[i + 1]) {
|
|
2463
|
-
out.rest.push(argv[++i]);
|
|
2464
|
-
}
|
|
2465
|
-
} else {
|
|
2466
|
-
out.parseError = true;
|
|
2467
|
-
out.unknownFlag = a;
|
|
2729
|
+
} else if (!a.startsWith('-')) {
|
|
2730
|
+
if (!dirExplicit && !firstPositionalConsumed) {
|
|
2731
|
+
out.dir = path.resolve(a);
|
|
2732
|
+
firstPositionalConsumed = true;
|
|
2733
|
+
} else out.rest.push(a);
|
|
2734
|
+
} else if (passthroughFlags.has(a)) {
|
|
2735
|
+
out.rest.push(a);
|
|
2736
|
+
if ((a === '--config-dir' || a === '-c') && argv[i + 1]) {
|
|
2737
|
+
out.rest.push(argv[++i]);
|
|
2738
|
+
}
|
|
2739
|
+
} else {
|
|
2740
|
+
out.parseError = true;
|
|
2741
|
+
out.unknownFlag = a;
|
|
2468
2742
|
break;
|
|
2469
2743
|
}
|
|
2470
2744
|
}
|
|
@@ -2519,7 +2793,7 @@ function runUpdateVersionCheck(u) {
|
|
|
2519
2793
|
/**
|
|
2520
2794
|
* @param {UpdateOpts} u
|
|
2521
2795
|
*/
|
|
2522
|
-
function runUpdate(u) {
|
|
2796
|
+
function runUpdate(u) {
|
|
2523
2797
|
assertNotWslWindowsNode();
|
|
2524
2798
|
const c = useAnsiColors();
|
|
2525
2799
|
if (u.dryRun) {
|
|
@@ -2530,12 +2804,12 @@ function runUpdate(u) {
|
|
|
2530
2804
|
);
|
|
2531
2805
|
}
|
|
2532
2806
|
console.log(` ${dim}Comando que seria executado (instalação):${reset}`);
|
|
2533
|
-
const dryRunArgs = ['-y', 'oxe-cc@latest', '--force'];
|
|
2534
|
-
if (updateArgsExplicitlyControlGlobalCli(u.rest)) dryRunArgs.push(...u.rest);
|
|
2535
|
-
else if (isRunningFromGlobalNpmInstall()) dryRunArgs.push('--global-cli');
|
|
2536
|
-
else dryRunArgs.push('--no-global-cli', '-l');
|
|
2537
|
-
console.log(` ${cyan}npx ${dryRunArgs.join(' ')}${reset}`);
|
|
2538
|
-
console.log(` ${dim}Diretório:${reset} ${u.dir}`);
|
|
2807
|
+
const dryRunArgs = ['-y', 'oxe-cc@latest', '--force'];
|
|
2808
|
+
if (updateArgsExplicitlyControlGlobalCli(u.rest)) dryRunArgs.push(...u.rest);
|
|
2809
|
+
else if (isRunningFromGlobalNpmInstall()) dryRunArgs.push('--global-cli');
|
|
2810
|
+
else dryRunArgs.push('--no-global-cli', '-l');
|
|
2811
|
+
console.log(` ${cyan}npx ${dryRunArgs.join(' ')}${reset}`);
|
|
2812
|
+
console.log(` ${dim}Diretório:${reset} ${u.dir}`);
|
|
2539
2813
|
printSummaryAndNextSteps(c, {
|
|
2540
2814
|
bullets: [
|
|
2541
2815
|
'[simulação] O npx baixaria o pacote oxe-cc@latest e rodaria a instalação com --force.',
|
|
@@ -2580,20 +2854,20 @@ function runUpdate(u) {
|
|
|
2580
2854
|
console.log(
|
|
2581
2855
|
` ${dim}Há versão mais nova no npm (${res.version} > ${current}); a executar npx…${reset}\n`
|
|
2582
2856
|
);
|
|
2583
|
-
}
|
|
2584
|
-
|
|
2585
|
-
printSection('OXE ▸ update');
|
|
2586
|
-
const args = ['-y', 'oxe-cc@latest', '--force'];
|
|
2587
|
-
if (updateArgsExplicitlyControlGlobalCli(u.rest)) {
|
|
2588
|
-
args.push(...u.rest);
|
|
2589
|
-
} else if (isRunningFromGlobalNpmInstall()) {
|
|
2590
|
-
args.push('--global-cli', ...u.rest);
|
|
2591
|
-
} else {
|
|
2592
|
-
args.push('--no-global-cli', '-l', ...u.rest);
|
|
2593
|
-
}
|
|
2594
|
-
const r = spawnSync('npx', args, {
|
|
2595
|
-
cwd: u.dir,
|
|
2596
|
-
stdio: 'inherit',
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
printSection('OXE ▸ update');
|
|
2860
|
+
const args = ['-y', 'oxe-cc@latest', '--force'];
|
|
2861
|
+
if (updateArgsExplicitlyControlGlobalCli(u.rest)) {
|
|
2862
|
+
args.push(...u.rest);
|
|
2863
|
+
} else if (isRunningFromGlobalNpmInstall()) {
|
|
2864
|
+
args.push('--global-cli', ...u.rest);
|
|
2865
|
+
} else {
|
|
2866
|
+
args.push('--no-global-cli', '-l', ...u.rest);
|
|
2867
|
+
}
|
|
2868
|
+
const r = spawnSync('npx', args, {
|
|
2869
|
+
cwd: u.dir,
|
|
2870
|
+
stdio: 'inherit',
|
|
2597
2871
|
env: { ...process.env },
|
|
2598
2872
|
shell: process.platform === 'win32',
|
|
2599
2873
|
});
|
|
@@ -2616,6 +2890,823 @@ function runUpdate(u) {
|
|
|
2616
2890
|
console.log(` ${c ? green : ''}✓${c ? reset : ''} Atualização concluída com sucesso.\n`);
|
|
2617
2891
|
}
|
|
2618
2892
|
|
|
2893
|
+
/**
|
|
2894
|
+
* @typedef {{ help: boolean, dir: string, action: string, id: string, parseError: boolean, unknownFlag: string }} CapabilityOpts
|
|
2895
|
+
*/
|
|
2896
|
+
|
|
2897
|
+
/**
|
|
2898
|
+
* @param {string[]} argv
|
|
2899
|
+
* @returns {CapabilityOpts}
|
|
2900
|
+
*/
|
|
2901
|
+
function parseCapabilitiesArgs(argv) {
|
|
2902
|
+
/** @type {CapabilityOpts} */
|
|
2903
|
+
const out = {
|
|
2904
|
+
help: false,
|
|
2905
|
+
dir: process.cwd(),
|
|
2906
|
+
action: 'list',
|
|
2907
|
+
id: '',
|
|
2908
|
+
parseError: false,
|
|
2909
|
+
unknownFlag: '',
|
|
2910
|
+
};
|
|
2911
|
+
const positionals = [];
|
|
2912
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2913
|
+
const a = argv[i];
|
|
2914
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
2915
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
2916
|
+
else if (!a.startsWith('-')) positionals.push(a);
|
|
2917
|
+
else {
|
|
2918
|
+
out.parseError = true;
|
|
2919
|
+
out.unknownFlag = a;
|
|
2920
|
+
break;
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
if (positionals.length) {
|
|
2924
|
+
out.action = positionals[0];
|
|
2925
|
+
out.id = positionals[1] || '';
|
|
2926
|
+
}
|
|
2927
|
+
return out;
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
/**
|
|
2931
|
+
* @typedef {{ help: boolean, dir: string, port: number, noOpen: boolean, readOnly: boolean, dumpContext: boolean, activeSession: string|null, parseError: boolean, unknownFlag: string }} DashboardOpts
|
|
2932
|
+
*/
|
|
2933
|
+
|
|
2934
|
+
/**
|
|
2935
|
+
* @typedef {{ help: boolean, dir: string, action: string, activeSession: string|null, wave: number|null, task: string, mode: string, reason: string, parseError: boolean, unknownFlag: string }} RuntimeOpts
|
|
2936
|
+
*/
|
|
2937
|
+
|
|
2938
|
+
/**
|
|
2939
|
+
* @typedef {{
|
|
2940
|
+
* help: boolean,
|
|
2941
|
+
* dir: string,
|
|
2942
|
+
* scope: string,
|
|
2943
|
+
* action: string,
|
|
2944
|
+
* query: string,
|
|
2945
|
+
* activeSession: string|null,
|
|
2946
|
+
* subscription: string,
|
|
2947
|
+
* kind: string,
|
|
2948
|
+
* resourceGroup: string,
|
|
2949
|
+
* name: string,
|
|
2950
|
+
* namespace: string,
|
|
2951
|
+
* topicName: string,
|
|
2952
|
+
* subscriptionName: string,
|
|
2953
|
+
* sourceResourceId: string,
|
|
2954
|
+
* endpoint: string,
|
|
2955
|
+
* location: string,
|
|
2956
|
+
* server: string,
|
|
2957
|
+
* database: string,
|
|
2958
|
+
* adminUser: string,
|
|
2959
|
+
* adminPasswordEnv: string,
|
|
2960
|
+
* startIpAddress: string,
|
|
2961
|
+
* endIpAddress: string,
|
|
2962
|
+
* approve: boolean,
|
|
2963
|
+
* overridePolicy: boolean,
|
|
2964
|
+
* dryRun: boolean,
|
|
2965
|
+
* diff: boolean,
|
|
2966
|
+
* filterType: string,
|
|
2967
|
+
* filterRg: string,
|
|
2968
|
+
* vpnConfirmed: boolean,
|
|
2969
|
+
* tenant: string,
|
|
2970
|
+
* parseError: boolean,
|
|
2971
|
+
* unknownFlag: string
|
|
2972
|
+
* }} AzureOpts
|
|
2973
|
+
*/
|
|
2974
|
+
|
|
2975
|
+
/**
|
|
2976
|
+
* @param {string[]} argv
|
|
2977
|
+
* @returns {DashboardOpts}
|
|
2978
|
+
*/
|
|
2979
|
+
function parseDashboardArgs(argv) {
|
|
2980
|
+
/** @type {DashboardOpts} */
|
|
2981
|
+
const out = {
|
|
2982
|
+
help: false,
|
|
2983
|
+
dir: process.cwd(),
|
|
2984
|
+
port: 4173,
|
|
2985
|
+
noOpen: false,
|
|
2986
|
+
readOnly: false,
|
|
2987
|
+
dumpContext: false,
|
|
2988
|
+
activeSession: null,
|
|
2989
|
+
parseError: false,
|
|
2990
|
+
unknownFlag: '',
|
|
2991
|
+
};
|
|
2992
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2993
|
+
const a = argv[i];
|
|
2994
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
2995
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
2996
|
+
else if (a === '--port' && argv[i + 1]) out.port = Number(argv[++i]) || 4173;
|
|
2997
|
+
else if (a === '--no-open') out.noOpen = true;
|
|
2998
|
+
else if (a === '--read-only') out.readOnly = true;
|
|
2999
|
+
else if (a === '--dump-context') out.dumpContext = true;
|
|
3000
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3001
|
+
else if (!a.startsWith('-') && i === 0) out.dir = path.resolve(a);
|
|
3002
|
+
else {
|
|
3003
|
+
out.parseError = true;
|
|
3004
|
+
out.unknownFlag = a;
|
|
3005
|
+
break;
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
return out;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
/**
|
|
3012
|
+
* @param {string[]} argv
|
|
3013
|
+
* @returns {RuntimeOpts}
|
|
3014
|
+
*/
|
|
3015
|
+
function parseRuntimeArgs(argv) {
|
|
3016
|
+
/** @type {RuntimeOpts} */
|
|
3017
|
+
const out = {
|
|
3018
|
+
help: false,
|
|
3019
|
+
dir: process.cwd(),
|
|
3020
|
+
action: 'status',
|
|
3021
|
+
activeSession: null,
|
|
3022
|
+
wave: null,
|
|
3023
|
+
task: '',
|
|
3024
|
+
mode: '',
|
|
3025
|
+
reason: '',
|
|
3026
|
+
parseError: false,
|
|
3027
|
+
unknownFlag: '',
|
|
3028
|
+
};
|
|
3029
|
+
const positionals = [];
|
|
3030
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
3031
|
+
const a = argv[i];
|
|
3032
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
3033
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
3034
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3035
|
+
else if (a === '--wave' && argv[i + 1]) out.wave = Number(argv[++i]);
|
|
3036
|
+
else if (a === '--task' && argv[i + 1]) out.task = String(argv[++i]);
|
|
3037
|
+
else if (a === '--mode' && argv[i + 1]) out.mode = String(argv[++i]);
|
|
3038
|
+
else if (a === '--reason' && argv[i + 1]) out.reason = String(argv[++i]);
|
|
3039
|
+
else if (!a.startsWith('-')) positionals.push(a);
|
|
3040
|
+
else {
|
|
3041
|
+
out.parseError = true;
|
|
3042
|
+
out.unknownFlag = a;
|
|
3043
|
+
break;
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
if (positionals[0]) out.action = positionals[0];
|
|
3047
|
+
if (positionals[1]) out.dir = path.resolve(positionals[1]);
|
|
3048
|
+
if (Number.isNaN(out.wave)) out.wave = null;
|
|
3049
|
+
return out;
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
/**
|
|
3053
|
+
* @param {string[]} argv
|
|
3054
|
+
* @returns {AzureOpts}
|
|
3055
|
+
*/
|
|
3056
|
+
function parseAzureArgs(argv) {
|
|
3057
|
+
/** @type {AzureOpts} */
|
|
3058
|
+
const out = {
|
|
3059
|
+
help: false,
|
|
3060
|
+
dir: process.cwd(),
|
|
3061
|
+
scope: 'doctor',
|
|
3062
|
+
action: '',
|
|
3063
|
+
query: '',
|
|
3064
|
+
activeSession: null,
|
|
3065
|
+
subscription: '',
|
|
3066
|
+
kind: '',
|
|
3067
|
+
resourceGroup: '',
|
|
3068
|
+
name: '',
|
|
3069
|
+
namespace: '',
|
|
3070
|
+
topicName: '',
|
|
3071
|
+
subscriptionName: '',
|
|
3072
|
+
sourceResourceId: '',
|
|
3073
|
+
endpoint: '',
|
|
3074
|
+
location: '',
|
|
3075
|
+
server: '',
|
|
3076
|
+
database: '',
|
|
3077
|
+
adminUser: '',
|
|
3078
|
+
adminPasswordEnv: '',
|
|
3079
|
+
startIpAddress: '',
|
|
3080
|
+
endIpAddress: '',
|
|
3081
|
+
approve: false,
|
|
3082
|
+
overridePolicy: false,
|
|
3083
|
+
dryRun: false,
|
|
3084
|
+
diff: false,
|
|
3085
|
+
filterType: '',
|
|
3086
|
+
filterRg: '',
|
|
3087
|
+
vpnConfirmed: false,
|
|
3088
|
+
tenant: '',
|
|
3089
|
+
parseError: false,
|
|
3090
|
+
unknownFlag: '',
|
|
3091
|
+
};
|
|
3092
|
+
const positionals = [];
|
|
3093
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
3094
|
+
const a = argv[i];
|
|
3095
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
3096
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
3097
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3098
|
+
else if (a === '--subscription' && argv[i + 1]) out.subscription = String(argv[++i]);
|
|
3099
|
+
else if (a === '--kind' && argv[i + 1]) out.kind = String(argv[++i]);
|
|
3100
|
+
else if (a === '--resource-group' && argv[i + 1]) out.resourceGroup = String(argv[++i]);
|
|
3101
|
+
else if (a === '--name' && argv[i + 1]) out.name = String(argv[++i]);
|
|
3102
|
+
else if (a === '--namespace' && argv[i + 1]) out.namespace = String(argv[++i]);
|
|
3103
|
+
else if (a === '--topic-name' && argv[i + 1]) out.topicName = String(argv[++i]);
|
|
3104
|
+
else if (a === '--subscription-name' && argv[i + 1]) out.subscriptionName = String(argv[++i]);
|
|
3105
|
+
else if (a === '--source-resource-id' && argv[i + 1]) out.sourceResourceId = String(argv[++i]);
|
|
3106
|
+
else if (a === '--endpoint' && argv[i + 1]) out.endpoint = String(argv[++i]);
|
|
3107
|
+
else if (a === '--location' && argv[i + 1]) out.location = String(argv[++i]);
|
|
3108
|
+
else if (a === '--server' && argv[i + 1]) out.server = String(argv[++i]);
|
|
3109
|
+
else if (a === '--database' && argv[i + 1]) out.database = String(argv[++i]);
|
|
3110
|
+
else if (a === '--admin-user' && argv[i + 1]) out.adminUser = String(argv[++i]);
|
|
3111
|
+
else if (a === '--admin-password-env' && argv[i + 1]) out.adminPasswordEnv = String(argv[++i]);
|
|
3112
|
+
else if (a === '--start-ip-address' && argv[i + 1]) out.startIpAddress = String(argv[++i]);
|
|
3113
|
+
else if (a === '--end-ip-address' && argv[i + 1]) out.endIpAddress = String(argv[++i]);
|
|
3114
|
+
else if (a === '--approve') out.approve = true;
|
|
3115
|
+
else if (a === '--override-policy') out.overridePolicy = true;
|
|
3116
|
+
else if (a === '--dry-run') out.dryRun = true;
|
|
3117
|
+
else if (a === '--diff') out.diff = true;
|
|
3118
|
+
else if (a === '--type' && argv[i + 1]) out.filterType = String(argv[++i]);
|
|
3119
|
+
else if (a === '--filter-rg' && argv[i + 1]) out.filterRg = String(argv[++i]);
|
|
3120
|
+
else if (a === '--vpn-confirmed') out.vpnConfirmed = true;
|
|
3121
|
+
else if (a === '--tenant' && argv[i + 1]) out.tenant = String(argv[++i]);
|
|
3122
|
+
else if (!a.startsWith('-')) positionals.push(a);
|
|
3123
|
+
else {
|
|
3124
|
+
out.parseError = true;
|
|
3125
|
+
out.unknownFlag = a;
|
|
3126
|
+
break;
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
if (positionals[0]) out.scope = String(positionals[0]);
|
|
3130
|
+
if (positionals[1]) out.action = String(positionals[1]);
|
|
3131
|
+
if (out.scope === 'find') {
|
|
3132
|
+
out.query = positionals.slice(1).join(' ').trim();
|
|
3133
|
+
} else if (!out.action && positionals[2]) {
|
|
3134
|
+
out.query = positionals.slice(2).join(' ').trim();
|
|
3135
|
+
} else if (positionals[2]) {
|
|
3136
|
+
out.query = positionals.slice(2).join(' ').trim();
|
|
3137
|
+
}
|
|
3138
|
+
return out;
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
/**
|
|
3142
|
+
* @param {DashboardOpts} opts
|
|
3143
|
+
*/
|
|
3144
|
+
async function runDashboard(opts) {
|
|
3145
|
+
assertNotWslWindowsNode();
|
|
3146
|
+
if (!fs.existsSync(opts.dir)) {
|
|
3147
|
+
console.error(`${yellow}Diretório não encontrado: ${opts.dir}${reset}`);
|
|
3148
|
+
process.exit(1);
|
|
3149
|
+
}
|
|
3150
|
+
const target = opts.dir;
|
|
3151
|
+
if (opts.dumpContext) {
|
|
3152
|
+
console.log(JSON.stringify(oxeDashboard.loadDashboardContext(target, { activeSession: opts.activeSession }), null, 2));
|
|
3153
|
+
return;
|
|
3154
|
+
}
|
|
3155
|
+
const c = useAnsiColors();
|
|
3156
|
+
printSection('OXE ▸ dashboard');
|
|
3157
|
+
const server = oxeDashboard.createDashboardServer(target, { activeSession: opts.activeSession });
|
|
3158
|
+
await new Promise((resolve, reject) => {
|
|
3159
|
+
server.once('error', reject);
|
|
3160
|
+
server.listen(opts.port, '127.0.0.1', () => resolve());
|
|
3161
|
+
});
|
|
3162
|
+
const address = server.address();
|
|
3163
|
+
const port = address && typeof address === 'object' ? address.port : opts.port;
|
|
3164
|
+
const url = `http://127.0.0.1:${port}/`;
|
|
3165
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${target}${c ? reset : ''}`);
|
|
3166
|
+
console.log(` ${c ? green : ''}URL:${c ? reset : ''} ${c ? cyan : ''}${url}${c ? reset : ''}`);
|
|
3167
|
+
if (opts.readOnly) {
|
|
3168
|
+
console.log(` ${c ? yellow : ''}Modo:${c ? reset : ''} read-only (UI visual; persistência deve ser evitada no uso desta flag)`);
|
|
3169
|
+
}
|
|
3170
|
+
if (!opts.noOpen) {
|
|
3171
|
+
try {
|
|
3172
|
+
openUrlInBrowser(url);
|
|
3173
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Browser aberto.`);
|
|
3174
|
+
} catch {
|
|
3175
|
+
console.log(` ${yellow}Não foi possível abrir o browser automaticamente.${reset}`);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
console.log(` ${dim}Pressione Ctrl+C para encerrar o servidor local.${reset}\n`);
|
|
3179
|
+
await new Promise(() => {});
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
/**
|
|
3183
|
+
* @param {RuntimeOpts} opts
|
|
3184
|
+
*/
|
|
3185
|
+
function runRuntime(opts) {
|
|
3186
|
+
const c = useAnsiColors();
|
|
3187
|
+
printSection('OXE ▸ runtime');
|
|
3188
|
+
if (!fs.existsSync(opts.dir)) {
|
|
3189
|
+
console.error(`${yellow}Diretório não encontrado: ${opts.dir}${reset}`);
|
|
3190
|
+
process.exit(1);
|
|
3191
|
+
}
|
|
3192
|
+
bootstrapOxe(opts.dir, { dryRun: false, force: false });
|
|
3193
|
+
const statePath = oxeHealth.oxePaths(opts.dir).state;
|
|
3194
|
+
const stateText = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
|
|
3195
|
+
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
3196
|
+
const p = oxeOperational.operationalPaths(opts.dir, activeSession);
|
|
3197
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3198
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
3199
|
+
|
|
3200
|
+
if (opts.action === 'status') {
|
|
3201
|
+
const current = oxeOperational.readRunState(opts.dir, activeSession);
|
|
3202
|
+
if (!current) {
|
|
3203
|
+
console.log(` ${yellow}Nenhum ACTIVE-RUN encontrado.${reset}`);
|
|
3204
|
+
return;
|
|
3205
|
+
}
|
|
3206
|
+
console.log(` ${c ? green : ''}Run:${c ? reset : ''} ${current.run_id}`);
|
|
3207
|
+
console.log(` ${c ? green : ''}Estado:${c ? reset : ''} ${current.status}`);
|
|
3208
|
+
console.log(` ${c ? green : ''}Cursor:${c ? reset : ''} onda=${current.cursor && current.cursor.wave != null ? current.cursor.wave : '—'} tarefa=${current.cursor && current.cursor.task ? current.cursor.task : '—'} modo=${current.cursor && current.cursor.mode ? current.cursor.mode : '—'}`);
|
|
3209
|
+
console.log(` ${c ? green : ''}Arquivo:${c ? reset : ''} ${path.join(p.runsDir, `${current.run_id}.json`)}`);
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
try {
|
|
3214
|
+
const next = oxeOperational.applyRuntimeAction(opts.dir, activeSession, {
|
|
3215
|
+
action: opts.action,
|
|
3216
|
+
wave: opts.wave,
|
|
3217
|
+
task: opts.task || null,
|
|
3218
|
+
mode: opts.mode || null,
|
|
3219
|
+
reason: opts.reason || '',
|
|
3220
|
+
});
|
|
3221
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Runtime atualizado.`);
|
|
3222
|
+
console.log(` ${c ? green : ''}Run:${c ? reset : ''} ${next.run_id}`);
|
|
3223
|
+
console.log(` ${c ? green : ''}Estado:${c ? reset : ''} ${next.status}`);
|
|
3224
|
+
console.log(` ${c ? green : ''}Cursor:${c ? reset : ''} onda=${next.cursor && next.cursor.wave != null ? next.cursor.wave : '—'} tarefa=${next.cursor && next.cursor.task ? next.cursor.task : '—'} modo=${next.cursor && next.cursor.mode ? next.cursor.mode : '—'}`);
|
|
3225
|
+
console.log(` ${c ? green : ''}Trace:${c ? reset : ''} ${p.events}`);
|
|
3226
|
+
} catch (err) {
|
|
3227
|
+
console.error(`${red}${err && err.message ? err.message : 'Falha ao atualizar runtime.'}${reset}`);
|
|
3228
|
+
process.exit(1);
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
/**
|
|
3233
|
+
* @param {AzureOpts} opts
|
|
3234
|
+
*/
|
|
3235
|
+
function runAzure(opts) {
|
|
3236
|
+
const c = useAnsiColors();
|
|
3237
|
+
printSection('OXE ▸ azure');
|
|
3238
|
+
if (!fs.existsSync(opts.dir)) {
|
|
3239
|
+
console.error(`${yellow}Diretório não encontrado: ${opts.dir}${reset}`);
|
|
3240
|
+
process.exit(1);
|
|
3241
|
+
}
|
|
3242
|
+
bootstrapOxe(opts.dir, { dryRun: false, force: false });
|
|
3243
|
+
oxeAzure.ensureAzureArtifacts(opts.dir);
|
|
3244
|
+
oxeAzure.ensureAzureCapabilities(opts.dir);
|
|
3245
|
+
writeCapabilitiesIndex(opts.dir);
|
|
3246
|
+
const { config } = oxeHealth.loadOxeConfigMerged(opts.dir);
|
|
3247
|
+
const statePath = oxeHealth.oxePaths(opts.dir).state;
|
|
3248
|
+
const stateText = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
|
|
3249
|
+
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
3250
|
+
const azureCfg = config.azure && typeof config.azure === 'object' ? config.azure : {};
|
|
3251
|
+
const preferredLocation = Array.isArray(azureCfg.preferred_locations) && azureCfg.preferred_locations.length
|
|
3252
|
+
? String(azureCfg.preferred_locations[0])
|
|
3253
|
+
: '';
|
|
3254
|
+
const input = {
|
|
3255
|
+
kind: opts.kind,
|
|
3256
|
+
resourceGroup: opts.resourceGroup || String(azureCfg.default_resource_group || ''),
|
|
3257
|
+
name: opts.name || opts.server,
|
|
3258
|
+
namespace: opts.namespace,
|
|
3259
|
+
topicName: opts.topicName,
|
|
3260
|
+
subscriptionName: opts.subscriptionName,
|
|
3261
|
+
sourceResourceId: opts.sourceResourceId,
|
|
3262
|
+
endpoint: opts.endpoint,
|
|
3263
|
+
location: opts.location || preferredLocation,
|
|
3264
|
+
server: opts.server,
|
|
3265
|
+
database: opts.database,
|
|
3266
|
+
adminUser: opts.adminUser,
|
|
3267
|
+
adminPasswordEnv: opts.adminPasswordEnv || 'AZURE_SQL_ADMIN_PASSWORD',
|
|
3268
|
+
startIpAddress: opts.startIpAddress,
|
|
3269
|
+
endIpAddress: opts.endIpAddress,
|
|
3270
|
+
env: process.env,
|
|
3271
|
+
};
|
|
3272
|
+
|
|
3273
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3274
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
3275
|
+
|
|
3276
|
+
try {
|
|
3277
|
+
if (opts.scope === 'status') {
|
|
3278
|
+
const st = oxeAzure.statusAzure(opts.dir, config);
|
|
3279
|
+
const loginColor = st.loginActive ? green : red;
|
|
3280
|
+
const invColor = st.inventoryPresent && !st.inventoryStale ? green : yellow;
|
|
3281
|
+
console.log(` ${c ? (st.cliInstalled ? green : red) : ''}CLI:${c ? reset : ''} ${st.cliInstalled ? `Azure CLI ${st.cliVersion || 'detectada'}` : 'não instalada'}`);
|
|
3282
|
+
console.log(` ${c ? loginColor : ''}Login:${c ? reset : ''} ${st.loginActive ? 'ativo' : 'ausente'}`);
|
|
3283
|
+
console.log(` ${c ? green : ''}Sub:${c ? reset : ''} ${st.subscription || '—'}`);
|
|
3284
|
+
console.log(` ${c ? invColor : ''}Inventário:${c ? reset : ''} ${st.inventoryPresent ? `${st.inventoryAgeHours !== null ? `${st.inventoryAgeHours}h atrás` : 'presente'}${st.inventoryStale ? ' (stale)' : ''}` : 'ausente'}`);
|
|
3285
|
+
if (st.inventorySummary) {
|
|
3286
|
+
console.log(` ${c ? green : ''}Recursos:${c ? reset : ''} total=${st.inventorySummary.total} sb=${st.inventorySummary.servicebus} eg=${st.inventorySummary.eventgrid} sql=${st.inventorySummary.sql}`);
|
|
3287
|
+
}
|
|
3288
|
+
if (st.pendingOperations > 0) {
|
|
3289
|
+
console.log(` ${yellow}Ops pendentes:${reset} ${st.pendingOperations} (${st.pendingOperationIds.join(', ')})`);
|
|
3290
|
+
}
|
|
3291
|
+
if (st.vpnRequired) {
|
|
3292
|
+
console.log(` ${yellow}VPN:${reset} requerida pela configuração do projeto`);
|
|
3293
|
+
}
|
|
3294
|
+
return;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
if (opts.scope === 'operations') {
|
|
3298
|
+
const ops = oxeAzure.listAzureOperations(opts.dir);
|
|
3299
|
+
if (!ops.length) {
|
|
3300
|
+
console.log(` ${c ? dim : ''}Nenhuma operação registrada.${c ? reset : ''}`);
|
|
3301
|
+
return;
|
|
3302
|
+
}
|
|
3303
|
+
for (const op of ops) {
|
|
3304
|
+
const phaseColor = op.phase === 'applied' ? green : op.phase === 'waiting_approval' ? yellow : op.phase === 'failed' ? red : dim;
|
|
3305
|
+
console.log(` ${c ? phaseColor : ''}${op.phase}${c ? reset : ''} · ${op.operation_id} · ${op.domain}/${op.kind} · ${op.summary || '—'}`);
|
|
3306
|
+
}
|
|
3307
|
+
return;
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
if (opts.scope === 'doctor') {
|
|
3311
|
+
const report = oxeAzure.azureDoctor(opts.dir, config, {
|
|
3312
|
+
autoInstall: Boolean(azureCfg.resource_graph_auto_install !== false),
|
|
3313
|
+
});
|
|
3314
|
+
console.log(` ${c ? green : ''}CLI:${c ? reset : ''} ${report.authStatus.installed ? `Azure CLI ${report.authStatus.version || 'detectada'}` : 'não instalada'}`);
|
|
3315
|
+
console.log(` ${c ? green : ''}Login:${c ? reset : ''} ${report.authStatus.login_active ? 'ativo' : 'ausente'}`);
|
|
3316
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${report.profile.subscription_name || report.profile.subscription_id || '—'}`);
|
|
3317
|
+
console.log(` ${c ? green : ''}Inventário:${c ? reset : ''} ${report.inventory && report.inventory.synced_at ? `sync em ${report.inventory.synced_at}` : 'ausente'}`);
|
|
3318
|
+
if (report.warnings.length) {
|
|
3319
|
+
for (const warning of report.warnings) {
|
|
3320
|
+
console.log(` ${yellow}AVISO${reset} ${warning}`);
|
|
3321
|
+
}
|
|
3322
|
+
} else {
|
|
3323
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Contexto Azure saudável.`);
|
|
3324
|
+
}
|
|
3325
|
+
if (!report.authStatus.installed || !report.authStatus.login_active || !report.profile.subscription_id) {
|
|
3326
|
+
process.exit(1);
|
|
3327
|
+
}
|
|
3328
|
+
return;
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
if (opts.scope === 'auth') {
|
|
3332
|
+
if (opts.action === 'login') {
|
|
3333
|
+
const context = oxeAzure.loginAzure(opts.dir, { inherit: true, tenant: opts.tenant || undefined });
|
|
3334
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Login Azure concluído.`);
|
|
3335
|
+
console.log(` ${c ? green : ''}Conta:${c ? reset : ''} ${context.authStatus.user || '—'}`);
|
|
3336
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${context.profile.subscription_name || context.profile.subscription_id || '—'}`);
|
|
3337
|
+
return;
|
|
3338
|
+
}
|
|
3339
|
+
if (opts.action === 'whoami' || !opts.action) {
|
|
3340
|
+
const context = oxeAzure.getAzureContext(opts.dir);
|
|
3341
|
+
console.log(` ${c ? green : ''}Cloud:${c ? reset : ''} ${context.profile.cloud || '—'}`);
|
|
3342
|
+
console.log(` ${c ? green : ''}Conta:${c ? reset : ''} ${context.authStatus.user || '—'}`);
|
|
3343
|
+
console.log(` ${c ? green : ''}Tipo:${c ? reset : ''} ${context.authStatus.user_type || context.profile.auth_mode || '—'}`);
|
|
3344
|
+
console.log(` ${c ? green : ''}Tenant:${c ? reset : ''} ${context.profile.tenant_id || '—'}`);
|
|
3345
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${context.profile.subscription_name || context.profile.subscription_id || '—'}`);
|
|
3346
|
+
console.log(` ${c ? green : ''}Resource Graph:${c ? reset : ''} ${context.authStatus.resource_graph_enabled ? 'habilitado' : 'ausente'}`);
|
|
3347
|
+
return;
|
|
3348
|
+
}
|
|
3349
|
+
if (opts.action === 'set-subscription') {
|
|
3350
|
+
if (!opts.subscription) {
|
|
3351
|
+
console.error(`${red}Informe --subscription <id|nome>.${reset}`);
|
|
3352
|
+
process.exit(1);
|
|
3353
|
+
}
|
|
3354
|
+
const context = oxeAzure.setAzureSubscription(opts.dir, opts.subscription);
|
|
3355
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Subscription selecionada.`);
|
|
3356
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${context.profile.subscription_name || context.profile.subscription_id || '—'}`);
|
|
3357
|
+
return;
|
|
3358
|
+
}
|
|
3359
|
+
throw new Error(`Subcomando Azure auth desconhecido: ${opts.action || '—'}`);
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
if (opts.scope === 'sync') {
|
|
3363
|
+
const synced = oxeAzure.syncAzureInventory(opts.dir, {
|
|
3364
|
+
autoInstall: Boolean(azureCfg.resource_graph_auto_install !== false),
|
|
3365
|
+
diff: opts.diff,
|
|
3366
|
+
});
|
|
3367
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Inventário Azure sincronizado.`);
|
|
3368
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${synced.profile.subscription_name || synced.profile.subscription_id || '—'}`);
|
|
3369
|
+
console.log(` ${c ? green : ''}Resumo:${c ? reset : ''} total=${synced.inventory.summary.total} servicebus=${synced.inventory.summary.servicebus} eventgrid=${synced.inventory.summary.eventgrid} sql=${synced.inventory.summary.sql}`);
|
|
3370
|
+
console.log(` ${c ? green : ''}Artefato:${c ? reset : ''} ${synced.paths.inventory}`);
|
|
3371
|
+
if (synced.diff) {
|
|
3372
|
+
const d = synced.diff;
|
|
3373
|
+
console.log(` ${c ? green : ''}Diff:${c ? reset : ''} +${d.added.length} adicionados · -${d.removed.length} removidos · ${d.unchanged} inalterados`);
|
|
3374
|
+
for (const item of d.added) console.log(` ${c ? green : ''}+${c ? reset : ''} ${item.name} · ${item.type} · ${item.resourceGroup || '—'}`);
|
|
3375
|
+
for (const item of d.removed) console.log(` ${c ? red : ''}-${c ? reset : ''} ${item.name} · ${item.type} · ${item.resourceGroup || '—'}`);
|
|
3376
|
+
}
|
|
3377
|
+
return;
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
if (opts.scope === 'find') {
|
|
3381
|
+
const filters = {};
|
|
3382
|
+
if (opts.filterType) filters.type = opts.filterType;
|
|
3383
|
+
if (opts.filterRg) filters.resourceGroup = opts.filterRg;
|
|
3384
|
+
const matches = oxeAzure.searchAzureInventory(opts.dir, opts.query, filters);
|
|
3385
|
+
if (!matches.length) {
|
|
3386
|
+
console.log(` ${yellow}Nenhum recurso encontrado no inventário local.${reset}`);
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
for (const item of matches) {
|
|
3390
|
+
console.log(` ${c ? dim : ''}•${c ? reset : ''} ${item.name} · ${item.type} · ${item.resourceGroup || '—'} · ${item.location || '—'}`);
|
|
3391
|
+
}
|
|
3392
|
+
return;
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
if (!['servicebus', 'eventgrid', 'sql'].includes(opts.scope)) {
|
|
3396
|
+
throw new Error(`Escopo Azure desconhecido: ${opts.scope}. Use: doctor | status | auth | sync | find | operations | servicebus | eventgrid | sql`);
|
|
3397
|
+
}
|
|
3398
|
+
if (!opts.action) {
|
|
3399
|
+
throw new Error(`Informe a ação para ${opts.scope}: list | show | plan | apply`);
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
if (opts.action === 'list' || opts.action === 'show') {
|
|
3403
|
+
const result = oxeAzure.executeAzureRead(opts.dir, activeSession, opts.scope, opts.action, input);
|
|
3404
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
if (opts.action === 'plan') {
|
|
3408
|
+
const planned = oxeAzure.planAzureOperation(opts.dir, activeSession, opts.scope, input);
|
|
3409
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Operação Azure planejada.`);
|
|
3410
|
+
console.log(` ${c ? green : ''}Operation ID:${c ? reset : ''} ${planned.operation.operation_id}`);
|
|
3411
|
+
console.log(` ${c ? green : ''}Checkpoint:${c ? reset : ''} ${planned.operation.checkpoint_id}`);
|
|
3412
|
+
console.log(` ${c ? green : ''}Comando:${c ? reset : ''} ${planned.operation.command_display_redacted}`);
|
|
3413
|
+
console.log(` ${c ? green : ''}Resumo:${c ? reset : ''} ${planned.operation.summary}`);
|
|
3414
|
+
console.log(` ${c ? green : ''}Artefatos:${c ? reset : ''} ${planned.files.jsonPath} · ${planned.files.mdPath}`);
|
|
3415
|
+
return;
|
|
3416
|
+
}
|
|
3417
|
+
if (opts.action === 'apply') {
|
|
3418
|
+
const applied = oxeAzure.applyAzureOperation(opts.dir, activeSession, opts.scope, input, {
|
|
3419
|
+
approve: opts.approve,
|
|
3420
|
+
overridePolicy: opts.overridePolicy,
|
|
3421
|
+
dryRun: opts.dryRun,
|
|
3422
|
+
vpnRequired: Boolean(azureCfg.vpn_required),
|
|
3423
|
+
vpnConfirmed: opts.vpnConfirmed,
|
|
3424
|
+
});
|
|
3425
|
+
if (applied.dryRun) {
|
|
3426
|
+
console.log(` ${yellow}[dry-run]${reset} Validação OK — nenhuma alteração foi feita.`);
|
|
3427
|
+
console.log(` ${c ? green : ''}Comando:${c ? reset : ''} ${applied.commandPreview}`);
|
|
3428
|
+
console.log(` ${c ? green : ''}Resumo:${c ? reset : ''} ${applied.operation.summary}`);
|
|
3429
|
+
return;
|
|
3430
|
+
}
|
|
3431
|
+
if (!applied.approved) {
|
|
3432
|
+
console.log(` ${yellow}Checkpoint aberto antes da mutação.${reset}`);
|
|
3433
|
+
console.log(` ${c ? green : ''}Operation ID:${c ? reset : ''} ${applied.operation.operation_id}`);
|
|
3434
|
+
console.log(` ${c ? green : ''}Checkpoint:${c ? reset : ''} ${applied.checkpoint_id}`);
|
|
3435
|
+
console.log(` ${c ? green : ''}Resumo:${c ? reset : ''} ${applied.operation.summary}`);
|
|
3436
|
+
console.log(` ${c ? green : ''}Reexecute:${c ? reset : ''} npx oxe-cc azure ${opts.scope} apply --approve --kind ${applied.operation.kind} --resource-group ${applied.operation.resource_group}`);
|
|
3437
|
+
return;
|
|
3438
|
+
}
|
|
3439
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Operação Azure aplicada.`);
|
|
3440
|
+
console.log(` ${c ? green : ''}Operation ID:${c ? reset : ''} ${applied.operation.operation_id}`);
|
|
3441
|
+
console.log(` ${c ? green : ''}Artefatos:${c ? reset : ''} ${applied.files.jsonPath} · ${applied.files.mdPath}`);
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3444
|
+
|
|
3445
|
+
throw new Error(`Ação Azure desconhecida: ${opts.action}`);
|
|
3446
|
+
} catch (err) {
|
|
3447
|
+
console.error(`${red}${err && err.message ? err.message : 'Falha no provider Azure.'}${reset}`);
|
|
3448
|
+
process.exit(1);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
function capabilityManifestPath(target, id) {
|
|
3453
|
+
return path.join(target, '.oxe', 'capabilities', id, 'CAPABILITY.md');
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3456
|
+
function listLocalCapabilities(target) {
|
|
3457
|
+
const dir = path.join(target, '.oxe', 'capabilities');
|
|
3458
|
+
if (!fs.existsSync(dir)) return [];
|
|
3459
|
+
return fs
|
|
3460
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
3461
|
+
.filter((e) => e.isDirectory() && fs.existsSync(path.join(dir, e.name, 'CAPABILITY.md')))
|
|
3462
|
+
.map((e) => e.name)
|
|
3463
|
+
.sort();
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3466
|
+
function renderCapabilitiesIndex(target) {
|
|
3467
|
+
const template = ['# OXE — Capabilities Instaladas', '', '> Catálogo local de capabilities do projeto. Cada capability vive em `.oxe/capabilities/<id>/`.', '', '| ID | Tipo | Status | Escopo | Política | Side effects | Requer env | Evidência | Resumo |', '|----|------|--------|--------|-----------|--------------|------------|-----------|--------|'];
|
|
3468
|
+
const catalog = oxeOperational.readCapabilityCatalog(target);
|
|
3469
|
+
if (!catalog.length) {
|
|
3470
|
+
template.push('| (vazio) | — | — | — | — | — | — | — | Nenhuma capability instalada |');
|
|
3471
|
+
return template.join('\n') + '\n';
|
|
3472
|
+
}
|
|
3473
|
+
for (const cap of catalog) {
|
|
3474
|
+
template.push(`| ${cap.id} | ${cap.type} | ${cap.status} | ${cap.scope} | ${cap.approvalPolicy || '—'} | ${(cap.sideEffects || []).join(', ') || '—'} | ${(cap.requiresEnv || []).join(', ') || '—'} | ${(cap.evidenceOutputs || []).join(', ') || '—'} | ${cap.description} |`);
|
|
3475
|
+
}
|
|
3476
|
+
return template.join('\n') + '\n';
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
function writeCapabilitiesIndex(target) {
|
|
3480
|
+
const dest = path.join(target, '.oxe', 'CAPABILITIES.md');
|
|
3481
|
+
fs.writeFileSync(dest, renderCapabilitiesIndex(target), 'utf8');
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
/**
|
|
3485
|
+
* @param {CapabilityOpts} opts
|
|
3486
|
+
*/
|
|
3487
|
+
function runCapabilities(opts) {
|
|
3488
|
+
const c = useAnsiColors();
|
|
3489
|
+
printSection('OXE ▸ capabilities');
|
|
3490
|
+
if (!fs.existsSync(opts.dir)) {
|
|
3491
|
+
console.error(`${yellow}Diretório não encontrado: ${opts.dir}${reset}`);
|
|
3492
|
+
process.exit(1);
|
|
3493
|
+
}
|
|
3494
|
+
bootstrapOxe(opts.dir, { dryRun: false, force: false });
|
|
3495
|
+
const capsDir = path.join(opts.dir, '.oxe', 'capabilities');
|
|
3496
|
+
ensureDir(capsDir);
|
|
3497
|
+
if (opts.action === 'list') {
|
|
3498
|
+
const ids = listLocalCapabilities(opts.dir);
|
|
3499
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3500
|
+
if (!ids.length) {
|
|
3501
|
+
console.log(` ${yellow}Nenhuma capability instalada.${reset}`);
|
|
3502
|
+
} else {
|
|
3503
|
+
for (const id of ids) console.log(` ${c ? dim : ''}•${c ? reset : ''} ${id}`);
|
|
3504
|
+
}
|
|
3505
|
+
return;
|
|
3506
|
+
}
|
|
3507
|
+
if (!opts.id && (opts.action === 'install' || opts.action === 'remove')) {
|
|
3508
|
+
console.error(`${red}Informe o ID da capability.${reset}`);
|
|
3509
|
+
process.exit(1);
|
|
3510
|
+
}
|
|
3511
|
+
if (opts.action === 'install') {
|
|
3512
|
+
const safeId = opts.id.trim().toLowerCase();
|
|
3513
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(safeId)) {
|
|
3514
|
+
console.error(`${red}ID inválido para capability:${reset} ${opts.id}`);
|
|
3515
|
+
process.exit(1);
|
|
3516
|
+
}
|
|
3517
|
+
const dir = path.join(capsDir, safeId);
|
|
3518
|
+
ensureDir(dir);
|
|
3519
|
+
const manifest = capabilityManifestPath(opts.dir, safeId);
|
|
3520
|
+
if (!fs.existsSync(manifest)) {
|
|
3521
|
+
const src = path.join(PKG_ROOT, 'oxe', 'templates', 'CAPABILITY.template.md');
|
|
3522
|
+
let raw = fs.readFileSync(src, 'utf8');
|
|
3523
|
+
raw = raw.replace(/^id:\s*sample-capability$/m, `id: ${safeId}`);
|
|
3524
|
+
fs.writeFileSync(manifest, raw, 'utf8');
|
|
3525
|
+
}
|
|
3526
|
+
writeCapabilitiesIndex(opts.dir);
|
|
3527
|
+
oxeOperational.appendEvent(opts.dir, null, { type: 'capability_installed', payload: { capability_id: safeId } });
|
|
3528
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Capability instalada: ${safeId}`);
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
if (opts.action === 'remove') {
|
|
3532
|
+
fs.rmSync(path.join(capsDir, opts.id), { recursive: true, force: true });
|
|
3533
|
+
writeCapabilitiesIndex(opts.dir);
|
|
3534
|
+
oxeOperational.appendEvent(opts.dir, null, { type: 'capability_removed', payload: { capability_id: opts.id } });
|
|
3535
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Capability removida: ${opts.id}`);
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
if (opts.action === 'update') {
|
|
3539
|
+
writeCapabilitiesIndex(opts.dir);
|
|
3540
|
+
oxeOperational.appendEvent(opts.dir, null, { type: 'capability_index_refreshed' });
|
|
3541
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Índice de capabilities atualizado.`);
|
|
3542
|
+
return;
|
|
3543
|
+
}
|
|
3544
|
+
console.error(`${red}Ação desconhecida:${reset} ${opts.action}`);
|
|
3545
|
+
process.exit(1);
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
/**
|
|
3549
|
+
* @param {string[]} argv
|
|
3550
|
+
* @returns {{ help: boolean, dir: string, visual: boolean, activeSession: string|null, parseError: boolean, unknownFlag: string }}
|
|
3551
|
+
*/
|
|
3552
|
+
function parsePlanArgs(argv) {
|
|
3553
|
+
const out = { help: false, dir: process.cwd(), visual: false, activeSession: null, parseError: false, unknownFlag: '' };
|
|
3554
|
+
for (let i = 0; i < argv.length; i++) {
|
|
3555
|
+
const a = argv[i];
|
|
3556
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
3557
|
+
else if (a === '--visual') out.visual = true;
|
|
3558
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
3559
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3560
|
+
else if (!a.startsWith('-') && i === 0) out.dir = path.resolve(a);
|
|
3561
|
+
else { out.parseError = true; out.unknownFlag = a; break; }
|
|
3562
|
+
}
|
|
3563
|
+
return out;
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
/**
|
|
3567
|
+
* @param {string[]} argv
|
|
3568
|
+
* @returns {{ help: boolean, dir: string, matrix: boolean, activeSession: string|null, parseError: boolean, unknownFlag: string }}
|
|
3569
|
+
*/
|
|
3570
|
+
function parseVerifyArgs(argv) {
|
|
3571
|
+
const out = { help: false, dir: process.cwd(), matrix: false, activeSession: null, parseError: false, unknownFlag: '' };
|
|
3572
|
+
for (let i = 0; i < argv.length; i++) {
|
|
3573
|
+
const a = argv[i];
|
|
3574
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
3575
|
+
else if (a === '--matrix') out.matrix = true;
|
|
3576
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
3577
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3578
|
+
else if (!a.startsWith('-') && i === 0) out.dir = path.resolve(a);
|
|
3579
|
+
else { out.parseError = true; out.unknownFlag = a; break; }
|
|
3580
|
+
}
|
|
3581
|
+
return out;
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3584
|
+
/**
|
|
3585
|
+
* Imprime grafo ASCII de ondas e tarefas lido do PLAN.md.
|
|
3586
|
+
* @param {{ dir: string, activeSession: string|null }} opts
|
|
3587
|
+
*/
|
|
3588
|
+
function runPlanVisual(opts) {
|
|
3589
|
+
const c = useAnsiColors();
|
|
3590
|
+
printSection('OXE ▸ plan --visual');
|
|
3591
|
+
const statePath = oxeHealth.oxePaths(opts.dir).state;
|
|
3592
|
+
const stateText = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
|
|
3593
|
+
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
3594
|
+
const sp = oxeHealth.scopedOxePaths(opts.dir, activeSession);
|
|
3595
|
+
const planPath = sp.plan || oxeHealth.oxePaths(opts.dir).plan;
|
|
3596
|
+
|
|
3597
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3598
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
3599
|
+
|
|
3600
|
+
if (!fs.existsSync(planPath)) {
|
|
3601
|
+
console.log(`\n ${yellow}PLAN.md não encontrado em ${planPath}${reset}`);
|
|
3602
|
+
console.log(` ${dim}Rode /oxe-plan para criar o plano.${reset}\n`);
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
const planMd = fs.readFileSync(planPath, 'utf8');
|
|
3607
|
+
const plan = oxeDashboard.parsePlan(planMd);
|
|
3608
|
+
|
|
3609
|
+
if (!plan.waves.length) {
|
|
3610
|
+
console.log(`\n ${yellow}Nenhuma onda encontrada no PLAN.md.${reset}\n`);
|
|
3611
|
+
return;
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3614
|
+
console.log('');
|
|
3615
|
+
for (const wave of plan.waves) {
|
|
3616
|
+
const waveLabel = ` ${c ? yellow : ''}Onda ${wave.wave}${c ? reset : ''}`;
|
|
3617
|
+
for (let i = 0; i < wave.tasks.length; i++) {
|
|
3618
|
+
const task = wave.tasks[i];
|
|
3619
|
+
const deps = task.dependsOn.length ? ` ${c ? dim : ''}← ${task.dependsOn.join(', ')}${c ? reset : ''}` : '';
|
|
3620
|
+
const complexity = task.complexity ? ` ${c ? dim : ''}[${task.complexity}]${c ? reset : ''}` : '';
|
|
3621
|
+
if (i === 0) {
|
|
3622
|
+
console.log(`${waveLabel} ${c ? cyan : ''}${task.id}${c ? reset : ''} — ${task.title}${complexity}${deps}`);
|
|
3623
|
+
} else {
|
|
3624
|
+
console.log(`${''.padEnd(waveLabel.replace(/\x1b\[[0-9;]*m/g, '').length + 3)}${c ? cyan : ''}${task.id}${c ? reset : ''} — ${task.title}${complexity}${deps}`);
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
console.log('');
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
const total = plan.totalTasks;
|
|
3631
|
+
const waveCount = plan.waves.length;
|
|
3632
|
+
console.log(` ${c ? dim : ''}${total} tarefa${total !== 1 ? 's' : ''} · ${waveCount} onda${waveCount !== 1 ? 's' : ''}${c ? reset : ''}`);
|
|
3633
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} plan --visual concluído.\n`);
|
|
3634
|
+
}
|
|
3635
|
+
|
|
3636
|
+
/**
|
|
3637
|
+
* Imprime tabela ANSI spec → plan → verify (coverage matrix).
|
|
3638
|
+
* @param {{ dir: string, activeSession: string|null }} opts
|
|
3639
|
+
*/
|
|
3640
|
+
function runVerifyMatrix(opts) {
|
|
3641
|
+
const c = useAnsiColors();
|
|
3642
|
+
printSection('OXE ▸ verify --matrix');
|
|
3643
|
+
const statePath = oxeHealth.oxePaths(opts.dir).state;
|
|
3644
|
+
const stateText = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
|
|
3645
|
+
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
3646
|
+
const sp = oxeHealth.scopedOxePaths(opts.dir, activeSession);
|
|
3647
|
+
const specPath = sp.spec || oxeHealth.oxePaths(opts.dir).spec;
|
|
3648
|
+
const planPath = sp.plan || oxeHealth.oxePaths(opts.dir).plan;
|
|
3649
|
+
const verifyPath = sp.verify || oxeHealth.oxePaths(opts.dir).verify;
|
|
3650
|
+
|
|
3651
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3652
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
3653
|
+
|
|
3654
|
+
const specMd = fs.existsSync(specPath) ? fs.readFileSync(specPath, 'utf8') : '';
|
|
3655
|
+
const planMd = fs.existsSync(planPath) ? fs.readFileSync(planPath, 'utf8') : '';
|
|
3656
|
+
const verifyMd = fs.existsSync(verifyPath) ? fs.readFileSync(verifyPath, 'utf8') : '';
|
|
3657
|
+
|
|
3658
|
+
const spec = oxeDashboard.parseSpec(specMd);
|
|
3659
|
+
const plan = oxeDashboard.parsePlan(planMd);
|
|
3660
|
+
const verify = oxeDashboard.parseVerify(verifyMd);
|
|
3661
|
+
const matrix = oxeDashboard.buildCoverageMatrix(spec, plan, verify);
|
|
3662
|
+
|
|
3663
|
+
if (!matrix.length) {
|
|
3664
|
+
if (!spec.criteria.length) {
|
|
3665
|
+
console.log(`\n ${yellow}Nenhum critério A* encontrado no SPEC.md.${reset}`);
|
|
3666
|
+
console.log(` ${dim}Rode /oxe-spec para criar a especificação.${reset}\n`);
|
|
3667
|
+
} else {
|
|
3668
|
+
console.log(`\n ${yellow}Nenhum dado disponível para a matriz.${reset}\n`);
|
|
3669
|
+
}
|
|
3670
|
+
return;
|
|
3671
|
+
}
|
|
3672
|
+
|
|
3673
|
+
const statusSymbol = (s) => {
|
|
3674
|
+
if (s === 'passed') return c ? `${green}✓${reset}` : '✓';
|
|
3675
|
+
if (s === 'failed') return c ? `${red}✗${reset}` : '✗';
|
|
3676
|
+
return c ? `${dim}—${reset}` : '—';
|
|
3677
|
+
};
|
|
3678
|
+
const planSymbol = (covered) => covered ? (c ? `${green}✓${reset}` : '✓') : (c ? `${dim}✗${reset}` : '✗');
|
|
3679
|
+
|
|
3680
|
+
// Compute column widths
|
|
3681
|
+
const colId = Math.max(9, ...matrix.map((r) => r.id.length)) + 2;
|
|
3682
|
+
const colTasks = Math.max(12, ...matrix.map((r) => (r.tasks.join(', ') || '—').length)) + 2;
|
|
3683
|
+
|
|
3684
|
+
const sep = ` ${'─'.repeat(colId)}┼${'─'.repeat(colTasks)}┼──────────┼──────────`;
|
|
3685
|
+
const header = ` ${' Critério'.padEnd(colId)}│${' Tarefas'.padEnd(colTasks)}│ PLAN │ VERIFY`;
|
|
3686
|
+
|
|
3687
|
+
console.log('');
|
|
3688
|
+
console.log(c ? `${dim}${header}${reset}` : header);
|
|
3689
|
+
console.log(sep);
|
|
3690
|
+
|
|
3691
|
+
let covered = 0;
|
|
3692
|
+
let verified = 0;
|
|
3693
|
+
for (const row of matrix) {
|
|
3694
|
+
const tasks = row.tasks.length ? row.tasks.join(', ') : '—';
|
|
3695
|
+
const planCell = planSymbol(row.planCovered);
|
|
3696
|
+
const verifyCell = statusSymbol(row.verifyStatus);
|
|
3697
|
+
const label = ` ${row.id}`.padEnd(colId);
|
|
3698
|
+
const taskCell = ` ${tasks}`.padEnd(colTasks);
|
|
3699
|
+
console.log(`${label}│${taskCell}│ ${planCell} │ ${verifyCell}`);
|
|
3700
|
+
if (row.planCovered) covered++;
|
|
3701
|
+
if (row.verifyStatus === 'passed') verified++;
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3704
|
+
console.log(sep);
|
|
3705
|
+
const total = matrix.length;
|
|
3706
|
+
console.log(`\n ${c ? dim : ''}${covered}/${total} critérios cobertos pelo plano · ${verified}/${total} aprovados no verify${c ? reset : ''}`);
|
|
3707
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} verify --matrix concluído.\n`);
|
|
3708
|
+
}
|
|
3709
|
+
|
|
2619
3710
|
async function main() {
|
|
2620
3711
|
const argv = process.argv.slice(2);
|
|
2621
3712
|
let command = 'install';
|
|
@@ -2623,8 +3714,14 @@ async function main() {
|
|
|
2623
3714
|
argv[0] === 'doctor' ||
|
|
2624
3715
|
argv[0] === 'status' ||
|
|
2625
3716
|
argv[0] === 'init-oxe' ||
|
|
3717
|
+
argv[0] === 'dashboard' ||
|
|
3718
|
+
argv[0] === 'runtime' ||
|
|
3719
|
+
argv[0] === 'azure' ||
|
|
2626
3720
|
argv[0] === 'uninstall' ||
|
|
2627
3721
|
argv[0] === 'update' ||
|
|
3722
|
+
argv[0] === 'capabilities' ||
|
|
3723
|
+
argv[0] === 'plan' ||
|
|
3724
|
+
argv[0] === 'verify' ||
|
|
2628
3725
|
argv[0] === 'install'
|
|
2629
3726
|
) {
|
|
2630
3727
|
command = argv[0];
|
|
@@ -2691,6 +3788,108 @@ async function main() {
|
|
|
2691
3788
|
return;
|
|
2692
3789
|
}
|
|
2693
3790
|
|
|
3791
|
+
if (command === 'capabilities') {
|
|
3792
|
+
const cap = parseCapabilitiesArgs(argv);
|
|
3793
|
+
if (cap.help) {
|
|
3794
|
+
printBanner();
|
|
3795
|
+
usage();
|
|
3796
|
+
process.exit(0);
|
|
3797
|
+
}
|
|
3798
|
+
if (cap.parseError) {
|
|
3799
|
+
printBanner();
|
|
3800
|
+
console.error(`${red}Opção desconhecida:${reset} ${cap.unknownFlag}`);
|
|
3801
|
+
usage();
|
|
3802
|
+
process.exit(1);
|
|
3803
|
+
}
|
|
3804
|
+
printBanner();
|
|
3805
|
+
if (!fs.existsSync(cap.dir)) {
|
|
3806
|
+
console.error(`${yellow}Diretório não encontrado: ${cap.dir}${reset}`);
|
|
3807
|
+
process.exit(1);
|
|
3808
|
+
}
|
|
3809
|
+
runCapabilities(cap);
|
|
3810
|
+
return;
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
if (command === 'dashboard') {
|
|
3814
|
+
const d = parseDashboardArgs(argv);
|
|
3815
|
+
if (d.help) {
|
|
3816
|
+
printBanner();
|
|
3817
|
+
usage();
|
|
3818
|
+
process.exit(0);
|
|
3819
|
+
}
|
|
3820
|
+
if (d.parseError) {
|
|
3821
|
+
printBanner();
|
|
3822
|
+
console.error(`${red}Opção desconhecida:${reset} ${d.unknownFlag}`);
|
|
3823
|
+
usage();
|
|
3824
|
+
process.exit(1);
|
|
3825
|
+
}
|
|
3826
|
+
printBanner();
|
|
3827
|
+
await runDashboard(d);
|
|
3828
|
+
return;
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3831
|
+
if (command === 'runtime') {
|
|
3832
|
+
const runtime = parseRuntimeArgs(argv);
|
|
3833
|
+
if (runtime.help) {
|
|
3834
|
+
printBanner();
|
|
3835
|
+
usage();
|
|
3836
|
+
process.exit(0);
|
|
3837
|
+
}
|
|
3838
|
+
if (runtime.parseError) {
|
|
3839
|
+
printBanner();
|
|
3840
|
+
console.error(`${red}Opção desconhecida:${reset} ${runtime.unknownFlag}`);
|
|
3841
|
+
usage();
|
|
3842
|
+
process.exit(1);
|
|
3843
|
+
}
|
|
3844
|
+
printBanner();
|
|
3845
|
+
if (!fs.existsSync(runtime.dir)) {
|
|
3846
|
+
console.error(`${yellow}Diretório não encontrado: ${runtime.dir}${reset}`);
|
|
3847
|
+
process.exit(1);
|
|
3848
|
+
}
|
|
3849
|
+
runRuntime(runtime);
|
|
3850
|
+
return;
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
if (command === 'azure') {
|
|
3854
|
+
const azure = parseAzureArgs(argv);
|
|
3855
|
+
if (azure.help) {
|
|
3856
|
+
printBanner();
|
|
3857
|
+
usage();
|
|
3858
|
+
process.exit(0);
|
|
3859
|
+
}
|
|
3860
|
+
if (azure.parseError) {
|
|
3861
|
+
printBanner();
|
|
3862
|
+
console.error(`${red}Opção desconhecida:${reset} ${azure.unknownFlag}`);
|
|
3863
|
+
usage();
|
|
3864
|
+
process.exit(1);
|
|
3865
|
+
}
|
|
3866
|
+
printBanner();
|
|
3867
|
+
runAzure(azure);
|
|
3868
|
+
return;
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3871
|
+
if (command === 'plan') {
|
|
3872
|
+
const planOpts = parsePlanArgs(argv);
|
|
3873
|
+
if (planOpts.help) { printBanner(); usage(); process.exit(0); }
|
|
3874
|
+
if (planOpts.parseError) { printBanner(); console.error(`${red}Opção desconhecida:${reset} ${planOpts.unknownFlag}`); usage(); process.exit(1); }
|
|
3875
|
+
if (!planOpts.visual) { printBanner(); console.error(`${yellow}Use oxe-cc plan --visual${reset}`); process.exit(1); }
|
|
3876
|
+
printBanner();
|
|
3877
|
+
if (!fs.existsSync(planOpts.dir)) { console.error(`${yellow}Diretório não encontrado: ${planOpts.dir}${reset}`); process.exit(1); }
|
|
3878
|
+
runPlanVisual(planOpts);
|
|
3879
|
+
return;
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3882
|
+
if (command === 'verify') {
|
|
3883
|
+
const verifyOpts = parseVerifyArgs(argv);
|
|
3884
|
+
if (verifyOpts.help) { printBanner(); usage(); process.exit(0); }
|
|
3885
|
+
if (verifyOpts.parseError) { printBanner(); console.error(`${red}Opção desconhecida:${reset} ${verifyOpts.unknownFlag}`); usage(); process.exit(1); }
|
|
3886
|
+
if (!verifyOpts.matrix) { printBanner(); console.error(`${yellow}Use oxe-cc verify --matrix${reset}`); process.exit(1); }
|
|
3887
|
+
printBanner();
|
|
3888
|
+
if (!fs.existsSync(verifyOpts.dir)) { console.error(`${yellow}Diretório não encontrado: ${verifyOpts.dir}${reset}`); process.exit(1); }
|
|
3889
|
+
runVerifyMatrix(verifyOpts);
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
|
|
2694
3893
|
const opts = parseInstallArgs(argv);
|
|
2695
3894
|
|
|
2696
3895
|
if (opts.version) {
|
|
@@ -2737,7 +3936,11 @@ async function main() {
|
|
|
2737
3936
|
console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
|
|
2738
3937
|
process.exit(1);
|
|
2739
3938
|
}
|
|
2740
|
-
|
|
3939
|
+
if (opts.statusFull) {
|
|
3940
|
+
runStatusFull(target);
|
|
3941
|
+
} else {
|
|
3942
|
+
runStatus(target, { json: opts.jsonOutput, hints: opts.statusHints });
|
|
3943
|
+
}
|
|
2741
3944
|
return;
|
|
2742
3945
|
}
|
|
2743
3946
|
|
|
@@ -2753,8 +3956,8 @@ async function main() {
|
|
|
2753
3956
|
bootstrapOxe(target, { dryRun: opts.dryRun, force: opts.force });
|
|
2754
3957
|
printSummaryAndNextSteps(c0, {
|
|
2755
3958
|
bullets: opts.dryRun
|
|
2756
|
-
? ['[simulação] Seriam criados ou atualizados .oxe/STATE.md, .oxe/config.json e .oxe/
|
|
2757
|
-
: ['.oxe/STATE.md, .oxe/config.json
|
|
3959
|
+
? ['[simulação] Seriam criados ou atualizados .oxe/STATE.md, .oxe/config.json, .oxe/codebase/, .oxe/ACTIVE-RUN.json e .oxe/OXE-EVENTS.ndjson']
|
|
3960
|
+
: ['.oxe/STATE.md, .oxe/config.json, .oxe/codebase/, .oxe/ACTIVE-RUN.json e .oxe/OXE-EVENTS.ndjson (criados ou atualizados conforme --force)'],
|
|
2758
3961
|
nextSteps: [
|
|
2759
3962
|
{ desc: 'Validar o projeto:', cmd: 'npx oxe-cc doctor' },
|
|
2760
3963
|
{ desc: 'Instalar integrações IDE/CLI (se ainda não fez):', cmd: 'npx oxe-cc@latest' },
|
|
@@ -2772,6 +3975,8 @@ async function main() {
|
|
|
2772
3975
|
}
|
|
2773
3976
|
|
|
2774
3977
|
main().catch((err) => {
|
|
2775
|
-
|
|
3978
|
+
const msg = err && err.message ? err.message : String(err);
|
|
3979
|
+
console.error(`${red}Erro:${reset} ${msg}`);
|
|
3980
|
+
if (process.env.OXE_DEBUG === '1') console.error(err);
|
|
2776
3981
|
process.exit(1);
|
|
2777
3982
|
});
|