thebird 1.2.80 → 1.2.82
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/docs/index.html +1 -1
- package/docs/kilo-fs-mirror.js +15 -0
- package/docs/kilo-http-stream.js +27 -4
- package/docs/shell.js +1 -2
- package/package.json +1 -1
- package/start-kilo.js +40 -12
package/docs/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<body>
|
|
12
12
|
<div style="display:flex;flex-direction:column;height:100%">
|
|
13
13
|
<div class="tui-header"><pre class="logo" style="margin:0;padding:2px 1ch"> ▀█▀ █░█ █▀▀ █▄▄ █ █▀█ █▀▄ <span class="ver">v1.2</span>
|
|
14
|
-
█ █▀█ ██▄ █▄█ █ █▀▄ █▄▀ <span style="color:#1a9a1a">
|
|
14
|
+
█ █▀█ ██▄ █▄█ █ █▀▄ █▄▀ <span style="color:#1a9a1a">instant posix js</span></pre></div>
|
|
15
15
|
<div class="tui-tabs">
|
|
16
16
|
<button id="tab-chat" class="tui-tab active" onclick="switchTab('chat')">Chat</button>
|
|
17
17
|
<button id="tab-term" class="tui-tab" onclick="switchTab('term')">Terminal</button>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function mirrorFromSandbox(fsBase, _touchedPaths) {
|
|
2
|
+
const listRes = await fetch(fsBase + '/__list');
|
|
3
|
+
if (!listRes.ok) return [];
|
|
4
|
+
const relFiles = await listRes.json();
|
|
5
|
+
const snap = window.__debug.idbSnapshot || (window.__debug.idbSnapshot = {});
|
|
6
|
+
const mirrored = [];
|
|
7
|
+
for (const rel of relFiles) {
|
|
8
|
+
const r = await fetch(fsBase + '/' + rel);
|
|
9
|
+
if (!r.ok) continue;
|
|
10
|
+
const content = await r.text();
|
|
11
|
+
if (snap[rel] !== content) { snap[rel] = content; mirrored.push(rel); }
|
|
12
|
+
}
|
|
13
|
+
if (mirrored.length) { window.__debug.idbPersist?.(); window.__debug.shell?.onPreviewWrite?.(); }
|
|
14
|
+
return mirrored;
|
|
15
|
+
}
|
package/docs/kilo-http-stream.js
CHANGED
|
@@ -1,24 +1,47 @@
|
|
|
1
|
+
import { mirrorFromSandbox } from './kilo-fs-mirror.js';
|
|
2
|
+
|
|
1
3
|
export async function* streamKiloHTTP({ url, model, messages }) {
|
|
2
4
|
yield { type: 'start-step' };
|
|
3
5
|
const base = (url || 'http://localhost:4780').replace(/\/$/, '');
|
|
6
|
+
const fsBase = base.replace(/:\d+$/, ':4781');
|
|
4
7
|
let sessRes;
|
|
5
8
|
try { sessRes = await fetch(base + '/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' }); }
|
|
6
|
-
catch (e) { throw new Error('kilo serve not reachable at ' + base + ' — start it with: kilo
|
|
9
|
+
catch (e) { throw new Error('kilo serve not reachable at ' + base + ' — start it with: node start-kilo.js --origin ' + location.origin); }
|
|
7
10
|
if (!sessRes.ok) throw new Error('kilo /session ' + sessRes.status + ': ' + await sessRes.text());
|
|
8
11
|
const { id: sessionId } = await sessRes.json();
|
|
9
|
-
Object.assign(window.__debug = window.__debug || {}, { kilo: { sessionId, url: base, lastStatus: null } });
|
|
12
|
+
Object.assign(window.__debug = window.__debug || {}, { kilo: { sessionId, url: base, fsBase, writes: [], lastStatus: null } });
|
|
13
|
+
|
|
14
|
+
const es = new EventSource(base + '/event');
|
|
15
|
+
const pendingWrites = new Set();
|
|
16
|
+
es.onmessage = (ev) => {
|
|
17
|
+
try {
|
|
18
|
+
const msg = JSON.parse(ev.data);
|
|
19
|
+
if (msg.type === 'message.part.updated') {
|
|
20
|
+
const part = msg.properties?.part;
|
|
21
|
+
if (part?.type === 'tool' && part.state?.status === 'completed' && (part.tool === 'write' || part.tool === 'edit')) {
|
|
22
|
+
const abs = part.state.input?.filePath;
|
|
23
|
+
if (abs) pendingWrites.add(abs);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} catch (_) {}
|
|
27
|
+
};
|
|
10
28
|
|
|
11
29
|
const userText = messages.filter(m => m.role === 'user').map(m =>
|
|
12
30
|
typeof m.content === 'string' ? m.content : (m.content || []).filter(b => b.type === 'text').map(b => b.text).join('')
|
|
13
31
|
).join('\n');
|
|
14
32
|
|
|
15
|
-
const body = { parts: [{ type: 'text', text: userText }], providerID: 'kilo', modelID: model || 'x-ai/grok-code-fast-1:optimized:free'
|
|
33
|
+
const body = { parts: [{ type: 'text', text: userText }], providerID: 'kilo', modelID: model || 'x-ai/grok-code-fast-1:optimized:free' };
|
|
16
34
|
const msgRes = await fetch(base + '/session/' + sessionId + '/message', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
|
17
35
|
window.__debug.kilo.lastStatus = msgRes.status;
|
|
18
|
-
if (!msgRes.ok) throw new Error('kilo message ' + msgRes.status + ': ' + await msgRes.text());
|
|
36
|
+
if (!msgRes.ok) { es.close(); throw new Error('kilo message ' + msgRes.status + ': ' + await msgRes.text()); }
|
|
19
37
|
const result = await msgRes.json();
|
|
20
38
|
window.__debug.kilo.lastResult = result;
|
|
39
|
+
es.close();
|
|
40
|
+
const mirrored = await mirrorFromSandbox(fsBase, [...pendingWrites]);
|
|
41
|
+
window.__debug.kilo.writes = mirrored;
|
|
42
|
+
if (mirrored.length) window.refreshPreview?.();
|
|
21
43
|
const textParts = (result.parts || []).filter(p => p.type === 'text');
|
|
22
44
|
for (const tp of textParts) yield { type: 'text-delta', textDelta: tp.text };
|
|
45
|
+
if (mirrored.length) yield { type: 'text-delta', textDelta: '\n\n[mirrored to preview: ' + mirrored.join(', ') + ']' };
|
|
23
46
|
yield { type: 'finish-step', finishReason: result.info?.finish || 'stop' };
|
|
24
47
|
}
|
package/docs/shell.js
CHANGED
|
@@ -35,6 +35,7 @@ export function createShell({ term, onPreviewWrite }) {
|
|
|
35
35
|
const toKey = p => p.replace(/^\//, '');
|
|
36
36
|
const snap = () => window.__debug.idbSnapshot || {};
|
|
37
37
|
|
|
38
|
+
let expandTokens, captureRun;
|
|
38
39
|
const BUILTINS = makeBuiltins(ctx, actor, invokeBuiltin);
|
|
39
40
|
ctx.builtinsRef = BUILTINS;
|
|
40
41
|
const _exp = makeExpander(ctx, l => captureRun(l), t => parseRedirect(t));
|
|
@@ -55,8 +56,6 @@ export function createShell({ term, onPreviewWrite }) {
|
|
|
55
56
|
const runNode = makeNodeRunner(ctx, actor);
|
|
56
57
|
const runNpmResult = makeNpmResultRunner(ctx, line => run(line));
|
|
57
58
|
|
|
58
|
-
let expandTokens, captureRun;
|
|
59
|
-
|
|
60
59
|
async function captureFn(fn) {
|
|
61
60
|
let out = ''; const orig = term.write.bind(term); term.write = s => { out += s; };
|
|
62
61
|
try { await fn(); } finally { term.write = orig; }
|
package/package.json
CHANGED
package/start-kilo.js
CHANGED
|
@@ -1,17 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// thebird — start local Kilo Code backend for docs/ page
|
|
3
|
-
// usage: node start-kilo.js [--port 4780] [--origin http://localhost:8787]
|
|
4
2
|
const { spawn } = require('child_process');
|
|
3
|
+
const http = require('http');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
5
6
|
const os = require('os');
|
|
6
7
|
const args = process.argv.slice(2);
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const origin =
|
|
11
|
-
const
|
|
8
|
+
const get = (f, d) => { const i = args.indexOf(f); return i >= 0 ? args[i + 1] : d; };
|
|
9
|
+
const kiloPort = get('--port', '4780');
|
|
10
|
+
const fsPort = get('--fs-port', '4781');
|
|
11
|
+
const origin = get('--origin', 'http://localhost:8787');
|
|
12
|
+
const sandbox = path.resolve(get('--sandbox', '.sandbox'));
|
|
13
|
+
fs.mkdirSync(sandbox, { recursive: true });
|
|
12
14
|
const kiloWin = process.env.USERPROFILE + '\\AppData\\Roaming\\npm\\node_modules\\@kilocode\\cli\\node_modules\\@kilocode\\cli-windows-x64\\bin\\kilo.exe';
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const bin = os.platform() === 'win32' && fs.existsSync(kiloWin) ? kiloWin : 'kilo';
|
|
16
|
+
const kilo = spawn(bin, ['serve', '--port', kiloPort, '--hostname', '127.0.0.1', '--cors', origin], { stdio: 'inherit', env: process.env, cwd: sandbox });
|
|
17
|
+
const cors = { 'Access-Control-Allow-Origin': origin, 'Access-Control-Allow-Methods': 'GET,OPTIONS', 'Access-Control-Allow-Headers': 'content-type' };
|
|
18
|
+
const srv = http.createServer((req, res) => {
|
|
19
|
+
if (req.method === 'OPTIONS') { res.writeHead(204, cors); res.end(); return; }
|
|
20
|
+
const rel = decodeURIComponent(req.url.replace(/^\/+/, '').split('?')[0]);
|
|
21
|
+
if (rel === '__list') {
|
|
22
|
+
const out = [];
|
|
23
|
+
const walk = d => { for (const e of fs.readdirSync(d, { withFileTypes: true })) {
|
|
24
|
+
if (e.name.startsWith('.')) continue;
|
|
25
|
+
const full = path.join(d, e.name);
|
|
26
|
+
if (e.isDirectory()) walk(full);
|
|
27
|
+
else out.push(path.relative(sandbox, full).replace(/\\/g, '/'));
|
|
28
|
+
}};
|
|
29
|
+
try { walk(sandbox); } catch (e) {}
|
|
30
|
+
res.writeHead(200, { ...cors, 'Content-Type': 'application/json' });
|
|
31
|
+
res.end(JSON.stringify(out));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const full = path.resolve(path.join(sandbox, rel));
|
|
35
|
+
if (!full.startsWith(sandbox)) { res.writeHead(403, cors); res.end('forbidden'); return; }
|
|
36
|
+
fs.readFile(full, (err, data) => {
|
|
37
|
+
if (err) { res.writeHead(404, cors); res.end('not found'); return; }
|
|
38
|
+
const ct = { '.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.md': 'text/plain' }[path.extname(rel)] || 'application/octet-stream';
|
|
39
|
+
res.writeHead(200, { ...cors, 'Content-Type': ct });
|
|
40
|
+
res.end(data);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
srv.listen(fsPort, '127.0.0.1', () => console.log('[fs-bridge] sandbox=' + sandbox + ' serving http://127.0.0.1:' + fsPort));
|
|
44
|
+
kilo.on('exit', c => { srv.close(); process.exit(c || 0); });
|
|
45
|
+
process.on('SIGINT', () => { kilo.kill(); srv.close(); });
|