@16pxh/cli-bridge 1.0.2 → 1.0.4

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/lib/claude.mjs CHANGED
@@ -25,9 +25,10 @@ export async function checkClaudeInstalled() {
25
25
  * @param {{ timeoutMs?: number }} [options]
26
26
  * @returns {Promise<{ result: string, session_id: string, duration_ms: number }>}
27
27
  */
28
- export function runPrompt(prompt, { timeoutMs = 60000 } = {}) {
28
+ export function runPrompt(prompt, { timeoutMs = 60000, sessionId } = {}) {
29
29
  return new Promise((resolve, reject) => {
30
30
  const args = ['-p', prompt, '--output-format', 'json'];
31
+ if (sessionId) args.push('--resume', sessionId);
31
32
  const child = spawn('claude', args, { stdio: ['ignore', 'pipe', 'pipe'] });
32
33
 
33
34
  activeProcess = child;
package/lib/server.mjs CHANGED
@@ -5,6 +5,40 @@ import { checkClaudeInstalled, runPrompt } from './claude.mjs';
5
5
  const PORT = 1676;
6
6
  const HOST = '127.0.0.1';
7
7
 
8
+ // ─── Colored logging ─────────────────────────────────────────────────────────
9
+ const c = {
10
+ reset: '\x1b[0m',
11
+ dim: '\x1b[2m',
12
+ green: '\x1b[32m',
13
+ yellow: '\x1b[33m',
14
+ red: '\x1b[31m',
15
+ cyan: '\x1b[36m',
16
+ magenta: '\x1b[35m',
17
+ };
18
+
19
+ function truncateWords(text, maxWords = 16) {
20
+ const words = text.replace(/\s+/g, ' ').trim().split(' ');
21
+ if (words.length <= maxWords) return words.join(' ');
22
+ return words.slice(0, maxWords).join(' ') + '…';
23
+ }
24
+
25
+ function truncateChars(text, maxChars = 64) {
26
+ const clean = text.replace(/\s+/g, ' ').trim();
27
+ if (clean.length <= maxChars) return clean;
28
+ return clean.slice(0, maxChars) + '…';
29
+ }
30
+
31
+ function timestamp() {
32
+ return new Date().toLocaleTimeString('en-US', { hour12: false });
33
+ }
34
+
35
+ function logRequest(method, path, status, extra = '') {
36
+ const statusColor = status < 400 ? c.green : status < 500 ? c.yellow : c.red;
37
+ console.log(
38
+ `${c.dim}${timestamp()}${c.reset} ${c.cyan}${method}${c.reset} ${path} ${statusColor}${status}${c.reset}${extra ? ' ' + extra : ''}`
39
+ );
40
+ }
41
+
8
42
  const CORS_HEADERS = {
9
43
  'Access-Control-Allow-Origin': '*',
10
44
  'Access-Control-Allow-Headers': 'x-bridge-token, content-type',
@@ -78,9 +112,12 @@ export function startServer() {
78
112
 
79
113
  // GET /health
80
114
  if (req.method === 'GET' && url.pathname === '/health') {
81
- if (!authenticate(req, res)) return;
82
-
115
+ if (!authenticate(req, res)) {
116
+ logRequest('GET', '/health', 403);
117
+ return;
118
+ }
83
119
  const version = await checkClaudeInstalled();
120
+ logRequest('GET', '/health', 200, `${c.dim}cli=${version ? 'ok' : 'missing'}${c.reset}`);
84
121
  json(res, 200, {
85
122
  status: 'ok',
86
123
  claude_cli: version !== null,
@@ -91,34 +128,46 @@ export function startServer() {
91
128
 
92
129
  // POST /prompt
93
130
  if (req.method === 'POST' && url.pathname === '/prompt') {
94
- if (!authenticate(req, res)) return;
131
+ if (!authenticate(req, res)) {
132
+ logRequest('POST', '/prompt', 403);
133
+ return;
134
+ }
95
135
 
96
136
  let body;
97
137
  try {
98
138
  const raw = await readBody(req);
99
139
  body = JSON.parse(raw);
100
140
  } catch {
141
+ logRequest('POST', '/prompt', 400, `${c.red}invalid JSON${c.reset}`);
101
142
  json(res, 400, { error: 'Invalid JSON body' });
102
143
  return;
103
144
  }
104
145
 
105
- const { prompt, images } = body;
146
+ const { prompt, images, session_id } = body;
106
147
  if (!prompt || typeof prompt !== 'string') {
148
+ logRequest('POST', '/prompt', 400, `${c.red}missing prompt${c.reset}`);
107
149
  json(res, 400, { error: 'Missing or invalid "prompt" field' });
108
150
  return;
109
151
  }
110
152
 
153
+ const promptPreview = truncateWords(prompt, 16);
154
+ console.log(`${c.dim}${timestamp()}${c.reset} ${c.magenta}→${c.reset} ${c.dim}${promptPreview}${c.reset}`);
155
+
111
156
  let fullPrompt = prompt;
112
157
  if (Array.isArray(images) && images.length > 0) {
113
- for (const url of images) {
114
- fullPrompt += `\n\nAnalyze this image: ${url}`;
158
+ for (const imgUrl of images) {
159
+ fullPrompt += `\n\nAnalyze this image: ${imgUrl}`;
115
160
  }
116
161
  }
117
162
 
118
163
  try {
119
- const { result, session_id, duration_ms } = await runPrompt(fullPrompt);
120
- json(res, 200, { success: true, result, session_id, duration_ms });
164
+ const { result, session_id: responseSessionId, duration_ms } = await runPrompt(fullPrompt, { sessionId: session_id });
165
+ const resultPreview = truncateChars(result, 64);
166
+ logRequest('POST', '/prompt', 200, `${c.dim}${duration_ms}ms${c.reset}`);
167
+ console.log(`${c.dim}${timestamp()}${c.reset} ${c.green}←${c.reset} ${c.dim}${resultPreview}${c.reset}`);
168
+ json(res, 200, { success: true, result, session_id: responseSessionId, duration_ms });
121
169
  } catch (err) {
170
+ logRequest('POST', '/prompt', 500, `${c.red}${err.message}${c.reset}`);
122
171
  json(res, 500, { error: err.message });
123
172
  }
124
173
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@16pxh/cli-bridge",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Bridge local Claude CLI to 16pxh AI features",
5
5
  "type": "module",
6
6
  "bin": {