@openlife/cli 1.7.14 → 1.8.2

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.
Files changed (35) hide show
  1. package/INSTALL.md +29 -1
  2. package/dist/cli/ChatTui.js +32 -0
  3. package/dist/cli/InstallModules.js +17 -2
  4. package/dist/cli/InstallWizardV2.js +110 -0
  5. package/dist/cli/install/Multiselect.js +285 -0
  6. package/dist/cli/install/OAuthRunner.js +170 -0
  7. package/dist/cli/install/Phases.js +864 -0
  8. package/dist/cli/install/ProvidersCatalog.js +320 -0
  9. package/dist/cli/install/WizardIO.js +271 -0
  10. package/dist/cli/install/types.js +17 -0
  11. package/dist/index.js +72 -33
  12. package/dist/orchestrator/ConsequenceForecaster.js +24 -1
  13. package/dist/orchestrator/DreamGoalStore.js +130 -0
  14. package/dist/orchestrator/Gatekeeper.js +14 -0
  15. package/dist/orchestrator/Gateway.js +149 -10
  16. package/dist/orchestrator/ModelManager.js +7 -1
  17. package/dist/orchestrator/OrchestrationLoop.js +12 -0
  18. package/dist/orchestrator/ParallelOrchestrationLoop.js +12 -2
  19. package/dist/orchestrator/RuntimePolicy.js +4 -1
  20. package/dist/orchestrator/ServiceCompletionPolicy.js +15 -0
  21. package/dist/orchestrator/SynthesizerAgent.js +20 -1
  22. package/dist/orchestrator/TaskExecutor.js +53 -0
  23. package/dist/orchestrator/capability/CapabilityGenesisEngine.js +66 -11
  24. package/dist/test_capability_genesis_engine.js +1 -1
  25. package/dist/test_chat_smoke_command.js +59 -0
  26. package/dist/test_dream_goal_commands.js +76 -0
  27. package/dist/test_gateway_telegram_formatting.js +74 -0
  28. package/dist/test_install_wizard_v2.js +193 -0
  29. package/dist/test_on_demand_voice_reply.js +65 -0
  30. package/dist/test_remaining_sprints_contracts.js +103 -0
  31. package/dist/test_runtime_policy.js +25 -6
  32. package/dist/test_service_completion_policy.js +7 -0
  33. package/dist/test_subsystems_routing_governance.js +13 -3
  34. package/dist/test_task_executor_gemini_api.js +68 -0
  35. package/package.json +5 -3
@@ -9,6 +9,7 @@ class ServiceCompletionPolicy {
9
9
  'Observabilidade mínima registrada',
10
10
  'Operação real documentada'
11
11
  ];
12
+ requiredEvidenceTypes = ['provisioning', 'integration', 'e2e', 'observability', 'operation'];
12
13
  enforceCriteria(criteria) {
13
14
  const merged = [...criteria];
14
15
  for (const required of this.requiredCriteria) {
@@ -30,6 +31,20 @@ class ServiceCompletionPolicy {
30
31
  missing.push('evidência de execução e validação');
31
32
  if (!state.artifacts || state.artifacts.length === 0)
32
33
  missing.push('artefatos operacionais');
34
+ const passedEvidence = (state.serviceEvidence || []).filter((item) => item.status === 'passed');
35
+ for (const type of this.requiredEvidenceTypes) {
36
+ const evidence = passedEvidence.find((item) => item.type === type);
37
+ if (!evidence) {
38
+ missing.push(`serviceEvidence.${type}`);
39
+ continue;
40
+ }
41
+ if (!evidence.summary || evidence.summary.trim().length === 0) {
42
+ missing.push(`serviceEvidence.${type}.summary`);
43
+ }
44
+ if ((type === 'operation') && (!evidence.command || evidence.command.trim().length === 0)) {
45
+ missing.push('serviceEvidence.operation.command');
46
+ }
47
+ }
33
48
  return { complete: missing.length === 0, missing };
34
49
  }
35
50
  }
@@ -9,7 +9,26 @@ class SynthesizerAgent {
9
9
  async synthesize(state, lastOutput, review) {
10
10
  const governanceRules = (state.governanceEvents || []).map((event) => `${event.type}: ${event.summary}`).join('\n- ');
11
11
  const artifacts = (state.artifacts || []).join('\n- ');
12
- const prompt = `
12
+ const isResearch = state.missionScope === 'research';
13
+ const prompt = isResearch ? `
14
+ Você é o Synthesizer do OpenLife.
15
+
16
+ O usuário pediu uma resposta de pesquisa, não um relatório interno de execução.
17
+
18
+ Pergunta do usuário:
19
+ ${state.goal}
20
+
21
+ Material coletado pelos agentes:
22
+ ${lastOutput}
23
+
24
+ Review final: approved=${review.approved}, score=${review.score}, critique=${review.critique}
25
+
26
+ Responda em português natural, direto e útil.
27
+ - NÃO diga "status da execução", "mission state", "artefato", "orquestração multiagente" ou caminhos internos.
28
+ - Entregue a resposta final para o usuário.
29
+ - Se houver incerteza sobre uma função, diga explicitamente "não encontrei evidência oficial suficiente".
30
+ - Para cada função mencionada, explique: significado, uso prático, e nível de confiança.
31
+ ` : `
13
32
  Você é o Synthesizer do OpenLife.
14
33
 
15
34
  Objetivo do serviço:
@@ -37,6 +37,7 @@ exports.TaskExecutor = void 0;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
39
  const child_process = __importStar(require("child_process"));
40
+ const generative_ai_1 = require("@google/generative-ai");
40
41
  const ToolsetGuard_1 = require("./toolset/ToolsetGuard");
41
42
  const ProcessSandbox_1 = require("./ProcessSandbox");
42
43
  class TaskExecutor {
@@ -146,6 +147,58 @@ class TaskExecutor {
146
147
  const stdoutFile = path.join(artifactDir, 'gemini-stdout.txt');
147
148
  const stderrFile = path.join(artifactDir, 'gemini-stderr.txt');
148
149
  const resultFile = path.join(artifactDir, 'gemini-result.txt');
150
+ const geminiApiKey = (process.env.GEMINI_API_KEY || '').trim();
151
+ if (geminiApiKey) {
152
+ const startedAt = new Date().toISOString();
153
+ const startedMs = Date.now();
154
+ const artifactPath = resultFile;
155
+ try {
156
+ const modelName = process.env.OPENLIFE_TASK_GEMINI_MODEL || process.env.OPENLIFE_GEMINI_MODEL || 'gemini-3.1-flash-lite-preview';
157
+ const api = new generative_ai_1.GoogleGenerativeAI(geminiApiKey);
158
+ const model = api.getGenerativeModel({ model: modelName });
159
+ const result = await model.generateContent(prompt);
160
+ const response = await result.response;
161
+ const finalMessage = response.text().trim();
162
+ const finishedAt = new Date().toISOString();
163
+ const durationMs = Date.now() - startedMs;
164
+ if (!finalMessage)
165
+ throw new Error('EMPTY_GEMINI_API_RESPONSE');
166
+ fs.writeFileSync(artifactPath, finalMessage, 'utf-8');
167
+ const maskedStdout = this.maskSensitiveText(finalMessage);
168
+ const metadataPath = this.writeExecutionMetadata(artifactDir, {
169
+ executor: 'gemini', ok: true, status: 'success', fallbackUsed: false, providerUnavailable: false, initRan: false,
170
+ projectName, artifactPath, stdout: maskedStdout, stderr: '', command: ['gemini-api', modelName],
171
+ commandProvenance: `gemini-api/${modelName}`,
172
+ exitCode: 0, signal: null, startedAt, finishedAt, durationMs
173
+ });
174
+ return {
175
+ executor: 'gemini', ok: true, projectName, artifactPath, stdout: maskedStdout, stderr: '',
176
+ command: ['gemini-api', modelName], commandProvenance: `gemini-api/${modelName}`,
177
+ exitCode: 0, signal: null, startedAt, finishedAt, durationMs, metadataPath,
178
+ status: 'success', fallbackUsed: false, providerUnavailable: false, initRan: false
179
+ };
180
+ }
181
+ catch (err) {
182
+ const finishedAt = new Date().toISOString();
183
+ const durationMs = Date.now() - startedMs;
184
+ const message = err instanceof Error ? err.message : String(err);
185
+ const errorPath = path.join(artifactDir, 'gemini-api-error.txt');
186
+ fs.writeFileSync(errorPath, message, 'utf-8');
187
+ const providerUnavailable = /quota|429|capacity|rate limit|temporarily unavailable|overloaded/i.test(message);
188
+ const maskedStderr = this.maskSensitiveText(message);
189
+ const metadataPath = this.writeExecutionMetadata(artifactDir, {
190
+ executor: 'gemini', ok: false, status: providerUnavailable ? 'partial' : 'failed', fallbackUsed: false, providerUnavailable, initRan: false,
191
+ projectName, artifactPath: errorPath, stdout: '', stderr: maskedStderr, command: ['gemini-api'],
192
+ commandProvenance: 'gemini-api', exitCode: 1, signal: null, startedAt, finishedAt, durationMs
193
+ });
194
+ return {
195
+ executor: 'gemini', ok: false, projectName, artifactPath: errorPath, stdout: '', stderr: maskedStderr,
196
+ command: ['gemini-api'], commandProvenance: 'gemini-api', exitCode: 1, signal: null,
197
+ startedAt, finishedAt, durationMs, metadataPath,
198
+ status: providerUnavailable ? 'partial' : 'failed', fallbackUsed: false, providerUnavailable, initRan: false
199
+ };
200
+ }
201
+ }
149
202
  const shellCommand = `cd ${JSON.stringify(cwd)} && gemini -o json -m gemini-2.5-pro -p ${JSON.stringify(prompt)} -y > ${JSON.stringify(stdoutFile)} 2> ${JSON.stringify(stderrFile)}`;
150
203
  const startedAt = new Date().toISOString();
151
204
  const startedMs = Date.now();
@@ -96,9 +96,10 @@ class CapabilityGenesisEngine {
96
96
  // wire one skill; 'professional' adds a squad; 'elite' adds a full
97
97
  // workflow stub too.
98
98
  const mode = brief.mode || 'professional';
99
- const skillsToInclude = brief.hints?.skills || [];
100
- const squadsToInclude = brief.hints?.squads || [];
101
- const workflowsToInclude = brief.hints?.workflows || [];
99
+ const defaultId = packId;
100
+ const skillsToInclude = brief.hints?.skills?.length ? brief.hints.skills : [defaultId];
101
+ const squadsToInclude = brief.hints?.squads?.length ? brief.hints.squads : [`${defaultId}-squad`];
102
+ const workflowsToInclude = brief.hints?.workflows?.length ? brief.hints.workflows : [defaultId];
102
103
  const createdAssets = [];
103
104
  const skillRefs = [];
104
105
  const squadRefs = [];
@@ -131,15 +132,23 @@ class CapabilityGenesisEngine {
131
132
  createdAssets.push(`squads/${squadId}/SQUAD.md`);
132
133
  }
133
134
  }
134
- // Workflows only in elite mode
135
- if (mode === 'elite') {
136
- for (const wfId of workflowsToInclude) {
137
- const stubPath = path.join(packDir, 'workflows', `${wfId}.yaml`);
138
- (0, AtomicWriter_1.writeStringAtomic)(stubPath, this.workflowStub(wfId, brief.description));
139
- workflowRefs.push({ id: wfId, source: 'embedded' });
140
- createdAssets.push(`workflows/${wfId}.yaml`);
141
- }
135
+ // Workflows are part of the default pack contract. Elite mode may refine later,
136
+ // but professional packs still need an executable checklist/workflow shell.
137
+ for (const wfId of workflowsToInclude) {
138
+ const stubPath = path.join(packDir, 'workflows', `${wfId}.yaml`);
139
+ (0, AtomicWriter_1.writeStringAtomic)(stubPath, this.workflowStub(wfId, brief.description));
140
+ workflowRefs.push({ id: wfId, source: 'embedded' });
141
+ createdAssets.push(`workflows/${wfId}.yaml`);
142
142
  }
143
+ const agentId = `${defaultId}-agent`;
144
+ const checklistId = `${defaultId}-checklist`;
145
+ const policyId = `${defaultId}-policy`;
146
+ (0, AtomicWriter_1.writeStringAtomic)(path.join(packDir, 'agents', `${agentId}.md`), this.agentStub(agentId, brief.description));
147
+ (0, AtomicWriter_1.writeStringAtomic)(path.join(packDir, 'checklists', `${checklistId}.md`), this.checklistStub(checklistId, brief.description));
148
+ fs.mkdirSync(path.join(packDir, 'governance'), { recursive: true });
149
+ (0, AtomicWriter_1.writeJsonAtomic)(path.join(packDir, 'governance', `${policyId}.json`), this.governancePolicyStub(policyId, brief.description));
150
+ const agentRefs = [{ id: agentId, source: 'embedded' }];
151
+ createdAssets.push(`agents/${agentId}.md`, `checklists/${checklistId}.md`, `governance/${policyId}.json`);
143
152
  // Build the manifest
144
153
  const pack = {
145
154
  id: packId,
@@ -148,9 +157,12 @@ class CapabilityGenesisEngine {
148
157
  version: '0.1.0',
149
158
  status: 'draft',
150
159
  objective: brief.description,
160
+ agents: agentRefs,
151
161
  skills: skillRefs.length > 0 ? skillRefs : undefined,
152
162
  squads: squadRefs.length > 0 ? squadRefs : undefined,
153
163
  workflows: workflowRefs.length > 0 ? workflowRefs : undefined,
164
+ checklists: [{ id: checklistId, path: `checklists/${checklistId}.md`, label: 'Promotion checklist' }],
165
+ policies: [{ id: policyId, rationale: 'Default risk policy created by Capability Genesis.', appliesTo: [packId] }],
154
166
  metadata: {
155
167
  createdAt: new Date().toISOString(),
156
168
  updatedAt: new Date().toISOString(),
@@ -224,6 +236,49 @@ version: 0.1.0
224
236
  | TBD | TBD |
225
237
  `;
226
238
  }
239
+ agentStub(agentId, briefText) {
240
+ return `---
241
+ id: ${agentId}
242
+ name: ${agentId}
243
+ status: draft
244
+ source: capability-genesis
245
+ version: 0.1.0
246
+ ---
247
+
248
+ # ${agentId}
249
+
250
+ > Auto-generated agent shell for capability execution.
251
+ > Brief: ${briefText}
252
+
253
+ ## Role
254
+
255
+ Execute and validate this capability under operator governance.
256
+ `;
257
+ }
258
+ checklistStub(checklistId, briefText) {
259
+ return `# ${checklistId}
260
+
261
+ Brief: ${briefText}
262
+
263
+ - [ ] Skill refined
264
+ - [ ] Agent role refined
265
+ - [ ] Squad ownership confirmed
266
+ - [ ] Workflow dry-run passed
267
+ - [ ] Service evidence captured
268
+ - [ ] Governance policy reviewed
269
+ `;
270
+ }
271
+ governancePolicyStub(policyId, briefText) {
272
+ return {
273
+ id: policyId,
274
+ source: 'capability-genesis',
275
+ riskLevel: 'medium',
276
+ permissionScope: 'draft-runtime-asset',
277
+ allowedCapabilities: ['read', 'write', 'network:read'],
278
+ deniedCapabilities: ['delete', 'rename', 'network:write'],
279
+ rationale: `Default governance template for: ${briefText}`,
280
+ };
281
+ }
227
282
  workflowStub(wfId, briefText) {
228
283
  return `workflow:
229
284
  id: ${wfId}
@@ -87,7 +87,7 @@ try {
87
87
  assertTrue(fs.existsSync(r1.manifestPath), 'scenario 1: manifest written');
88
88
  assertTrue(fs.existsSync(path.join(r1.packDir, 'INDEX.md')), 'scenario 1: INDEX.md written');
89
89
  assertTrue(r1.pack.status === 'draft', 'scenario 1: pack status=draft');
90
- assertTrue(r1.createdAssets.length === 3, `scenario 1: 3 assets created (got ${r1.createdAssets.length})`);
90
+ assertTrue(r1.createdAssets.length === 7, `scenario 1: 7 assets created (got ${r1.createdAssets.length})`);
91
91
  console.log('[1.5] scenario 1 (genesis → draft pack) ✓');
92
92
  // ── Scenario 2: Generated manifest parses cleanly ──────────────────
93
93
  const parsed = (0, CapabilityPackParser_1.parseCapabilityPackFile)(r1.manifestPath);
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const assert = __importStar(require("assert"));
37
+ const child_process = __importStar(require("child_process"));
38
+ const path = __importStar(require("path"));
39
+ const InstallModules_1 = require("./cli/InstallModules");
40
+ function runCli(args) {
41
+ return child_process.spawnSync(process.execPath, [path.join(__dirname, 'index.js'), ...args], {
42
+ cwd: path.join(__dirname, '..'),
43
+ encoding: 'utf-8',
44
+ env: { ...process.env, OPENLIFE_DISABLE_CHAT_TUI: '1' },
45
+ timeout: 5000,
46
+ });
47
+ }
48
+ function main() {
49
+ const cli = runCli(['chat', '--test']);
50
+ assert.strictEqual(cli.status, 0, `chat --test should exit 0, stderr=${cli.stderr}, stdout=${cli.stdout}`);
51
+ assert.match(cli.stdout, /OPENLIFE_CHAT_SMOKE_OK/, 'chat --test should print stable smoke marker');
52
+ assert.match(cli.stdout, /sessionId=/, 'chat --test should surface session context');
53
+ assert.match(cli.stdout, /model=/, 'chat --test should surface active model context');
54
+ const smoke = (0, InstallModules_1.chatSmoke)(path.join(__dirname, '..'));
55
+ assert.strictEqual(smoke.ok, true, smoke.detail);
56
+ assert.match(smoke.detail, /OPENLIFE_CHAT_SMOKE_OK/, 'installer chatSmoke should call the real chat test path');
57
+ console.log('TEST_CHAT_SMOKE_COMMAND_OK');
58
+ }
59
+ main();
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const assert = __importStar(require("assert"));
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const DreamGoalStore_1 = require("./orchestrator/DreamGoalStore");
41
+ const Gateway_1 = require("./orchestrator/Gateway");
42
+ async function main() {
43
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'openlife-dream-goal-'));
44
+ process.env.OPENLIFE_STATE_DIR = path.join(root, '.openlife');
45
+ process.env.OPENLIFE_TELEGRAM_ALLOWED_USER_ID = '';
46
+ process.env.OPENLIFE_TTS_ENABLED = 'off';
47
+ const store = new DreamGoalStore_1.DreamGoalStore(process.env.OPENLIFE_STATE_DIR);
48
+ const dream = store.setDream('Construir o OpenLife como sistema operacional de agentes autônomos.');
49
+ assert.equal(dream.kind, 'dream');
50
+ assert.ok(dream.value.includes('sistema operacional'));
51
+ assert.ok(fs.existsSync(path.join(process.env.OPENLIFE_STATE_DIR, 'dream-goal.json')));
52
+ const goal = store.setGoal('Implementar /dream e /goal no Telegram.');
53
+ assert.equal(goal.kind, 'goal');
54
+ assert.ok(goal.value.includes('/dream'));
55
+ const summary = store.summary();
56
+ assert.ok(summary.includes('Dream atual'));
57
+ assert.ok(summary.includes('Goal atual'));
58
+ const gateway = new Gateway_1.Gateway();
59
+ const dreamResp = await gateway.processTextForTest('1344110010', '/dream Criar um AIOBUILDER que transforma sonhos em planos executáveis.');
60
+ assert.match(dreamResp, /Dream registrado/i);
61
+ assert.match(dreamResp, /AIOBUILDER/);
62
+ const goalResp = await gateway.processTextForTest('1344110010', '/goal Entregar comandos slash persistentes no OpenLife.');
63
+ assert.match(goalResp, /Goal registrado/i);
64
+ assert.match(goalResp, /comandos slash/);
65
+ const statusResp = await gateway.processTextForTest('1344110010', '/goal');
66
+ assert.match(statusResp, /Goal atual/i);
67
+ assert.match(statusResp, /comandos slash/);
68
+ const dreamStatus = await gateway.processTextForTest('1344110010', '/dream');
69
+ assert.match(dreamStatus, /Dream atual/i);
70
+ assert.match(dreamStatus, /AIOBUILDER/);
71
+ console.log('DREAM_GOAL_COMMANDS_OK');
72
+ }
73
+ main().catch((err) => {
74
+ console.error(err);
75
+ process.exit(1);
76
+ });
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const assert = __importStar(require("assert"));
37
+ const Gateway_1 = require("./orchestrator/Gateway");
38
+ async function main() {
39
+ process.env.OPENLIFE_TELEGRAM_ALLOWED_USER_ID = '';
40
+ process.env.OPENLIFE_ENABLE_TTS = 'false';
41
+ process.env.OPENLIFE_REASONING_MODE = 'off';
42
+ const gateway = new Gateway_1.Gateway();
43
+ gateway.classifier = {
44
+ classify: async (_text) => ({ intent: 'KNOWLEDGE_RETRIEVAL', budget: 0.1 })
45
+ };
46
+ gateway.gatekeeper = {
47
+ routeTask: async (_task, _text, _userId) => [
48
+ '### Síntese para Operação',
49
+ '',
50
+ '| Comando | Foco Principal | Quando usar |',
51
+ '| :--- | :--- | :--- |',
52
+ '| `/goal` | Execução (O quê) | Para tarefas práticas, correções de bugs ou implementação de recursos definidos. |',
53
+ '| `/dream` | Visão (Como) | Para buscar arquiteturas otimizadas, sugestões de design ou refatorações complexas. |',
54
+ '',
55
+ '---'
56
+ ].join('\n')
57
+ };
58
+ const result = await gateway.processTextForTestDetailed('1344110010', 'compare /goal e /dream');
59
+ const finalText = result.finalText;
60
+ assert.ok(!finalText.includes('| Comando |'), `SHOULD_NOT_LEAK_TABLE_HEADER_${finalText}`);
61
+ assert.ok(!/^\s*\|\s*:?-{3,}/m.test(finalText), `SHOULD_NOT_LEAK_SEPARATOR_${finalText}`);
62
+ assert.ok(!finalText.includes('###'), `SHOULD_NOT_LEAK_MARKDOWN_HEADING_${finalText}`);
63
+ assert.ok(!/^\s*---\s*$/m.test(finalText), `SHOULD_NOT_LEAK_HORIZONTAL_RULE_${finalText}`);
64
+ assert.ok(finalText.includes('Síntese para Operação'), `SHOULD_KEEP_HEADING_TEXT_${finalText}`);
65
+ assert.ok(finalText.includes('• /goal'), `SHOULD_CONVERT_GOAL_ROW_TO_BULLET_${finalText}`);
66
+ assert.ok(finalText.includes('Foco Principal: Execução (O quê)'), `SHOULD_LABEL_GOAL_FOCUS_${finalText}`);
67
+ assert.ok(finalText.includes('Quando usar: Para tarefas práticas'), `SHOULD_LABEL_GOAL_WHEN_${finalText}`);
68
+ assert.ok(finalText.includes('• /dream'), `SHOULD_CONVERT_DREAM_ROW_TO_BULLET_${finalText}`);
69
+ console.log('TEST_GATEWAY_TELEGRAM_FORMATTING_OK');
70
+ }
71
+ main().catch((err) => {
72
+ console.error('TEST_GATEWAY_TELEGRAM_FORMATTING_FAIL:', err?.message || err);
73
+ process.exit(1);
74
+ });
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ // src/test_install_wizard_v2.ts
3
+ // Smoke + invariant tests for the v1.8.0 Install Wizard 2.0.
4
+ //
5
+ // Covers:
6
+ // 1. ProvidersCatalog consistency — every ProviderId has a catalog row.
7
+ // 2. OAuth policy — Anthropic OAuth is blocked, Codex/Gemini/Grok allowed.
8
+ // 3. Phase registry — all phases have unique ids and required fields.
9
+ // 4. CannedIO driver — wizard can walk through happy-path with canned answers
10
+ // without throwing (early phases only — won't touch the filesystem).
11
+ //
12
+ // Pattern follows existing src/test_install_wizard.ts (canned-answer driver).
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ const fs = __importStar(require("fs"));
48
+ const os = __importStar(require("os"));
49
+ const path = __importStar(require("path"));
50
+ const ProvidersCatalog_1 = require("./cli/install/ProvidersCatalog");
51
+ const types_1 = require("./cli/install/types");
52
+ const Phases_1 = require("./cli/install/Phases");
53
+ const WizardIO_1 = require("./cli/install/WizardIO");
54
+ const OAuthRunner_1 = require("./cli/install/OAuthRunner");
55
+ let failed = 0;
56
+ let passed = 0;
57
+ function check(name, cond, detail) {
58
+ if (cond) {
59
+ passed++;
60
+ console.log(` ✓ ${name}`);
61
+ }
62
+ else {
63
+ failed++;
64
+ console.error(` ✗ ${name}${detail ? ` — ${detail}` : ''}`);
65
+ }
66
+ }
67
+ async function main() {
68
+ console.log('test_install_wizard_v2: starting');
69
+ // ── 1. ProvidersCatalog consistency ─────────────────────────────────
70
+ console.log('\n[1] ProvidersCatalog consistency');
71
+ check('catalog has at least 25 entries', ProvidersCatalog_1.PROVIDERS_CATALOG.length >= 25, `actual=${ProvidersCatalog_1.PROVIDERS_CATALOG.length}`);
72
+ // All entries have required fields
73
+ for (const p of ProvidersCatalog_1.PROVIDERS_CATALOG) {
74
+ check(`${p.id} has label`, p.label.length > 0);
75
+ check(`${p.id} has tier`, !!p.tier);
76
+ check(`${p.id} has tags`, p.tags.length > 0);
77
+ }
78
+ // No duplicate IDs
79
+ const seen = new Set();
80
+ let dupes = 0;
81
+ for (const p of ProvidersCatalog_1.PROVIDERS_CATALOG) {
82
+ if (seen.has(p.id))
83
+ dupes++;
84
+ seen.add(p.id);
85
+ }
86
+ check('no duplicate provider ids', dupes === 0, `dupes=${dupes}`);
87
+ // ── 2. OAuth policy enforcement ─────────────────────────────────────
88
+ console.log('\n[2] OAuth policy');
89
+ const oauthList = types_1.OAUTH_ENABLED_PROVIDERS;
90
+ check('OAUTH_ENABLED_PROVIDERS has openai-cli', oauthList.includes('openai-cli'));
91
+ check('OAUTH_ENABLED_PROVIDERS has gemini-cli', oauthList.includes('gemini-cli'));
92
+ check('OAUTH_ENABLED_PROVIDERS has xai (Grok)', oauthList.includes('xai'));
93
+ check('OAUTH_ENABLED_PROVIDERS excludes anthropic', !oauthList.includes('anthropic'), 'Anthropic OAuth must be policy-blocked');
94
+ // OAuth runner refuses Anthropic
95
+ const antResult = await (0, OAuthRunner_1.runOAuth)('anthropic', { dryRun: true });
96
+ check('runOAuth("anthropic") returns ok=false', antResult.ok === false, `detail=${antResult.detail}`);
97
+ check('runOAuth("anthropic") detail mentions POLICY_BLOCK', /POLICY_BLOCK/.test(antResult.detail));
98
+ // OAuth runner accepts Codex dry-run
99
+ const codexResult = await (0, OAuthRunner_1.runOAuth)('openai-cli', { dryRun: true });
100
+ check('runOAuth("openai-cli", dryRun) returns ok=true', codexResult.ok === true);
101
+ const geminiResult = await (0, OAuthRunner_1.runOAuth)('gemini-cli', { dryRun: true });
102
+ check('runOAuth("gemini-cli", dryRun) returns ok=true', geminiResult.ok === true);
103
+ // ── 3. Phase registry ───────────────────────────────────────────────
104
+ console.log('\n[3] Phase registry');
105
+ check('ALL_PHASES has at least 20 phases', Phases_1.ALL_PHASES.length >= 20, `actual=${Phases_1.ALL_PHASES.length}`);
106
+ const phaseIds = new Set();
107
+ let phaseDupes = 0;
108
+ for (const ph of Phases_1.ALL_PHASES) {
109
+ if (phaseIds.has(ph.id))
110
+ phaseDupes++;
111
+ phaseIds.add(ph.id);
112
+ check(`phase "${ph.id}" has label`, ph.label.length > 0);
113
+ check(`phase "${ph.id}" has run fn`, typeof ph.run === 'function');
114
+ }
115
+ check('no duplicate phase ids', phaseDupes === 0, `dupes=${phaseDupes}`);
116
+ // The first 3 phases (banner, existing-install, product-select) MUST be unconditional.
117
+ const banner = Phases_1.ALL_PHASES.find(p => p.id === '00-banner');
118
+ check('00-banner has no `when` condition', banner !== undefined && !banner.when);
119
+ const existing = Phases_1.ALL_PHASES.find(p => p.id === '01-existing-install');
120
+ check('01-existing-install has no `when` condition', existing !== undefined && !existing.when);
121
+ const productSel = Phases_1.ALL_PHASES.find(p => p.id === '02-product-select');
122
+ check('02-product-select has no `when` condition', productSel !== undefined && !productSel.when);
123
+ // Core / agent phases must have `when` conditions
124
+ const coreEnter = Phases_1.ALL_PHASES.find(p => p.id === '10-core-enter');
125
+ check('10-core-enter has `when` condition', coreEnter !== undefined && typeof coreEnter.when === 'function');
126
+ const agentEnter = Phases_1.ALL_PHASES.find(p => p.id === '20-agent-enter');
127
+ check('20-agent-enter has `when` condition', agentEnter !== undefined && typeof agentEnter.when === 'function');
128
+ // ── 4. Catalog ↔ OAuth consistency ──────────────────────────────────
129
+ console.log('\n[4] Catalog ↔ OAuth consistency');
130
+ for (const id of types_1.OAUTH_ENABLED_PROVIDERS) {
131
+ try {
132
+ const p = (0, ProvidersCatalog_1.getProvider)(id);
133
+ check(`OAuth provider "${id}" has oauthCli`, !!p.oauthCli, `oauthCli="${p.oauthCli}"`);
134
+ }
135
+ catch (err) {
136
+ check(`OAuth provider "${id}" exists in catalog`, false, err instanceof Error ? err.message : String(err));
137
+ }
138
+ }
139
+ // Every catalog entry with oauthCli set should appear in OAUTH_ENABLED_PROVIDERS
140
+ for (const p of (0, ProvidersCatalog_1.oauthProviders)()) {
141
+ check(`catalog OAuth "${p.id}" in OAUTH_ENABLED_PROVIDERS`, types_1.OAUTH_ENABLED_PROVIDERS.includes(p.id), `catalog declares oauthCli=${p.oauthCli} but type not allow-listed`);
142
+ }
143
+ // ── 5. CannedIO smoke (banner + existing-install only, no FS writes) ─
144
+ // Use a clean tmp dir so phase 01 hits the "no previous install" branch
145
+ // and doesn't prompt for abort/reinstall/repair.
146
+ console.log('\n[5] CannedIO smoke');
147
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'openlife-test-v2-'));
148
+ try {
149
+ const io = new WizardIO_1.CannedIO([
150
+ /* phase 00-banner: pause */ true,
151
+ /* phase 01-existing-install: pause */ true,
152
+ ]);
153
+ const phase00 = Phases_1.ALL_PHASES[0];
154
+ const phase01 = Phases_1.ALL_PHASES[1];
155
+ const ctx = {
156
+ root: tmpRoot,
157
+ products: [],
158
+ coreHosts: [],
159
+ coreProviders: [],
160
+ coreApiKeys: {},
161
+ agentAuthDecisions: [],
162
+ agentOAuthResults: [],
163
+ agentApiKeys: {},
164
+ warnings: [],
165
+ };
166
+ await phase00.run(ctx, io);
167
+ await phase01.run(ctx, io);
168
+ check('CannedIO drove first 2 phases without throwing', true);
169
+ check('CannedIO transcript captured output', io.transcript.length > 0);
170
+ check('ctx not aborted on clean tmpdir', !ctx.aborted);
171
+ }
172
+ catch (err) {
173
+ check('CannedIO drove first 2 phases without throwing', false, err instanceof Error ? err.message : String(err));
174
+ }
175
+ finally {
176
+ try {
177
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
178
+ }
179
+ catch { /* ignore */ }
180
+ }
181
+ // ── Done ────────────────────────────────────────────────────────────
182
+ console.log('');
183
+ console.log(`Results: ${passed} passed, ${failed} failed`);
184
+ if (failed > 0) {
185
+ console.error('TEST_INSTALL_WIZARD_V2_FAIL');
186
+ process.exit(1);
187
+ }
188
+ console.log('✅ test_install_wizard_v2: all checks passed');
189
+ }
190
+ main().catch((err) => {
191
+ console.error('TEST_INSTALL_WIZARD_V2_UNCAUGHT', err);
192
+ process.exit(1);
193
+ });