Skip to content

Commit a14fc6b

Browse files
committed
fix: stabilize Codra Code release validation
- Fix ESM require('os') in status.ts (was breaking /status) - Update test script to 'node --import tsx --test ...' so tests run (was node --test on .ts) - Update tsconfig to exclude **/*.test.* so published dist does not ship test files - Rebuild dist with clean artifacts - Targeted pnpm install + typecheck + test + build + smoke + npm pack --dry-run all clean for @talocode/codra-code - No tokens/secrets in outputs; local-first preserved; auth/slash work intact - package contents verified (dist, README, skills, plugins only)
1 parent e28ca2a commit a14fc6b

32 files changed

Lines changed: 3618 additions & 161 deletions

apps/code/dist/auth/index.js

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ const AUTH_DEV_BYPASS = process.env.CODRA_AUTH_DEV_BYPASS === '1';
88
const ALLOW_DEV_BYPASS = process.env.CODRA_ALLOW_DEV_BYPASS === '1' || process.env.NODE_ENV !== 'production';
99
let authToken = null;
1010
export function getAuthBaseUrl() {
11-
return process.env.CODRA_AUTH_BASE_URL || DEFAULT_AUTH_URL;
11+
return process.env.CODRA_AUTH_BASE_URL ||
12+
process.env.TERA_AUTH_BASE_URL ||
13+
DEFAULT_AUTH_URL;
1214
}
1315
export function getAuthFilePath() {
1416
return AUTH_FILE;
@@ -37,9 +39,8 @@ export function getAuthToken() {
3739
try {
3840
const content = fs.readFileSync(AUTH_FILE, 'utf-8');
3941
const token = JSON.parse(content);
40-
// Check if token is expired
4142
if (new Date(token.expiresAt) < new Date()) {
42-
console.log(chalk.yellow(' Warning: Auth token has expired. Please run: codra-code login'));
43+
console.log(chalk.yellow(' Warning: Auth token has expired. Please run: codra login'));
4344
return null;
4445
}
4546
authToken = token;
@@ -57,13 +58,12 @@ export async function saveAuthToken(token) {
5758
fs.mkdirSync(codraDir, { recursive: true });
5859
}
5960
fs.writeFileSync(AUTH_FILE, JSON.stringify(token, null, 2));
60-
// Set permissions to 600 on Linux/macOS
6161
if (process.platform !== 'win32') {
6262
try {
6363
fs.chmodSync(AUTH_FILE, 0o600);
6464
}
6565
catch {
66-
// Ignore permission errors on some systems
66+
// Ignore
6767
}
6868
}
6969
authToken = token;
@@ -79,12 +79,11 @@ export async function startLogin(options = {}) {
7979
console.log(chalk.cyan('\n Codra Code Authentication'));
8080
console.log(chalk.gray(' Starting Tera login flow...\n'));
8181
try {
82-
// Start device auth session
8382
const startResponse = await fetch(`${authBaseUrl}/api/codra/auth/device/start`, {
8483
method: 'POST',
8584
headers: { 'Content-Type': 'application/json' },
8685
body: JSON.stringify({
87-
cli_version: '0.1.6',
86+
cli_version: '0.4.0',
8887
platform: process.platform
8988
})
9089
});
@@ -105,18 +104,16 @@ export async function startLogin(options = {}) {
105104
console.log(chalk.gray(' Opening browser for authentication...'));
106105
console.log(chalk.gray(' If browser doesn\'t open, visit:\n'));
107106
console.log(chalk.cyan(` ${verification_url}\n`));
108-
// Try to open browser
109107
await openBrowser(verification_url);
110108
}
111109
console.log(chalk.gray(' Waiting for authentication...'));
112110
console.log(chalk.gray(' (Press Ctrl+C to cancel)\n'));
113-
// Poll for authentication
114111
const token = await pollForAuth(device_code, authBaseUrl, interval || 2);
115112
if (token) {
116113
await saveAuthToken(token);
117114
console.log(chalk.green('\n ✓ Authenticated with Tera successfully!'));
118115
console.log(chalk.gray(` Account: ${token.email}`));
119-
console.log(chalk.gray(' You can now use Codra Code.\n'));
116+
console.log(chalk.gray(' You can now use Codra Code for hosted features.\n'));
120117
return true;
121118
}
122119
console.log(chalk.red('\n ✗ Authentication failed or timed out.\n'));
@@ -127,14 +124,6 @@ export async function startLogin(options = {}) {
127124
return false;
128125
}
129126
}
130-
function generateDeviceCode() {
131-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
132-
let result = '';
133-
for (let i = 0; i < 32; i++) {
134-
result += chars.charAt(Math.floor(Math.random() * chars.length));
135-
}
136-
return result;
137-
}
138127
async function openBrowser(url) {
139128
const { exec } = await import('child_process');
140129
const platform = process.platform;
@@ -148,12 +137,10 @@ async function openBrowser(url) {
148137
else {
149138
command = `xdg-open "${url}" || echo "Could not open browser"`;
150139
}
151-
return new Promise((resolve) => {
152-
exec(command, () => resolve());
153-
});
140+
return new Promise((resolve) => { exec(command, () => resolve()); });
154141
}
155142
async function pollForAuth(deviceCode, authBaseUrl, interval) {
156-
const maxAttempts = 150; // 5 minutes with 2-second intervals
143+
const maxAttempts = 150;
157144
const pollInterval = interval * 1000;
158145
for (let i = 0; i < maxAttempts; i++) {
159146
try {
@@ -185,7 +172,7 @@ async function pollForAuth(deviceCode, authBaseUrl, interval) {
185172
}
186173
}
187174
catch {
188-
// Backend endpoint may not exist yet
175+
// safe placeholder, backend may not be live yet
189176
}
190177
await new Promise(resolve => setTimeout(resolve, pollInterval));
191178
}
@@ -195,7 +182,7 @@ export async function authStatus() {
195182
console.log(chalk.cyan('\n Auth Status'));
196183
if (AUTH_DEV_BYPASS && ALLOW_DEV_BYPASS) {
197184
console.log(chalk.yellow(' Mode: Development bypass active'));
198-
console.log(chalk.gray(' Set CODRA_AUTH_DEV_BYPASS=0 to disable\n'));
185+
console.log(chalk.gray(' Set CODRA_AUTH_DEV_BYPASS=*** to disable\n'));
199186
return;
200187
}
201188
const token = getAuthToken();
@@ -206,22 +193,25 @@ export async function authStatus() {
206193
console.log(chalk.gray(` Account: ${token.email}`));
207194
console.log(chalk.gray(` Expires: ${expiresDate.toLocaleDateString()}`));
208195
if (isExpired) {
209-
console.log(chalk.yellow(' Warning: Token has expired. Run: codra-code login'));
196+
console.log(chalk.yellow(' Warning: Token has expired. Run: codra login'));
210197
}
211-
console.log(chalk.gray(` Token: ${AUTH_FILE}`));
198+
console.log(chalk.gray(` Hosted usage: available (Tera/Talocode account)`));
199+
console.log(chalk.gray(` Token file: ${AUTH_FILE} (contents never printed)`));
212200
}
213201
else {
214202
console.log(chalk.red(' Status: Not authenticated'));
215-
console.log(chalk.gray(' Run: codra-code login'));
203+
console.log(chalk.gray(' Run: codra login'));
216204
console.log(chalk.gray(' Sign in at: https://teraai.chat/auth/signin'));
205+
console.log(chalk.gray(' Hosted usage: blocked for hosted providers'));
206+
console.log(chalk.gray(' Local mode (mock, ollama): works without auth'));
217207
}
218208
console.log('');
219209
}
220210
export function requireAuth() {
221211
if (isAuthenticated())
222212
return true;
223-
console.log(chalk.red('\n Codra Code requires a Tera account.'));
224-
console.log(chalk.gray(' Run: codra-code login'));
213+
console.log(chalk.red('\n Codra Code requires a Tera account for hosted features.'));
214+
console.log(chalk.gray(' Run: codra login'));
225215
console.log(chalk.gray(' Sign in at: https://teraai.chat/auth/signin\n'));
226216
return false;
227217
}

apps/code/dist/commands/help.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ export async function helpCommand() {
5151
console.log(chalk.gray(' /skills clear Clear all active skills'));
5252
console.log(chalk.gray(' /skills paths Show skill search paths'));
5353
console.log('');
54+
console.log(chalk.gray(' Setup:'));
55+
console.log(chalk.gray(' /setup Analyze project and recommend setup'));
56+
console.log(chalk.gray(' /setup analyze Detect project stack only'));
57+
console.log(chalk.gray(' /setup recommend Generate recommendations'));
58+
console.log(chalk.gray(' /setup status Show last setup report'));
59+
console.log(chalk.gray(' /setup apply Preview applying safe changes'));
60+
console.log('');
5461
console.log(chalk.gray(' Plugins:'));
5562
console.log(chalk.gray(' /plugins List installed plugins'));
5663
console.log(chalk.gray(' /plugin <name> Show plugin info'));

apps/code/dist/commands/index.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,18 @@ import { permissionsCommand } from './permissions.js';
2929
import { activityCommand } from './activity.js';
3030
import { toolsCommand } from './tools.js';
3131
import { visualPlanCommand, visualPlansCommand } from './visualPlan.js';
32+
import { setupCommand } from './setup.js';
3233
import { isAuthenticated, startLogin, clearAuthToken, authStatus } from '../auth/index.js';
3334
// Commands that don't require authentication
34-
const PUBLIC_COMMANDS = ['/help', '/login', '/logout', '/auth', '/auth status', '/auth:token-path', '/skills', '/skill'];
35+
const PUBLIC_COMMANDS = ['/help', '/login', '/logout', '/auth', '/auth status', '/auth:token-path', '/skills', '/skill', '/setup', '/model', '/provider', '/status', '/clear', '/exit'];
3536
export async function handleCommand(input) {
3637
const parts = input.trim().split(' ');
3738
const command = parts[0].toLowerCase();
3839
const args = parts.slice(1);
3940
// Check authentication for protected commands
4041
if (!PUBLIC_COMMANDS.includes(command) && !isAuthenticated()) {
4142
console.log(chalk.red('\n Codra Code requires a Tera account.'));
42-
console.log(chalk.gray(' Run: /login'));
43+
console.log(chalk.gray(' Run: codra login or /login'));
4344
console.log(chalk.gray(' Sign in at: https://teraai.chat/auth/signin\n'));
4445
return;
4546
}
@@ -175,6 +176,24 @@ export async function handleCommand(input) {
175176
case '/visual-recap':
176177
await visualPlanCommand(args);
177178
break;
179+
case '/setup':
180+
await setupCommand(args);
181+
break;
182+
case '/project':
183+
console.log(chalk.gray('\n /project is not fully wired yet in this build. Use normal chat or /status for workspace info.\n'));
184+
break;
185+
case '/build':
186+
console.log(chalk.gray('\n /build is not wired yet in this build. Use a normal instruction or /plan first.\n'));
187+
break;
188+
case '/review':
189+
console.log(chalk.gray('\n /review is not wired yet in this build. Describe what to review in chat.\n'));
190+
break;
191+
case '/test':
192+
console.log(chalk.gray('\n /test is not wired yet in this build. Use /run or normal prompt for test commands.\n'));
193+
break;
194+
case '/commit':
195+
console.log(chalk.gray('\n /commit is not wired yet in this build. Use /git or run git commands via /run.\n'));
196+
break;
178197
default:
179198
console.log(`Unknown command: ${command}. Type /help for available commands.`);
180199
}

apps/code/dist/commands/model.js

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,68 @@
11
import chalk from 'chalk';
2-
import { getConfig, updateConfig } from '../config.js';
2+
import * as readline from 'readline';
3+
import { getConfig } from '../config.js';
4+
import { getAvailableProviders, getProviderInfo } from '../providers/registry.js';
35
export async function modelCommand(args) {
46
const config = getConfig();
57
if (args.length === 0) {
6-
console.log(chalk.cyan(`\n Current Model: ${config.model || 'Not configured'}`));
7-
console.log(chalk.gray(' Use /model <name> to change the model.\n'));
8+
await showModelPicker();
89
}
910
else {
1011
const newModel = args[0];
11-
updateConfig({ model: newModel });
12+
const { saveConfig } = await import('../config.js');
13+
saveConfig({ model: newModel });
1214
console.log(chalk.green(`\n Model updated to: ${newModel}\n`));
1315
}
1416
}
17+
async function showModelPicker() {
18+
console.log(chalk.cyan('\n Model Picker — Select Provider'));
19+
const current = getConfig();
20+
const currentInfo = getProviderInfo(current.provider) || { label: current.provider };
21+
console.log(chalk.gray(` Current: ${current.model || 'n/a'} on ${currentInfo.label || current.provider}\n`));
22+
const providers = getAvailableProviders();
23+
providers.forEach((p, i) => {
24+
const marker = p.name === current.provider ? '>' : ' ';
25+
const authNote = p.needsAuth ? ' (auth required for hosted)' : p.local ? ' (local)' : '';
26+
console.log(chalk.gray(` ${marker} ${i + 1}. ${p.label}${authNote}`));
27+
});
28+
console.log(chalk.gray(' 0. Cancel\n'));
29+
const choice = await ask(' Select provider number: ');
30+
const idx = parseInt(choice.trim(), 10) - 1;
31+
if (isNaN(idx) || idx < -1 || idx >= providers.length) {
32+
if (choice.trim() !== '0')
33+
console.log(chalk.yellow(' Invalid selection.'));
34+
return;
35+
}
36+
if (idx === -1)
37+
return;
38+
const selectedProvider = providers[idx];
39+
console.log(chalk.cyan(`\n Select model for ${selectedProvider.label}:`));
40+
selectedProvider.models.forEach((m, i) => {
41+
const marker = m === current.model ? '>' : ' ';
42+
console.log(chalk.gray(` ${marker} ${i + 1}. ${m}`));
43+
});
44+
console.log(chalk.gray(' 0. Cancel\n'));
45+
const modelChoice = await ask(' Select model number: ');
46+
const mIdx = parseInt(modelChoice.trim(), 10) - 1;
47+
if (isNaN(mIdx) || mIdx < -1 || mIdx >= selectedProvider.models.length) {
48+
if (modelChoice.trim() !== '0')
49+
console.log(chalk.yellow(' Invalid.'));
50+
return;
51+
}
52+
if (mIdx === -1)
53+
return;
54+
const newModel = selectedProvider.models[mIdx];
55+
const { saveConfig } = await import('../config.js');
56+
saveConfig({ provider: selectedProvider.name, model: newModel });
57+
console.log(chalk.green(`\n ✓ Switched to ${newModel} on ${selectedProvider.label}\n`));
58+
console.log(chalk.gray(' Restart or continue session to use new provider/model.\n'));
59+
}
60+
function ask(question) {
61+
return new Promise((resolve) => {
62+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
63+
rl.question(question, (answer) => {
64+
rl.close();
65+
resolve(answer);
66+
});
67+
});
68+
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import chalk from 'chalk';
2-
import { getConfig, updateConfig } from '../config.js';
2+
import { getConfig } from '../config.js';
33
export async function providerCommand(args) {
44
const config = getConfig();
55
if (args.length === 0) {
66
console.log(chalk.cyan(`\n Current Provider: ${config.provider || 'Not configured'}`));
77
console.log(chalk.gray(' Use /provider <name> to change the provider.'));
8-
console.log(chalk.gray(' Supported: openai, anthropic, ollama, custom\n'));
8+
console.log(chalk.gray(' Supported: mock, ollama, openai, gemini, anthropic\n'));
99
}
1010
else {
1111
const newProvider = args[0];
12-
updateConfig({ provider: newProvider });
12+
const { saveConfig } = await import('../config.js');
13+
saveConfig({ provider: newProvider });
1314
console.log(chalk.green(`\n Provider updated to: ${newProvider}\n`));
1415
}
1516
}

apps/code/dist/commands/setup.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export declare function setupCommand(args: string[]): Promise<void>;

apps/code/dist/commands/setup.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import chalk from 'chalk';
2+
import { analyzeProject } from '../setup/analyze.js';
3+
import { generateRecommendations } from '../setup/recommend.js';
4+
import { formatAnalysis, formatRecommendation } from '../setup/format.js';
5+
import { saveSetupReport, loadSetupReport } from '../setup/store.js';
6+
export async function setupCommand(args) {
7+
const subcommand = args[0] || 'full';
8+
switch (subcommand) {
9+
case 'analyze': {
10+
const cwd = process.cwd();
11+
console.log(chalk.gray('\n Analyzing project...\n'));
12+
const stack = analyzeProject(cwd);
13+
console.log(formatAnalysis(stack));
14+
break;
15+
}
16+
case 'recommend': {
17+
const cwd = process.cwd();
18+
console.log(chalk.gray('\n Analyzing and recommending...\n'));
19+
const stack = analyzeProject(cwd);
20+
const recommendation = generateRecommendations(stack);
21+
console.log(formatAnalysis(stack));
22+
console.log(formatRecommendation(recommendation));
23+
break;
24+
}
25+
case 'status': {
26+
const cwd = process.cwd();
27+
const existing = loadSetupReport(cwd);
28+
if (existing) {
29+
console.log(chalk.cyan('\n Setup Status:'));
30+
console.log(chalk.gray(` Last analyzed: ${existing.analyzedAt}`));
31+
console.log(chalk.gray(` Project type: ${existing.stack.projectType}`));
32+
console.log(chalk.gray(` Recommended skills: ${existing.recommendation.recommendedSkills.length}`));
33+
console.log(chalk.gray(` Confidence: ${existing.recommendation.confidence}%`));
34+
console.log('');
35+
}
36+
else {
37+
console.log(chalk.gray('\n No setup analysis found. Run /setup to analyze your project.\n'));
38+
}
39+
break;
40+
}
41+
case 'apply': {
42+
const cwd = process.cwd();
43+
const existing = loadSetupReport(cwd);
44+
if (!existing) {
45+
console.log(chalk.gray('\n No setup report found. Run /setup first.\n'));
46+
return;
47+
}
48+
console.log(chalk.cyan('\n Setup Apply — Safe Changes:'));
49+
console.log(chalk.gray(` Project type: ${existing.stack.projectType}`));
50+
console.log(chalk.gray(` Recommended skills: ${existing.recommendation.recommendedSkills.length}`));
51+
console.log('');
52+
console.log(chalk.gray(' The following changes would be applied:'));
53+
console.log(chalk.gray(` - Activate ${existing.recommendation.recommendedSkills.length} recommended skills`));
54+
console.log(chalk.gray(` - Set permission level to ${existing.recommendation.recommendedPermissionLevel}`));
55+
console.log('');
56+
console.log(chalk.yellow(' Note: /setup apply is a preview in v0.1.'));
57+
console.log(chalk.yellow(' Full automated setup will be available in a future version.'));
58+
console.log('');
59+
break;
60+
}
61+
default: {
62+
const cwd = process.cwd();
63+
console.log(chalk.gray('\n Running project analysis and recommendations...\n'));
64+
const stack = analyzeProject(cwd);
65+
const recommendation = generateRecommendations(stack);
66+
console.log(formatAnalysis(stack));
67+
console.log(formatRecommendation(recommendation));
68+
const report = {
69+
version: '0.1',
70+
analyzedAt: new Date().toISOString(),
71+
cwd,
72+
stack,
73+
recommendation
74+
};
75+
saveSetupReport(report);
76+
console.log(chalk.gray(` Report saved to .codra/setup/latest.json`));
77+
console.log('');
78+
break;
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)