viagen 0.0.45 → 0.0.46
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/dist/index.js +164 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ var __export = (target, all) => {
|
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
8
|
import { execSync as execSync2 } from "child_process";
|
|
9
|
-
import { mkdirSync as
|
|
9
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
10
10
|
import { join as join6 } from "path";
|
|
11
11
|
import { loadEnv } from "vite";
|
|
12
12
|
|
|
@@ -110,7 +110,7 @@ function registerHealthRoutes(server, env, errorRef) {
|
|
|
110
110
|
);
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
|
-
const currentVersion = true ? "0.0.
|
|
113
|
+
const currentVersion = true ? "0.0.46" : "0.0.0";
|
|
114
114
|
debug("health", `version resolved: ${currentVersion}`);
|
|
115
115
|
let versionCache = null;
|
|
116
116
|
server.middlewares.use("/via/version", (_req, res) => {
|
|
@@ -2122,7 +2122,8 @@ function buildUiHtml(opts) {
|
|
|
2122
2122
|
font-family: inherit;
|
|
2123
2123
|
outline: none;
|
|
2124
2124
|
resize: none;
|
|
2125
|
-
height:
|
|
2125
|
+
height: auto;
|
|
2126
|
+
min-height: 20px;
|
|
2126
2127
|
overflow: hidden;
|
|
2127
2128
|
line-height: 1.5;
|
|
2128
2129
|
}
|
|
@@ -2151,7 +2152,45 @@ function buildUiHtml(opts) {
|
|
|
2151
2152
|
.send-btn:hover { background: #e5e5e5; color: #525252; }
|
|
2152
2153
|
.send-btn.active { background: #171717; color: #ffffff; border-color: #171717; }
|
|
2153
2154
|
.send-btn.active:hover { background: #404040; border-color: #404040; }
|
|
2155
|
+
.attach-btn {
|
|
2156
|
+
padding: 6px 6px;
|
|
2157
|
+
background: transparent;
|
|
2158
|
+
color: #a3a3a3;
|
|
2159
|
+
border: none;
|
|
2160
|
+
border-radius: 8px;
|
|
2161
|
+
cursor: pointer;
|
|
2162
|
+
display: flex;
|
|
2163
|
+
align-items: center;
|
|
2164
|
+
justify-content: center;
|
|
2165
|
+
transition: color 0.15s, background 0.15s;
|
|
2166
|
+
margin-right: 2px;
|
|
2167
|
+
}
|
|
2168
|
+
.attach-btn:hover { color: #525252; background: #f5f5f5; }
|
|
2154
2169
|
.send-btn:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
2170
|
+
.send-btn.stop { background: #ef4444; color: #ffffff; border-color: #ef4444; opacity: 1; cursor: pointer; }
|
|
2171
|
+
.send-btn.stop:hover { background: #dc2626; border-color: #dc2626; }
|
|
2172
|
+
.drop-overlay {
|
|
2173
|
+
display: none;
|
|
2174
|
+
position: absolute;
|
|
2175
|
+
inset: 0;
|
|
2176
|
+
background: rgba(37,99,235,0.08);
|
|
2177
|
+
border: 2px dashed #2563eb;
|
|
2178
|
+
border-radius: 12px;
|
|
2179
|
+
z-index: 100;
|
|
2180
|
+
align-items: center;
|
|
2181
|
+
justify-content: center;
|
|
2182
|
+
pointer-events: none;
|
|
2183
|
+
}
|
|
2184
|
+
.drop-overlay.visible { display: flex; }
|
|
2185
|
+
.drop-overlay span {
|
|
2186
|
+
font-size: 13px;
|
|
2187
|
+
font-weight: 600;
|
|
2188
|
+
color: #2563eb;
|
|
2189
|
+
background: #ffffff;
|
|
2190
|
+
padding: 8px 16px;
|
|
2191
|
+
border-radius: 8px;
|
|
2192
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
2193
|
+
}
|
|
2155
2194
|
.status-bar {
|
|
2156
2195
|
display: none;
|
|
2157
2196
|
align-items: center;
|
|
@@ -2438,7 +2477,8 @@ function buildUiHtml(opts) {
|
|
|
2438
2477
|
${hasEditor ? '<button class="tab" data-tab="files"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>Files</button>' : ""}
|
|
2439
2478
|
<button class="tab" data-tab="logs"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><line x1="8" x2="21" y1="6" y2="6"/><line x1="8" x2="21" y1="12" y2="12"/><line x1="8" x2="21" y1="18" y2="18"/><line x1="3" x2="3.01" y1="6" y2="6"/><line x1="3" x2="3.01" y1="12" y2="12"/><line x1="3" x2="3.01" y1="18" y2="18"/></svg>Logs</button>
|
|
2440
2479
|
</div>` : ""}
|
|
2441
|
-
<div id="chat-view" style="display:flex;flex-direction:column;flex:1;overflow:hidden;">
|
|
2480
|
+
<div id="chat-view" style="display:flex;flex-direction:column;flex:1;overflow:hidden;position:relative;">
|
|
2481
|
+
<div class="drop-overlay" id="drop-overlay"><span>Drop files to upload</span></div>
|
|
2442
2482
|
<div class="setup-banner" id="setup-banner"></div>
|
|
2443
2483
|
|
|
2444
2484
|
<div class="activity-bar" id="activity-bar"></div>
|
|
@@ -2448,6 +2488,8 @@ function buildUiHtml(opts) {
|
|
|
2448
2488
|
<div class="input-wrap" id="input-wrap">
|
|
2449
2489
|
<textarea id="input" rows="1" placeholder="What do you want to build?" autofocus></textarea>
|
|
2450
2490
|
<div class="input-bottom">
|
|
2491
|
+
<input type="file" id="file-input" multiple style="display:none;">
|
|
2492
|
+
<button class="attach-btn" id="attach-btn" title="Upload files"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg></button>
|
|
2451
2493
|
<button class="send-btn" id="send-btn"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline></svg></button>
|
|
2452
2494
|
</div>
|
|
2453
2495
|
</div>
|
|
@@ -2545,7 +2587,7 @@ function buildUiHtml(opts) {
|
|
|
2545
2587
|
|
|
2546
2588
|
function scrollToBottom() {
|
|
2547
2589
|
requestAnimationFrame(function() {
|
|
2548
|
-
|
|
2590
|
+
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
2549
2591
|
});
|
|
2550
2592
|
}
|
|
2551
2593
|
|
|
@@ -3202,23 +3244,35 @@ function buildUiHtml(opts) {
|
|
|
3202
3244
|
}
|
|
3203
3245
|
inputEl.addEventListener('input', function() { autoResize(); updateSendActive(); });
|
|
3204
3246
|
|
|
3247
|
+
var currentAbort = null;
|
|
3205
3248
|
function setStreaming(v) {
|
|
3206
3249
|
isStreaming = v;
|
|
3207
3250
|
inputEl.disabled = v;
|
|
3208
3251
|
document.getElementById('input-wrap').classList.toggle('disabled', v);
|
|
3209
|
-
|
|
3210
|
-
|
|
3252
|
+
if (v) {
|
|
3253
|
+
sendBtn.disabled = false;
|
|
3254
|
+
sendBtn.classList.add('stop');
|
|
3255
|
+
sendBtn.classList.remove('active');
|
|
3256
|
+
sendBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" stroke="none"><rect x="6" y="6" width="12" height="12" rx="2"></rect></svg>';
|
|
3257
|
+
} else {
|
|
3258
|
+
sendBtn.classList.remove('stop');
|
|
3259
|
+
sendBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline></svg>';
|
|
3260
|
+
currentAbort = null;
|
|
3261
|
+
}
|
|
3211
3262
|
updateSendActive();
|
|
3212
3263
|
if (v) stopHistoryPolling();
|
|
3213
3264
|
else startHistoryPolling();
|
|
3214
3265
|
}
|
|
3215
3266
|
|
|
3216
3267
|
async function sendRaw(text) {
|
|
3268
|
+
var abort = new AbortController();
|
|
3269
|
+
currentAbort = abort;
|
|
3217
3270
|
try {
|
|
3218
3271
|
var res = await fetch('/via/chat', {
|
|
3219
3272
|
method: 'POST',
|
|
3220
3273
|
headers: { 'Content-Type': 'application/json' },
|
|
3221
3274
|
body: JSON.stringify({ message: text }),
|
|
3275
|
+
signal: abort.signal,
|
|
3222
3276
|
});
|
|
3223
3277
|
|
|
3224
3278
|
var reader = res.body.getReader();
|
|
@@ -3255,13 +3309,17 @@ function buildUiHtml(opts) {
|
|
|
3255
3309
|
}
|
|
3256
3310
|
}
|
|
3257
3311
|
} catch (e) {
|
|
3258
|
-
if (
|
|
3312
|
+
if (abort.signal.aborted) {
|
|
3313
|
+
// User cancelled \u2014 not an error
|
|
3314
|
+
} else if (!unloading) {
|
|
3315
|
+
addErrorBlock('Connection failed');
|
|
3316
|
+
}
|
|
3259
3317
|
}
|
|
3260
3318
|
|
|
3261
3319
|
closeToolGroup();
|
|
3262
3320
|
closeAllTaskGroups('completed');
|
|
3263
3321
|
hideActivity(lastUsage);
|
|
3264
|
-
playDoneSound();
|
|
3322
|
+
if (!abort.signal.aborted) playDoneSound();
|
|
3265
3323
|
historyTimestamp = Date.now();
|
|
3266
3324
|
setStreaming(false);
|
|
3267
3325
|
markCommittedFiles();
|
|
@@ -3288,7 +3346,13 @@ function buildUiHtml(opts) {
|
|
|
3288
3346
|
await sendRaw(text);
|
|
3289
3347
|
}
|
|
3290
3348
|
|
|
3291
|
-
sendBtn.addEventListener('click',
|
|
3349
|
+
sendBtn.addEventListener('click', function() {
|
|
3350
|
+
if (isStreaming && currentAbort) {
|
|
3351
|
+
currentAbort.abort();
|
|
3352
|
+
return;
|
|
3353
|
+
}
|
|
3354
|
+
send();
|
|
3355
|
+
});
|
|
3292
3356
|
inputEl.addEventListener('keydown', function (e) {
|
|
3293
3357
|
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); }
|
|
3294
3358
|
});
|
|
@@ -3300,6 +3364,79 @@ function buildUiHtml(opts) {
|
|
|
3300
3364
|
currentTextSpan = null;
|
|
3301
3365
|
inputEl.focus();
|
|
3302
3366
|
});
|
|
3367
|
+
// \u2500\u2500 Drag & drop file upload \u2500\u2500
|
|
3368
|
+
var chatView = document.getElementById('chat-view');
|
|
3369
|
+
var dropOverlay = document.getElementById('drop-overlay');
|
|
3370
|
+
var dragCounter = 0;
|
|
3371
|
+
chatView.addEventListener('dragenter', function(e) {
|
|
3372
|
+
e.preventDefault();
|
|
3373
|
+
dragCounter++;
|
|
3374
|
+
dropOverlay.classList.add('visible');
|
|
3375
|
+
});
|
|
3376
|
+
chatView.addEventListener('dragleave', function(e) {
|
|
3377
|
+
e.preventDefault();
|
|
3378
|
+
dragCounter--;
|
|
3379
|
+
if (dragCounter <= 0) { dragCounter = 0; dropOverlay.classList.remove('visible'); }
|
|
3380
|
+
});
|
|
3381
|
+
chatView.addEventListener('dragover', function(e) { e.preventDefault(); });
|
|
3382
|
+
function readFileAsBase64(file) {
|
|
3383
|
+
return new Promise(function(resolve) {
|
|
3384
|
+
var reader = new FileReader();
|
|
3385
|
+
reader.onload = function() {
|
|
3386
|
+
var result = reader.result;
|
|
3387
|
+
resolve(result.split(',')[1] || result);
|
|
3388
|
+
};
|
|
3389
|
+
reader.readAsDataURL(file);
|
|
3390
|
+
});
|
|
3391
|
+
}
|
|
3392
|
+
var MAX_UPLOAD_SIZE = 10 * 1024 * 1024; // 10MB
|
|
3393
|
+
async function uploadFiles(files) {
|
|
3394
|
+
if (!files || !files.length) return;
|
|
3395
|
+
var uploaded = [];
|
|
3396
|
+
for (var i = 0; i < files.length; i++) {
|
|
3397
|
+
var file = files[i];
|
|
3398
|
+
if (file.size > MAX_UPLOAD_SIZE) {
|
|
3399
|
+
addErrorBlock(file.name + ' is too large (max 10MB)');
|
|
3400
|
+
continue;
|
|
3401
|
+
}
|
|
3402
|
+
var isText = file.type.startsWith('text/') || /\\.(json|md|txt|csv|js|ts|jsx|tsx|css|html|xml|yaml|yml|toml|sh|py|rb|go|rs|sql|env|log|cfg|ini|conf)$/i.test(file.name);
|
|
3403
|
+
try {
|
|
3404
|
+
var payload;
|
|
3405
|
+
if (isText) {
|
|
3406
|
+
payload = { path: '.viagen/uploads/' + file.name, content: await file.text() };
|
|
3407
|
+
} else {
|
|
3408
|
+
payload = { path: '.viagen/uploads/' + file.name, content: await readFileAsBase64(file), encoding: 'base64' };
|
|
3409
|
+
}
|
|
3410
|
+
await fetch('/via/file', {
|
|
3411
|
+
method: 'POST',
|
|
3412
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3413
|
+
body: JSON.stringify(payload),
|
|
3414
|
+
});
|
|
3415
|
+
uploaded.push(file.name);
|
|
3416
|
+
} catch (err) {
|
|
3417
|
+
addErrorBlock('Failed to upload ' + file.name);
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
if (uploaded.length > 0) {
|
|
3421
|
+
var msg = uploaded.length === 1
|
|
3422
|
+
? 'I just uploaded a file: .viagen/uploads/' + uploaded[0] + '. Take a look and let me know what you think.'
|
|
3423
|
+
: 'I just uploaded ' + uploaded.length + ' files to .viagen/uploads/: ' + uploaded.join(', ') + '. Take a look and let me know what you think.';
|
|
3424
|
+
inputEl.value = msg;
|
|
3425
|
+
send();
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
chatView.addEventListener('drop', async function(e) {
|
|
3429
|
+
e.preventDefault();
|
|
3430
|
+
dragCounter = 0;
|
|
3431
|
+
dropOverlay.classList.remove('visible');
|
|
3432
|
+
uploadFiles(e.dataTransfer ? e.dataTransfer.files : []);
|
|
3433
|
+
});
|
|
3434
|
+
|
|
3435
|
+
// \u2500\u2500 Paperclip file picker \u2500\u2500
|
|
3436
|
+
var fileInput = document.getElementById('file-input');
|
|
3437
|
+
document.getElementById('attach-btn').addEventListener('click', function() { fileInput.click(); });
|
|
3438
|
+
fileInput.addEventListener('change', function() { uploadFiles(fileInput.files); fileInput.value = ''; });
|
|
3439
|
+
|
|
3303
3440
|
// \u2500\u2500 View mode detection \u2500\u2500
|
|
3304
3441
|
var viewMode = 'overlay';
|
|
3305
3442
|
if (window.self === window.top) viewMode = 'standalone';
|
|
@@ -3965,8 +4102,14 @@ function createAuthMiddleware(token) {
|
|
|
3965
4102
|
}
|
|
3966
4103
|
|
|
3967
4104
|
// src/files.ts
|
|
3968
|
-
import {
|
|
3969
|
-
|
|
4105
|
+
import {
|
|
4106
|
+
readdirSync,
|
|
4107
|
+
readFileSync as readFileSync2,
|
|
4108
|
+
writeFileSync as writeFileSync3,
|
|
4109
|
+
mkdirSync as mkdirSync2,
|
|
4110
|
+
statSync
|
|
4111
|
+
} from "fs";
|
|
4112
|
+
import { join as join3, resolve, relative, basename, dirname } from "path";
|
|
3970
4113
|
function readBody2(req) {
|
|
3971
4114
|
return new Promise((resolve3, reject) => {
|
|
3972
4115
|
let body = "";
|
|
@@ -4133,14 +4276,20 @@ function registerFileRoutes(server, opts) {
|
|
|
4133
4276
|
res.end(JSON.stringify({ error: "Missing path or content" }));
|
|
4134
4277
|
return;
|
|
4135
4278
|
}
|
|
4136
|
-
|
|
4279
|
+
const isViagen = writePath.startsWith(".viagen/");
|
|
4280
|
+
if (!isViagen && !isPathAllowed(writePath, resolvedPatterns, opts.projectRoot)) {
|
|
4137
4281
|
res.statusCode = 403;
|
|
4138
4282
|
res.end(JSON.stringify({ error: "Path not in editable list" }));
|
|
4139
4283
|
return;
|
|
4140
4284
|
}
|
|
4141
4285
|
const abs = resolve(opts.projectRoot, writePath);
|
|
4142
4286
|
try {
|
|
4143
|
-
|
|
4287
|
+
mkdirSync2(dirname(abs), { recursive: true });
|
|
4288
|
+
if (body.encoding === "base64") {
|
|
4289
|
+
writeFileSync3(abs, Buffer.from(writeContent, "base64"));
|
|
4290
|
+
} else {
|
|
4291
|
+
writeFileSync3(abs, writeContent, "utf-8");
|
|
4292
|
+
}
|
|
4144
4293
|
res.setHeader("Content-Type", "application/json");
|
|
4145
4294
|
res.end(JSON.stringify({ status: "ok", path: writePath }));
|
|
4146
4295
|
} catch (err) {
|
|
@@ -18621,7 +18770,7 @@ function viagen(options) {
|
|
|
18621
18770
|
logBuffer.init(projectRoot);
|
|
18622
18771
|
wrapLogger(config2.logger, logBuffer);
|
|
18623
18772
|
const viagenDir = join6(projectRoot, ".viagen");
|
|
18624
|
-
|
|
18773
|
+
mkdirSync3(viagenDir, { recursive: true });
|
|
18625
18774
|
writeFileSync4(
|
|
18626
18775
|
join6(viagenDir, "config.json"),
|
|
18627
18776
|
JSON.stringify({
|