knoxis-collab 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/knoxis-collab.js +91 -25
- package/package.json +4 -1
package/knoxis-collab.js
CHANGED
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
* KNOXIS_TASK_FILE=/tmp/task.txt node knoxis-collab.js
|
|
19
19
|
*
|
|
20
20
|
* Env:
|
|
21
|
-
*
|
|
21
|
+
* KNOXIS_BACKEND_URL - Backend URL (or backendUrl in ~/.knoxis/config.json)
|
|
22
|
+
* KNOXIS_USER_ID - User UUID (or userId in ~/.knoxis/config.json)
|
|
23
|
+
* GROQ_API_KEY - Direct Groq key (optional — backend proxy is preferred)
|
|
22
24
|
* KNOXIS_WORKSPACE - Workspace directory (default: cwd)
|
|
23
25
|
* KNOXIS_TASK_FILE - Path to file containing initial task
|
|
24
26
|
* KNOXIS_GROQ_MODEL - Groq model (default: llama-3.3-70b-versatile)
|
|
@@ -80,6 +82,27 @@ function loadConfig() {
|
|
|
80
82
|
|
|
81
83
|
const config = loadConfig();
|
|
82
84
|
const GROQ_API_KEY = process.env.GROQ_API_KEY || config.groqApiKey || '';
|
|
85
|
+
const BACKEND_URL = process.env.KNOXIS_BACKEND_URL || config.backendUrl || '';
|
|
86
|
+
const USER_ID = process.env.KNOXIS_USER_ID || config.userId || '';
|
|
87
|
+
|
|
88
|
+
// Resolve Claude binary — bundled (node_modules) first, then global PATH
|
|
89
|
+
function resolveClaudeBin() {
|
|
90
|
+
// Check local node_modules (when installed as npm package)
|
|
91
|
+
const localBin = path.join(__dirname, 'node_modules', '.bin', 'claude');
|
|
92
|
+
if (fs.existsSync(localBin)) return localBin;
|
|
93
|
+
|
|
94
|
+
// Check parent node_modules (when this is a dep of another package)
|
|
95
|
+
const parentBin = path.join(__dirname, '..', '.bin', 'claude');
|
|
96
|
+
if (fs.existsSync(parentBin)) return parentBin;
|
|
97
|
+
|
|
98
|
+
// Fall back to global PATH
|
|
99
|
+
const { status, stdout } = spawnSync('which', ['claude'], { stdio: 'pipe' });
|
|
100
|
+
if (status === 0 && stdout.toString().trim()) return 'claude';
|
|
101
|
+
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const CLAUDE_BIN = resolveClaudeBin();
|
|
83
106
|
|
|
84
107
|
// ═══════════════════════════════════════════════════════════════
|
|
85
108
|
// STATE
|
|
@@ -135,7 +158,8 @@ function printHeader() {
|
|
|
135
158
|
console.log('');
|
|
136
159
|
console.log(` ${C.dim}Session:${C.reset} ${SESSION_ID.slice(0, 8)}`);
|
|
137
160
|
console.log(` ${C.dim}Dir:${C.reset} ${WORKSPACE}`);
|
|
138
|
-
|
|
161
|
+
const groqMode = (BACKEND_URL && USER_ID) ? `via backend` : 'direct API';
|
|
162
|
+
console.log(` ${C.dim}Knoxis:${C.reset} Groq (${GROQ_MODEL}, ${groqMode})`);
|
|
139
163
|
console.log(` ${C.dim}Claude:${C.reset} Claude Code (session-persistent)`);
|
|
140
164
|
console.log(` ${C.dim}Log:${C.reset} ${path.basename(logFile)}`);
|
|
141
165
|
console.log('');
|
|
@@ -293,12 +317,19 @@ PERSONALITY: Direct, technical, efficient. You're a developer talking to a devel
|
|
|
293
317
|
|
|
294
318
|
function callGroq(messages, projectContext) {
|
|
295
319
|
return new Promise((resolve, reject) => {
|
|
296
|
-
|
|
297
|
-
|
|
320
|
+
const useBackend = BACKEND_URL && USER_ID;
|
|
321
|
+
const useDirectGroq = GROQ_API_KEY && !useBackend;
|
|
322
|
+
|
|
323
|
+
if (!useBackend && !useDirectGroq) {
|
|
324
|
+
reject(new Error(
|
|
325
|
+
'No Groq access configured.\n' +
|
|
326
|
+
' Option 1 (recommended): Set backendUrl + userId in ~/.knoxis/config.json\n' +
|
|
327
|
+
' Option 2 (direct): Set GROQ_API_KEY env var or groqApiKey in config.json'
|
|
328
|
+
));
|
|
298
329
|
return;
|
|
299
330
|
}
|
|
300
331
|
|
|
301
|
-
const
|
|
332
|
+
const groqPayload = {
|
|
302
333
|
model: GROQ_MODEL,
|
|
303
334
|
messages: [
|
|
304
335
|
{ role: 'system', content: buildSystemPrompt(projectContext) },
|
|
@@ -307,20 +338,51 @@ function callGroq(messages, projectContext) {
|
|
|
307
338
|
temperature: 0.3,
|
|
308
339
|
max_tokens: 2048,
|
|
309
340
|
response_format: { type: 'json_object' }
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
const options = {
|
|
313
|
-
hostname: 'api.groq.com',
|
|
314
|
-
path: '/openai/v1/chat/completions',
|
|
315
|
-
method: 'POST',
|
|
316
|
-
headers: {
|
|
317
|
-
'Content-Type': 'application/json',
|
|
318
|
-
'Authorization': `Bearer ${GROQ_API_KEY}`,
|
|
319
|
-
'Content-Length': Buffer.byteLength(payload)
|
|
320
|
-
}
|
|
321
341
|
};
|
|
322
342
|
|
|
323
|
-
|
|
343
|
+
// If backend is available, route through the proxy (no local key needed)
|
|
344
|
+
if (useBackend) {
|
|
345
|
+
groqPayload.userId = USER_ID;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const payload = JSON.stringify(groqPayload);
|
|
349
|
+
|
|
350
|
+
let options;
|
|
351
|
+
if (useBackend) {
|
|
352
|
+
// Route through backend proxy
|
|
353
|
+
const backendClean = BACKEND_URL
|
|
354
|
+
.replace(/^wss:\/\//, 'https://')
|
|
355
|
+
.replace(/^ws:\/\//, 'http://')
|
|
356
|
+
.replace(/\/+$/, '');
|
|
357
|
+
const url = new URL(backendClean + '/api/knoxis/collab/chat');
|
|
358
|
+
options = {
|
|
359
|
+
hostname: url.hostname,
|
|
360
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
361
|
+
path: url.pathname,
|
|
362
|
+
method: 'POST',
|
|
363
|
+
headers: {
|
|
364
|
+
'Content-Type': 'application/json',
|
|
365
|
+
'Content-Length': Buffer.byteLength(payload)
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
} else {
|
|
369
|
+
// Direct Groq API call (legacy / developer override)
|
|
370
|
+
options = {
|
|
371
|
+
hostname: 'api.groq.com',
|
|
372
|
+
path: '/openai/v1/chat/completions',
|
|
373
|
+
method: 'POST',
|
|
374
|
+
headers: {
|
|
375
|
+
'Content-Type': 'application/json',
|
|
376
|
+
'Authorization': `Bearer ${GROQ_API_KEY}`,
|
|
377
|
+
'Content-Length': Buffer.byteLength(payload)
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const transport = options.hostname === 'localhost' || options.port === 80
|
|
383
|
+
? require('http') : https;
|
|
384
|
+
|
|
385
|
+
const req = transport.request(options, (res) => {
|
|
324
386
|
let data = '';
|
|
325
387
|
res.on('data', chunk => data += chunk);
|
|
326
388
|
res.on('end', () => {
|
|
@@ -373,8 +435,7 @@ function callGroq(messages, projectContext) {
|
|
|
373
435
|
// ═══════════════════════════════════════════════════════════════
|
|
374
436
|
|
|
375
437
|
function checkClaudeInstalled() {
|
|
376
|
-
|
|
377
|
-
return result.status === 0;
|
|
438
|
+
return CLAUDE_BIN !== null;
|
|
378
439
|
}
|
|
379
440
|
|
|
380
441
|
function dispatchToClaude(prompt) {
|
|
@@ -392,7 +453,7 @@ function dispatchToClaude(prompt) {
|
|
|
392
453
|
|
|
393
454
|
log(`DISPATCH #${claudeDispatches}:\n${prompt}`);
|
|
394
455
|
|
|
395
|
-
const proc = spawn(
|
|
456
|
+
const proc = spawn(CLAUDE_BIN, args, {
|
|
396
457
|
cwd: WORKSPACE,
|
|
397
458
|
env: { ...process.env },
|
|
398
459
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
@@ -651,15 +712,20 @@ async function handleUserInput(input, rl) {
|
|
|
651
712
|
|
|
652
713
|
async function main() {
|
|
653
714
|
// Preflight checks
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
715
|
+
const hasBackend = BACKEND_URL && USER_ID;
|
|
716
|
+
const hasDirectKey = !!GROQ_API_KEY;
|
|
717
|
+
|
|
718
|
+
if (!hasBackend && !hasDirectKey) {
|
|
719
|
+
console.error(`\n ${C.red}Error: No Groq access configured.${C.reset}`);
|
|
720
|
+
console.error(` Set ${C.bold}backendUrl${C.reset} + ${C.bold}userId${C.reset} in ~/.knoxis/config.json (recommended)`);
|
|
721
|
+
console.error(` Or set GROQ_API_KEY for direct access.\n`);
|
|
657
722
|
process.exit(1);
|
|
658
723
|
}
|
|
659
724
|
|
|
660
725
|
if (!checkClaudeInstalled()) {
|
|
661
|
-
console.error(`\n ${C.red}Error: 'claude' CLI not found
|
|
662
|
-
console.error(`
|
|
726
|
+
console.error(`\n ${C.red}Error: 'claude' CLI not found.${C.reset}`);
|
|
727
|
+
console.error(` It should be bundled with this package. Try: npm install`);
|
|
728
|
+
console.error(` Or install globally: npm install -g @anthropic-ai/claude-code\n`);
|
|
663
729
|
process.exit(1);
|
|
664
730
|
}
|
|
665
731
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knoxis-collab",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Three-way collaborative programming — User + Knoxis + Claude Code in a persistent session",
|
|
5
5
|
"main": "knoxis-collab.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"knoxis-collab": "./knoxis-collab.js"
|
|
8
8
|
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@anthropic-ai/claude-code": "^1.0.0"
|
|
11
|
+
},
|
|
9
12
|
"keywords": [
|
|
10
13
|
"knoxis",
|
|
11
14
|
"pair-programming",
|