viagen 0.0.53 → 0.0.54
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 +421 -151
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -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.54" : "0.0.0";
|
|
114
114
|
debug("health", `version resolved: ${currentVersion}`);
|
|
115
115
|
let versionCache = null;
|
|
116
116
|
server.middlewares.use("/via/version", (_req, res) => {
|
|
@@ -758,23 +758,14 @@ data: ${JSON.stringify(doneData)}
|
|
|
758
758
|
`);
|
|
759
759
|
res.end();
|
|
760
760
|
}
|
|
761
|
-
if (opts.
|
|
761
|
+
if (opts.viagenClient && opts.projectId) {
|
|
762
762
|
const taskId = opts.env["VIAGEN_TASK_ID"];
|
|
763
|
-
if (taskId && (event.inputTokens || event.outputTokens
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
"Content-Type": "application/json",
|
|
768
|
-
Authorization: `Bearer ${opts.env["VIAGEN_AUTH_TOKEN"]}`
|
|
769
|
-
},
|
|
770
|
-
body: JSON.stringify({
|
|
771
|
-
taskId,
|
|
772
|
-
...event.inputTokens != null && { inputTokens: event.inputTokens },
|
|
773
|
-
...event.outputTokens != null && { outputTokens: event.outputTokens },
|
|
774
|
-
...event.costUsd != null && { costUsd: event.costUsd }
|
|
775
|
-
})
|
|
763
|
+
if (taskId && (event.inputTokens || event.outputTokens)) {
|
|
764
|
+
opts.viagenClient.tasks.update(opts.projectId, taskId, {
|
|
765
|
+
...event.inputTokens != null && { inputTokens: event.inputTokens },
|
|
766
|
+
...event.outputTokens != null && { outputTokens: event.outputTokens }
|
|
776
767
|
}).catch((err) => {
|
|
777
|
-
debug("chat", `usage
|
|
768
|
+
debug("chat", `usage report failed: ${err}`);
|
|
778
769
|
});
|
|
779
770
|
}
|
|
780
771
|
}
|
|
@@ -797,6 +788,213 @@ data: ${JSON.stringify(doneData)}
|
|
|
797
788
|
}
|
|
798
789
|
|
|
799
790
|
// src/overlay.ts
|
|
791
|
+
function buildPreviewScript() {
|
|
792
|
+
return (
|
|
793
|
+
/* js */
|
|
794
|
+
`
|
|
795
|
+
(function() {
|
|
796
|
+
if (document.getElementById('viagen-preview-btn')) return;
|
|
797
|
+
|
|
798
|
+
var btn = document.createElement('button');
|
|
799
|
+
btn.id = 'viagen-preview-btn';
|
|
800
|
+
btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:inline-block;vertical-align:middle;margin-right:5px;"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg><span style="vertical-align:middle;">Feedback</span>';
|
|
801
|
+
btn.style.cssText = 'position:fixed;bottom:16px;left:16px;z-index:99998;padding:8px 14px;background:#ffffff;color:#525252;border:1px solid #e5e5e5;border-radius:20px;font-size:12px;font-weight:500;font-family:Geist,-apple-system,BlinkMacSystemFont,sans-serif;cursor:pointer;letter-spacing:-0.01em;transition:border-color 0.15s,color 0.15s,box-shadow 0.15s;box-shadow:0 1px 3px rgba(0,0,0,0.08),0 1px 2px rgba(0,0,0,0.04);display:flex;align-items:center;';
|
|
802
|
+
btn.onmouseenter = function() { btn.style.borderColor = '#d4d4d4'; btn.style.color = '#171717'; btn.style.boxShadow = '0 2px 6px rgba(0,0,0,0.1),0 1px 3px rgba(0,0,0,0.06)'; };
|
|
803
|
+
btn.onmouseleave = function() { btn.style.borderColor = '#e5e5e5'; btn.style.color = '#525252'; btn.style.boxShadow = '0 1px 3px rgba(0,0,0,0.08),0 1px 2px rgba(0,0,0,0.04)'; };
|
|
804
|
+
|
|
805
|
+
btn.addEventListener('click', function() { openFeedback(); });
|
|
806
|
+
document.body.appendChild(btn);
|
|
807
|
+
|
|
808
|
+
function loadHtml2Canvas() {
|
|
809
|
+
return new Promise(function(resolve, reject) {
|
|
810
|
+
if (typeof window.html2canvas !== 'undefined') { resolve(window.html2canvas); return; }
|
|
811
|
+
var s = document.createElement('script');
|
|
812
|
+
s.src = 'https://unpkg.com/html2canvas@1.4.1/dist/html2canvas.min.js';
|
|
813
|
+
s.onload = function() { resolve(window.html2canvas); };
|
|
814
|
+
s.onerror = function() { reject(new Error('Failed to load html2canvas')); };
|
|
815
|
+
document.head.appendChild(s);
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function openFeedback() {
|
|
820
|
+
btn.disabled = true;
|
|
821
|
+
var origHTML = btn.innerHTML;
|
|
822
|
+
btn.innerHTML = '<span style="vertical-align:middle;">Capturing\u2026</span>';
|
|
823
|
+
|
|
824
|
+
loadHtml2Canvas().then(function(h2c) {
|
|
825
|
+
return h2c(document.documentElement, {
|
|
826
|
+
useCORS: true,
|
|
827
|
+
allowTaint: true,
|
|
828
|
+
logging: false,
|
|
829
|
+
scale: Math.min(window.devicePixelRatio || 1, 2),
|
|
830
|
+
});
|
|
831
|
+
}).then(function(canvas) {
|
|
832
|
+
var dataUrl = canvas.toDataURL('image/jpeg', 0.82);
|
|
833
|
+
btn.disabled = false;
|
|
834
|
+
btn.innerHTML = origHTML;
|
|
835
|
+
showModal(dataUrl);
|
|
836
|
+
}).catch(function(e) {
|
|
837
|
+
console.warn('[viagen-preview] Screenshot failed:', e);
|
|
838
|
+
btn.disabled = false;
|
|
839
|
+
btn.innerHTML = origHTML;
|
|
840
|
+
showModal(null);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function showModal(screenshotDataUrl) {
|
|
845
|
+
var overlay = document.createElement('div');
|
|
846
|
+
overlay.id = 'viagen-preview-modal';
|
|
847
|
+
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:99999;display:flex;align-items:center;justify-content:center;font-family:Geist,-apple-system,BlinkMacSystemFont,sans-serif;';
|
|
848
|
+
|
|
849
|
+
var modal = document.createElement('div');
|
|
850
|
+
modal.style.cssText = 'background:#fff;border-radius:16px;padding:24px;max-width:560px;width:calc(100% - 32px);max-height:90vh;overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,0.2);box-sizing:border-box;';
|
|
851
|
+
|
|
852
|
+
/* Header */
|
|
853
|
+
var header = document.createElement('div');
|
|
854
|
+
header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;';
|
|
855
|
+
var title = document.createElement('h2');
|
|
856
|
+
title.textContent = 'Send Feedback';
|
|
857
|
+
title.style.cssText = 'margin:0;font-size:16px;font-weight:600;color:#171717;';
|
|
858
|
+
var closeBtn = document.createElement('button');
|
|
859
|
+
closeBtn.innerHTML = '×';
|
|
860
|
+
closeBtn.style.cssText = 'background:none;border:none;font-size:22px;cursor:pointer;color:#a3a3a3;padding:0;line-height:1;transition:color 0.15s;';
|
|
861
|
+
closeBtn.onmouseenter = function() { closeBtn.style.color = '#171717'; };
|
|
862
|
+
closeBtn.onmouseleave = function() { closeBtn.style.color = '#a3a3a3'; };
|
|
863
|
+
closeBtn.onclick = function() { overlay.remove(); };
|
|
864
|
+
header.appendChild(title);
|
|
865
|
+
header.appendChild(closeBtn);
|
|
866
|
+
modal.appendChild(header);
|
|
867
|
+
|
|
868
|
+
/* Screenshot preview */
|
|
869
|
+
if (screenshotDataUrl) {
|
|
870
|
+
var imgWrap = document.createElement('div');
|
|
871
|
+
imgWrap.style.cssText = 'margin-bottom:16px;border-radius:8px;overflow:hidden;border:1px solid #e5e5e5;background:#f5f5f5;';
|
|
872
|
+
var img = document.createElement('img');
|
|
873
|
+
img.src = screenshotDataUrl;
|
|
874
|
+
img.style.cssText = 'width:100%;display:block;max-height:220px;object-fit:cover;object-position:top;';
|
|
875
|
+
imgWrap.appendChild(img);
|
|
876
|
+
modal.appendChild(imgWrap);
|
|
877
|
+
} else {
|
|
878
|
+
var pageRef = document.createElement('div');
|
|
879
|
+
pageRef.textContent = 'Page: ' + window.location.href;
|
|
880
|
+
pageRef.style.cssText = 'margin-bottom:16px;padding:10px 12px;background:#f5f5f5;border-radius:8px;font-size:12px;color:#737373;word-break:break-all;';
|
|
881
|
+
modal.appendChild(pageRef);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/* Textarea */
|
|
885
|
+
var label = document.createElement('label');
|
|
886
|
+
label.textContent = 'Describe your feedback';
|
|
887
|
+
label.style.cssText = 'display:block;font-size:13px;font-weight:500;color:#525252;margin-bottom:8px;';
|
|
888
|
+
modal.appendChild(label);
|
|
889
|
+
|
|
890
|
+
var textarea = document.createElement('textarea');
|
|
891
|
+
textarea.placeholder = 'e.g. The button color looks off, the layout breaks on mobile\u2026';
|
|
892
|
+
textarea.style.cssText = 'width:100%;min-height:96px;border:1px solid #e5e5e5;border-radius:8px;padding:12px;font-size:14px;font-family:inherit;resize:vertical;box-sizing:border-box;outline:none;transition:border-color 0.15s;color:#171717;';
|
|
893
|
+
textarea.onfocus = function() { textarea.style.borderColor = '#a3a3a3'; };
|
|
894
|
+
textarea.onblur = function() { textarea.style.borderColor = '#e5e5e5'; };
|
|
895
|
+
modal.appendChild(textarea);
|
|
896
|
+
|
|
897
|
+
/* Status */
|
|
898
|
+
var statusEl = document.createElement('div');
|
|
899
|
+
statusEl.style.cssText = 'margin-top:10px;font-size:13px;color:#737373;display:none;min-height:20px;';
|
|
900
|
+
modal.appendChild(statusEl);
|
|
901
|
+
|
|
902
|
+
/* Buttons */
|
|
903
|
+
var btnRow = document.createElement('div');
|
|
904
|
+
btnRow.style.cssText = 'display:flex;gap:8px;margin-top:16px;justify-content:flex-end;';
|
|
905
|
+
var cancelBtn = document.createElement('button');
|
|
906
|
+
cancelBtn.textContent = 'Cancel';
|
|
907
|
+
cancelBtn.style.cssText = 'padding:9px 16px;background:#f5f5f5;color:#525252;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;font-family:inherit;transition:background 0.15s;';
|
|
908
|
+
cancelBtn.onmouseenter = function() { cancelBtn.style.background = '#e5e5e5'; };
|
|
909
|
+
cancelBtn.onmouseleave = function() { cancelBtn.style.background = '#f5f5f5'; };
|
|
910
|
+
cancelBtn.onclick = function() { overlay.remove(); };
|
|
911
|
+
var submitBtn = document.createElement('button');
|
|
912
|
+
submitBtn.textContent = 'Create Task';
|
|
913
|
+
submitBtn.style.cssText = 'padding:9px 16px;background:#171717;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;font-family:inherit;transition:background 0.15s;';
|
|
914
|
+
submitBtn.onmouseenter = function() { if (!submitBtn.disabled) submitBtn.style.background = '#404040'; };
|
|
915
|
+
submitBtn.onmouseleave = function() { if (!submitBtn.disabled) submitBtn.style.background = '#171717'; };
|
|
916
|
+
|
|
917
|
+
submitBtn.addEventListener('click', function() {
|
|
918
|
+
var feedback = textarea.value.trim();
|
|
919
|
+
if (!feedback) {
|
|
920
|
+
textarea.style.borderColor = '#ef4444';
|
|
921
|
+
textarea.focus();
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
submitBtn.disabled = true;
|
|
925
|
+
cancelBtn.disabled = true;
|
|
926
|
+
submitBtn.textContent = 'Creating\u2026';
|
|
927
|
+
submitBtn.style.opacity = '0.7';
|
|
928
|
+
statusEl.style.display = 'block';
|
|
929
|
+
statusEl.style.color = '#737373';
|
|
930
|
+
statusEl.textContent = 'Submitting your feedback\u2026';
|
|
931
|
+
|
|
932
|
+
var payload = {
|
|
933
|
+
prompt: feedback,
|
|
934
|
+
pageUrl: window.location.href,
|
|
935
|
+
screenshot: screenshotDataUrl || undefined,
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
fetch('/via/preview/task', {
|
|
939
|
+
method: 'POST',
|
|
940
|
+
headers: { 'Content-Type': 'application/json' },
|
|
941
|
+
body: JSON.stringify(payload),
|
|
942
|
+
}).then(function(res) {
|
|
943
|
+
if (!res.ok) {
|
|
944
|
+
return res.json().catch(function() { return {}; }).then(function(e) {
|
|
945
|
+
throw new Error(e.error || 'HTTP ' + res.status);
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
return res.json();
|
|
949
|
+
}).then(function() {
|
|
950
|
+
/* Success state */
|
|
951
|
+
modal.innerHTML = '';
|
|
952
|
+
var successDiv = document.createElement('div');
|
|
953
|
+
successDiv.style.cssText = 'text-align:center;padding:32px 24px;';
|
|
954
|
+
var check = document.createElement('div');
|
|
955
|
+
check.style.cssText = 'width:48px;height:48px;background:#22c55e;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 16px;color:#fff;font-size:22px;font-weight:700;';
|
|
956
|
+
check.textContent = '\\u2713';
|
|
957
|
+
var h3 = document.createElement('h3');
|
|
958
|
+
h3.textContent = 'Task Created!';
|
|
959
|
+
h3.style.cssText = 'margin:0 0 8px;font-size:16px;font-weight:600;color:#171717;font-family:Geist,-apple-system,BlinkMacSystemFont,sans-serif;';
|
|
960
|
+
var p = document.createElement('p');
|
|
961
|
+
p.textContent = 'Your feedback has been submitted successfully.';
|
|
962
|
+
p.style.cssText = 'margin:0;color:#737373;font-size:14px;font-family:Geist,-apple-system,BlinkMacSystemFont,sans-serif;';
|
|
963
|
+
successDiv.appendChild(check);
|
|
964
|
+
successDiv.appendChild(h3);
|
|
965
|
+
successDiv.appendChild(p);
|
|
966
|
+
modal.appendChild(successDiv);
|
|
967
|
+
setTimeout(function() { overlay.remove(); }, 2500);
|
|
968
|
+
}).catch(function(err) {
|
|
969
|
+
submitBtn.disabled = false;
|
|
970
|
+
cancelBtn.disabled = false;
|
|
971
|
+
submitBtn.textContent = 'Create Task';
|
|
972
|
+
submitBtn.style.opacity = '1';
|
|
973
|
+
statusEl.style.color = '#ef4444';
|
|
974
|
+
statusEl.textContent = 'Error: ' + (err.message || 'Failed to submit. Please try again.');
|
|
975
|
+
});
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
btnRow.appendChild(cancelBtn);
|
|
979
|
+
btnRow.appendChild(submitBtn);
|
|
980
|
+
modal.appendChild(btnRow);
|
|
981
|
+
overlay.appendChild(modal);
|
|
982
|
+
|
|
983
|
+
overlay.addEventListener('click', function(e) {
|
|
984
|
+
if (e.target === overlay) overlay.remove();
|
|
985
|
+
});
|
|
986
|
+
var escHandler = function(e) {
|
|
987
|
+
if (e.key === 'Escape') { overlay.remove(); document.removeEventListener('keydown', escHandler); }
|
|
988
|
+
};
|
|
989
|
+
document.addEventListener('keydown', escHandler);
|
|
990
|
+
|
|
991
|
+
document.body.appendChild(overlay);
|
|
992
|
+
setTimeout(function() { textarea.focus(); }, 50);
|
|
993
|
+
}
|
|
994
|
+
})();
|
|
995
|
+
`
|
|
996
|
+
);
|
|
997
|
+
}
|
|
800
998
|
function buildClientScript(opts) {
|
|
801
999
|
const pos = opts.position;
|
|
802
1000
|
const pw = opts.panelWidth;
|
|
@@ -4334,7 +4532,9 @@ function registerFileRoutes(server, opts) {
|
|
|
4334
4532
|
// src/inject.ts
|
|
4335
4533
|
var SCRIPT_TAG = '<script src="/via/client.js" defer></script>';
|
|
4336
4534
|
var MARKER = "viagen-toggle";
|
|
4337
|
-
|
|
4535
|
+
var PREVIEW_SCRIPT_TAG = '<script src="/via/preview.js" defer></script>';
|
|
4536
|
+
var PREVIEW_MARKER = "viagen-preview-btn";
|
|
4537
|
+
function createScriptInjectionMiddleware(scriptTag, marker) {
|
|
4338
4538
|
return function injectMiddleware(req, res, next) {
|
|
4339
4539
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
4340
4540
|
return next();
|
|
@@ -4359,7 +4559,7 @@ function createInjectionMiddleware() {
|
|
|
4359
4559
|
if (!isHtmlResponse()) return chunk;
|
|
4360
4560
|
const str = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : null;
|
|
4361
4561
|
if (!str) return chunk;
|
|
4362
|
-
if (str.includes(
|
|
4562
|
+
if (str.includes(marker) || str.includes(scriptTag)) {
|
|
4363
4563
|
injected = true;
|
|
4364
4564
|
return chunk;
|
|
4365
4565
|
}
|
|
@@ -4370,7 +4570,7 @@ function createInjectionMiddleware() {
|
|
|
4370
4570
|
if (!res.headersSent) {
|
|
4371
4571
|
res.removeHeader("content-length");
|
|
4372
4572
|
}
|
|
4373
|
-
const result = str.slice(0, idx) +
|
|
4573
|
+
const result = str.slice(0, idx) + scriptTag + str.slice(idx);
|
|
4374
4574
|
return typeof chunk === "string" ? result : Buffer.from(result, "utf-8");
|
|
4375
4575
|
}
|
|
4376
4576
|
res.write = function(chunk, ...args) {
|
|
@@ -4390,6 +4590,12 @@ function createInjectionMiddleware() {
|
|
|
4390
4590
|
next();
|
|
4391
4591
|
};
|
|
4392
4592
|
}
|
|
4593
|
+
function createInjectionMiddleware() {
|
|
4594
|
+
return createScriptInjectionMiddleware(SCRIPT_TAG, MARKER);
|
|
4595
|
+
}
|
|
4596
|
+
function createPreviewInjectionMiddleware() {
|
|
4597
|
+
return createScriptInjectionMiddleware(PREVIEW_SCRIPT_TAG, PREVIEW_MARKER);
|
|
4598
|
+
}
|
|
4393
4599
|
|
|
4394
4600
|
// src/git.ts
|
|
4395
4601
|
import { readFileSync as readFileSync3 } from "fs";
|
|
@@ -18433,17 +18639,14 @@ function date4(params) {
|
|
|
18433
18639
|
// node_modules/zod/v4/classic/external.js
|
|
18434
18640
|
config(en_default());
|
|
18435
18641
|
|
|
18436
|
-
// src/
|
|
18642
|
+
// src/tools.ts
|
|
18437
18643
|
import {
|
|
18438
18644
|
createSdkMcpServer,
|
|
18439
18645
|
tool
|
|
18440
18646
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
18441
|
-
import {
|
|
18442
|
-
listTasks,
|
|
18443
|
-
getTask,
|
|
18444
|
-
createTask
|
|
18445
|
-
} from "viagen-sdk/sandbox";
|
|
18446
18647
|
function createViagenTools(config2) {
|
|
18648
|
+
const { client, projectId } = config2;
|
|
18649
|
+
const taskId = process.env["VIAGEN_TASK_ID"];
|
|
18447
18650
|
const tools = [
|
|
18448
18651
|
tool(
|
|
18449
18652
|
"viagen_update_task",
|
|
@@ -18459,112 +18662,94 @@ function createViagenTools(config2) {
|
|
|
18459
18662
|
prReviewStatus: external_exports.string().optional().describe("PR review outcome \u2014 e.g. 'pass', 'flag', or 'fail'.")
|
|
18460
18663
|
},
|
|
18461
18664
|
async (args) => {
|
|
18462
|
-
const
|
|
18463
|
-
if (!
|
|
18665
|
+
const id = args.taskId || taskId;
|
|
18666
|
+
if (!id) {
|
|
18464
18667
|
return {
|
|
18465
18668
|
content: [{ type: "text", text: "Error: No taskId provided and VIAGEN_TASK_ID is not set." }]
|
|
18466
18669
|
};
|
|
18467
18670
|
}
|
|
18468
|
-
const callbackUrl = process.env["VIAGEN_CALLBACK_URL"];
|
|
18469
|
-
const authToken = process.env["VIAGEN_AUTH_TOKEN"];
|
|
18470
18671
|
const internalStatus = args.status === "review" ? "validating" : args.status === "completed" ? "completed" : args.status;
|
|
18471
|
-
|
|
18472
|
-
|
|
18473
|
-
headers: {
|
|
18474
|
-
"Content-Type": "application/json",
|
|
18475
|
-
Authorization: `Bearer ${authToken}`
|
|
18476
|
-
},
|
|
18477
|
-
body: JSON.stringify({
|
|
18478
|
-
taskId,
|
|
18672
|
+
try {
|
|
18673
|
+
await client.tasks.update(projectId, id, {
|
|
18479
18674
|
...internalStatus && { status: internalStatus },
|
|
18480
18675
|
...args.prUrl && { prUrl: args.prUrl },
|
|
18481
18676
|
result: args.result,
|
|
18482
18677
|
...args.inputTokens != null && { inputTokens: args.inputTokens },
|
|
18483
18678
|
...args.outputTokens != null && { outputTokens: args.outputTokens },
|
|
18484
|
-
...args.costUsd != null && { costUsd: args.costUsd },
|
|
18485
18679
|
...args.prReviewStatus && { prReviewStatus: args.prReviewStatus }
|
|
18486
|
-
})
|
|
18487
|
-
});
|
|
18488
|
-
if (!res.ok) {
|
|
18489
|
-
const text = await res.text().catch(() => "");
|
|
18680
|
+
});
|
|
18490
18681
|
return {
|
|
18491
|
-
content: [{ type: "text", text: `
|
|
18682
|
+
content: [{ type: "text", text: `Task ${id} updated.${args.status ? ` Status: '${args.status}'.` : ""}${args.prReviewStatus ? ` PR review: '${args.prReviewStatus}'.` : ""}` }]
|
|
18683
|
+
};
|
|
18684
|
+
} catch (err) {
|
|
18685
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
18686
|
+
return {
|
|
18687
|
+
content: [{ type: "text", text: `Error updating task: ${message}` }]
|
|
18492
18688
|
};
|
|
18493
18689
|
}
|
|
18690
|
+
}
|
|
18691
|
+
),
|
|
18692
|
+
tool(
|
|
18693
|
+
"viagen_list_tasks",
|
|
18694
|
+
"List tasks in the current project. Optionally filter by status.",
|
|
18695
|
+
{
|
|
18696
|
+
status: external_exports.enum(["ready", "running", "validating", "completed", "timed_out"]).optional().describe("Filter tasks by status.")
|
|
18697
|
+
},
|
|
18698
|
+
async (args) => {
|
|
18699
|
+
const tasks = await client.tasks.list(projectId, args.status);
|
|
18700
|
+
return {
|
|
18701
|
+
content: [
|
|
18702
|
+
{
|
|
18703
|
+
type: "text",
|
|
18704
|
+
text: JSON.stringify(tasks, null, 2)
|
|
18705
|
+
}
|
|
18706
|
+
]
|
|
18707
|
+
};
|
|
18708
|
+
}
|
|
18709
|
+
),
|
|
18710
|
+
tool(
|
|
18711
|
+
"viagen_get_task",
|
|
18712
|
+
"Get full details of a specific task by ID.",
|
|
18713
|
+
{
|
|
18714
|
+
taskId: external_exports.string().describe("The task ID to retrieve.")
|
|
18715
|
+
},
|
|
18716
|
+
async (args) => {
|
|
18717
|
+
const task = await client.tasks.get(projectId, args.taskId);
|
|
18718
|
+
return {
|
|
18719
|
+
content: [
|
|
18720
|
+
{
|
|
18721
|
+
type: "text",
|
|
18722
|
+
text: JSON.stringify(task, null, 2)
|
|
18723
|
+
}
|
|
18724
|
+
]
|
|
18725
|
+
};
|
|
18726
|
+
}
|
|
18727
|
+
),
|
|
18728
|
+
tool(
|
|
18729
|
+
"viagen_create_task",
|
|
18730
|
+
"Create a new task in the current project. Use this to create follow-up work.",
|
|
18731
|
+
{
|
|
18732
|
+
prompt: external_exports.string().describe("The task prompt / instructions."),
|
|
18733
|
+
branch: external_exports.string().optional().describe("Git branch name for the task."),
|
|
18734
|
+
type: external_exports.enum(["task", "plan"]).optional().describe("Task type: 'task' for code changes, 'plan' for implementation plans.")
|
|
18735
|
+
},
|
|
18736
|
+
async (args) => {
|
|
18737
|
+
const task = await client.tasks.create(projectId, {
|
|
18738
|
+
prompt: args.prompt,
|
|
18739
|
+
branch: args.branch,
|
|
18740
|
+
type: args.type
|
|
18741
|
+
});
|
|
18494
18742
|
return {
|
|
18495
|
-
content: [
|
|
18743
|
+
content: [
|
|
18744
|
+
{
|
|
18745
|
+
type: "text",
|
|
18746
|
+
text: JSON.stringify(task, null, 2)
|
|
18747
|
+
}
|
|
18748
|
+
]
|
|
18496
18749
|
};
|
|
18497
18750
|
}
|
|
18498
18751
|
)
|
|
18499
18752
|
];
|
|
18500
|
-
if (config2?.projectId) {
|
|
18501
|
-
tools.push(
|
|
18502
|
-
tool(
|
|
18503
|
-
"viagen_list_tasks",
|
|
18504
|
-
"List tasks in the current project. Optionally filter by status.",
|
|
18505
|
-
{
|
|
18506
|
-
status: external_exports.enum(["ready", "running", "validating", "completed", "timed_out"]).optional().describe("Filter tasks by status.")
|
|
18507
|
-
},
|
|
18508
|
-
async (args) => {
|
|
18509
|
-
const tasks = await listTasks({ status: args.status });
|
|
18510
|
-
return {
|
|
18511
|
-
content: [
|
|
18512
|
-
{
|
|
18513
|
-
type: "text",
|
|
18514
|
-
text: JSON.stringify(tasks, null, 2)
|
|
18515
|
-
}
|
|
18516
|
-
]
|
|
18517
|
-
};
|
|
18518
|
-
}
|
|
18519
|
-
)
|
|
18520
|
-
);
|
|
18521
|
-
tools.push(
|
|
18522
|
-
tool(
|
|
18523
|
-
"viagen_get_task",
|
|
18524
|
-
"Get full details of a specific task by ID.",
|
|
18525
|
-
{
|
|
18526
|
-
taskId: external_exports.string().describe("The task ID to retrieve.")
|
|
18527
|
-
},
|
|
18528
|
-
async (args) => {
|
|
18529
|
-
const task = await getTask(args.taskId);
|
|
18530
|
-
return {
|
|
18531
|
-
content: [
|
|
18532
|
-
{
|
|
18533
|
-
type: "text",
|
|
18534
|
-
text: JSON.stringify(task, null, 2)
|
|
18535
|
-
}
|
|
18536
|
-
]
|
|
18537
|
-
};
|
|
18538
|
-
}
|
|
18539
|
-
)
|
|
18540
|
-
);
|
|
18541
|
-
tools.push(
|
|
18542
|
-
tool(
|
|
18543
|
-
"viagen_create_task",
|
|
18544
|
-
"Create a new task in the current project. Use this to create follow-up work.",
|
|
18545
|
-
{
|
|
18546
|
-
prompt: external_exports.string().describe("The task prompt / instructions."),
|
|
18547
|
-
branch: external_exports.string().optional().describe("Git branch name for the task."),
|
|
18548
|
-
type: external_exports.enum(["task", "plan"]).optional().describe("Task type: 'task' for code changes, 'plan' for implementation plans.")
|
|
18549
|
-
},
|
|
18550
|
-
async (args) => {
|
|
18551
|
-
const task = await createTask({
|
|
18552
|
-
prompt: args.prompt,
|
|
18553
|
-
branch: args.branch,
|
|
18554
|
-
type: args.type
|
|
18555
|
-
});
|
|
18556
|
-
return {
|
|
18557
|
-
content: [
|
|
18558
|
-
{
|
|
18559
|
-
type: "text",
|
|
18560
|
-
text: JSON.stringify(task, null, 2)
|
|
18561
|
-
}
|
|
18562
|
-
]
|
|
18563
|
-
};
|
|
18564
|
-
}
|
|
18565
|
-
)
|
|
18566
|
-
);
|
|
18567
|
-
}
|
|
18568
18753
|
return createSdkMcpServer({ name: "viagen", tools });
|
|
18569
18754
|
}
|
|
18570
18755
|
var PLAN_MODE_DISALLOWED_TOOLS = ["Edit", "NotebookEdit"];
|
|
@@ -18604,6 +18789,9 @@ You have access to viagen platform tools for task management:
|
|
|
18604
18789
|
Use these to understand project context and create follow-up work when appropriate.
|
|
18605
18790
|
`;
|
|
18606
18791
|
|
|
18792
|
+
// src/index.ts
|
|
18793
|
+
import { createViagen } from "viagen-sdk";
|
|
18794
|
+
|
|
18607
18795
|
// src/sandbox.ts
|
|
18608
18796
|
import { randomUUID } from "crypto";
|
|
18609
18797
|
import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
|
|
@@ -18868,6 +19056,7 @@ function viagen(options) {
|
|
|
18868
19056
|
ui: options?.ui ?? true
|
|
18869
19057
|
};
|
|
18870
19058
|
let env;
|
|
19059
|
+
let previewEnabled = false;
|
|
18871
19060
|
let projectRoot;
|
|
18872
19061
|
let lastError = null;
|
|
18873
19062
|
let promptSent = false;
|
|
@@ -18877,12 +19066,13 @@ function viagen(options) {
|
|
|
18877
19066
|
name: "viagen",
|
|
18878
19067
|
config(_, { mode }) {
|
|
18879
19068
|
const e = loadEnv(mode, process.cwd(), "");
|
|
18880
|
-
if (e["VIAGEN_AUTH_TOKEN"]) {
|
|
19069
|
+
if (e["VIAGEN_AUTH_TOKEN"] || e["VIAGEN_USER_TOKEN"]) {
|
|
18881
19070
|
return { server: { host: true, allowedHosts: true } };
|
|
18882
19071
|
}
|
|
18883
19072
|
},
|
|
18884
19073
|
configResolved(config2) {
|
|
18885
19074
|
env = loadEnv(config2.mode, config2.envDir ?? config2.root, "");
|
|
19075
|
+
previewEnabled = env["VIAGEN_PREVIEW"] === "true";
|
|
18886
19076
|
projectRoot = config2.root;
|
|
18887
19077
|
const debugEnabled = options?.debug ?? env["VIAGEN_DEBUG"] === "1";
|
|
18888
19078
|
setDebug(debugEnabled);
|
|
@@ -18893,6 +19083,7 @@ function viagen(options) {
|
|
|
18893
19083
|
debug("init", `CLAUDE_ACCESS_TOKEN: ${env["CLAUDE_ACCESS_TOKEN"] ? "set" : "NOT SET"}`);
|
|
18894
19084
|
debug("init", `GITHUB_TOKEN: ${env["GITHUB_TOKEN"] ? "set" : "NOT SET"}`);
|
|
18895
19085
|
debug("init", `VIAGEN_AUTH_TOKEN: ${env["VIAGEN_AUTH_TOKEN"] ? "set" : "NOT SET"}`);
|
|
19086
|
+
debug("init", `VIAGEN_USER_TOKEN: ${env["VIAGEN_USER_TOKEN"] ? "set" : "NOT SET"}`);
|
|
18896
19087
|
debug("init", `VIAGEN_MODEL: ${env["VIAGEN_MODEL"] || "(not set)"}`);
|
|
18897
19088
|
debug("init", `VIAGEN_PROMPT: ${env["VIAGEN_PROMPT"] ? `"${env["VIAGEN_PROMPT"].slice(0, 80)}..."` : "(not set)"}`);
|
|
18898
19089
|
debug("init", `VIAGEN_TASK_ID: ${env["VIAGEN_TASK_ID"] || "(not set)"}`);
|
|
@@ -18911,21 +19102,30 @@ function viagen(options) {
|
|
|
18911
19102
|
);
|
|
18912
19103
|
},
|
|
18913
19104
|
transformIndexHtml(_html, ctx) {
|
|
18914
|
-
|
|
18915
|
-
|
|
18916
|
-
|
|
18917
|
-
|
|
18918
|
-
|
|
18919
|
-
|
|
19105
|
+
const tags = [];
|
|
19106
|
+
if (opts.ui) {
|
|
19107
|
+
const url2 = new URL(ctx.originalUrl || ctx.path, "http://localhost");
|
|
19108
|
+
const isEmbed = url2.searchParams.has("_viagen_embed");
|
|
19109
|
+
if (!isEmbed || opts.overlay) {
|
|
19110
|
+
tags.push({
|
|
19111
|
+
tag: "script",
|
|
19112
|
+
children: buildClientScript({
|
|
19113
|
+
position: opts.position,
|
|
19114
|
+
panelWidth: opts.panelWidth,
|
|
19115
|
+
overlay: opts.overlay
|
|
19116
|
+
}),
|
|
19117
|
+
injectTo: "body"
|
|
19118
|
+
});
|
|
19119
|
+
}
|
|
19120
|
+
}
|
|
19121
|
+
if (previewEnabled) {
|
|
19122
|
+
tags.push({
|
|
18920
19123
|
tag: "script",
|
|
18921
|
-
children:
|
|
18922
|
-
position: opts.position,
|
|
18923
|
-
panelWidth: opts.panelWidth,
|
|
18924
|
-
overlay: opts.overlay
|
|
18925
|
-
}),
|
|
19124
|
+
children: buildPreviewScript(),
|
|
18926
19125
|
injectTo: "body"
|
|
18927
|
-
}
|
|
18928
|
-
|
|
19126
|
+
});
|
|
19127
|
+
}
|
|
19128
|
+
return tags;
|
|
18929
19129
|
},
|
|
18930
19130
|
configureServer(server) {
|
|
18931
19131
|
debug("server", "configureServer starting");
|
|
@@ -18968,6 +19168,14 @@ ${payload.err.frame || ""}`
|
|
|
18968
19168
|
} else {
|
|
18969
19169
|
debug("server", "auth middleware DISABLED (no VIAGEN_AUTH_TOKEN)");
|
|
18970
19170
|
}
|
|
19171
|
+
const platformToken = env["VIAGEN_USER_TOKEN"] || env["VIAGEN_AUTH_TOKEN"];
|
|
19172
|
+
const platformUrl = env["VIAGEN_PLATFORM_URL"] || "https://app.viagen.dev";
|
|
19173
|
+
const projectId = env["VIAGEN_PROJECT_ID"];
|
|
19174
|
+
let viagenClient = null;
|
|
19175
|
+
if (platformToken) {
|
|
19176
|
+
viagenClient = createViagen({ token: platformToken, baseUrl: platformUrl });
|
|
19177
|
+
debug("server", `platform client created (baseUrl: ${platformUrl})`);
|
|
19178
|
+
}
|
|
18971
19179
|
const hasEditor = !!(options?.editable && options.editable.length > 0);
|
|
18972
19180
|
const clientJs = buildClientScript({
|
|
18973
19181
|
position: opts.position,
|
|
@@ -18990,18 +19198,80 @@ ${payload.err.frame || ""}`
|
|
|
18990
19198
|
res.setHeader("Content-Type", "text/html");
|
|
18991
19199
|
res.end(buildIframeHtml({ panelWidth: opts.panelWidth }));
|
|
18992
19200
|
});
|
|
19201
|
+
if (previewEnabled) {
|
|
19202
|
+
const previewJs = buildPreviewScript();
|
|
19203
|
+
server.middlewares.use("/via/preview.js", (_req, res) => {
|
|
19204
|
+
res.setHeader("Content-Type", "application/javascript");
|
|
19205
|
+
res.end(previewJs);
|
|
19206
|
+
});
|
|
19207
|
+
server.middlewares.use("/via/preview/task", (req, res) => {
|
|
19208
|
+
if (req.method !== "POST") {
|
|
19209
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
19210
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
19211
|
+
return;
|
|
19212
|
+
}
|
|
19213
|
+
if (!viagenClient || !projectId) {
|
|
19214
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
19215
|
+
res.end(JSON.stringify({ error: "Task creation not configured: missing platform credentials or VIAGEN_PROJECT_ID" }));
|
|
19216
|
+
return;
|
|
19217
|
+
}
|
|
19218
|
+
let body = "";
|
|
19219
|
+
req.on("data", (chunk) => {
|
|
19220
|
+
body += chunk.toString();
|
|
19221
|
+
});
|
|
19222
|
+
req.on("end", async () => {
|
|
19223
|
+
try {
|
|
19224
|
+
const data = JSON.parse(body);
|
|
19225
|
+
const { prompt, pageUrl, screenshot } = data;
|
|
19226
|
+
if (!prompt || typeof prompt !== "string") {
|
|
19227
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
19228
|
+
res.end(JSON.stringify({ error: "prompt is required" }));
|
|
19229
|
+
return;
|
|
19230
|
+
}
|
|
19231
|
+
const parts = [prompt.trim()];
|
|
19232
|
+
if (pageUrl) parts.push(`
|
|
19233
|
+
Page URL: ${pageUrl}`);
|
|
19234
|
+
if (screenshot) parts.push("\n[Screenshot attached]");
|
|
19235
|
+
parts.push("\n\n[Submitted via viagen preview feedback]");
|
|
19236
|
+
const fullPrompt = parts.join("");
|
|
19237
|
+
const task = await viagenClient.tasks.create(projectId, { prompt: fullPrompt, type: "task" });
|
|
19238
|
+
if (screenshot && task.id) {
|
|
19239
|
+
try {
|
|
19240
|
+
const [header, b64] = screenshot.split(",");
|
|
19241
|
+
const mime = header.match(/:(.*?);/)?.[1] || "image/jpeg";
|
|
19242
|
+
const binary = Buffer.from(b64, "base64");
|
|
19243
|
+
const blob = new Blob([binary], { type: mime });
|
|
19244
|
+
const ext = mime === "image/png" ? "png" : "jpeg";
|
|
19245
|
+
await viagenClient.tasks.addAttachment(projectId, task.id, blob, `screenshot.${ext}`);
|
|
19246
|
+
debug("preview", `screenshot attached to task ${task.id}`);
|
|
19247
|
+
} catch (attachErr) {
|
|
19248
|
+
debug("preview", `screenshot attachment failed (non-fatal): ${attachErr}`);
|
|
19249
|
+
}
|
|
19250
|
+
}
|
|
19251
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
19252
|
+
res.end(JSON.stringify({ task }));
|
|
19253
|
+
} catch (err) {
|
|
19254
|
+
const message = err instanceof Error ? err.message : "Internal error";
|
|
19255
|
+
debug("preview", `preview/task error: ${message}`);
|
|
19256
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
19257
|
+
res.end(JSON.stringify({ error: message }));
|
|
19258
|
+
}
|
|
19259
|
+
});
|
|
19260
|
+
});
|
|
19261
|
+
}
|
|
18993
19262
|
registerHealthRoutes(server, env, {
|
|
18994
19263
|
get: () => lastError
|
|
18995
19264
|
});
|
|
18996
19265
|
const resolvedModel = env["VIAGEN_MODEL"] || opts.model;
|
|
18997
19266
|
debug("server", `creating ChatSession (model: ${resolvedModel})`);
|
|
18998
19267
|
let mcpServers;
|
|
18999
|
-
const
|
|
19000
|
-
if (
|
|
19001
|
-
debug("server", "creating viagen MCP tools (
|
|
19002
|
-
const viagenMcp = createViagenTools(
|
|
19003
|
-
|
|
19004
|
-
|
|
19268
|
+
const hasPlatformContext = !!(viagenClient && projectId);
|
|
19269
|
+
if (hasPlatformContext) {
|
|
19270
|
+
debug("server", "creating viagen MCP tools (platform connected)");
|
|
19271
|
+
const viagenMcp = createViagenTools({
|
|
19272
|
+
client: viagenClient,
|
|
19273
|
+
projectId
|
|
19274
|
+
});
|
|
19005
19275
|
mcpServers = { [viagenMcp.name]: viagenMcp };
|
|
19006
19276
|
}
|
|
19007
19277
|
const isPlanMode = env["VIAGEN_TASK_TYPE"] === "plan";
|
|
@@ -19009,7 +19279,7 @@ ${payload.err.frame || ""}`
|
|
|
19009
19279
|
if (isPlanMode) {
|
|
19010
19280
|
debug("server", "plan mode active \u2014 restricting tools");
|
|
19011
19281
|
systemPrompt = PLAN_SYSTEM_PROMPT;
|
|
19012
|
-
} else if (
|
|
19282
|
+
} else if (hasPlatformContext) {
|
|
19013
19283
|
systemPrompt = (systemPrompt || "") + TASK_TOOLS_PROMPT;
|
|
19014
19284
|
}
|
|
19015
19285
|
const chatSession = new ChatSession({
|
|
@@ -19025,7 +19295,11 @@ ${payload.err.frame || ""}`
|
|
|
19025
19295
|
} : {}
|
|
19026
19296
|
});
|
|
19027
19297
|
debug("server", "registering chat routes");
|
|
19028
|
-
registerChatRoutes(server, chatSession, {
|
|
19298
|
+
registerChatRoutes(server, chatSession, {
|
|
19299
|
+
env,
|
|
19300
|
+
viagenClient: viagenClient ?? void 0,
|
|
19301
|
+
projectId
|
|
19302
|
+
});
|
|
19029
19303
|
if (hasEditor) {
|
|
19030
19304
|
registerFileRoutes(server, {
|
|
19031
19305
|
editable: options.editable,
|
|
@@ -19043,30 +19317,26 @@ ${payload.err.frame || ""}`
|
|
|
19043
19317
|
if (event.type === "done") {
|
|
19044
19318
|
debug("server", "auto-prompt completed");
|
|
19045
19319
|
logBuffer.push("info", `[viagen] Prompt completed`);
|
|
19046
|
-
const
|
|
19047
|
-
if (
|
|
19048
|
-
|
|
19049
|
-
|
|
19050
|
-
|
|
19051
|
-
"Content-Type": "application/json",
|
|
19052
|
-
Authorization: `Bearer ${env["VIAGEN_AUTH_TOKEN"]}`
|
|
19053
|
-
},
|
|
19054
|
-
body: JSON.stringify({
|
|
19055
|
-
taskId,
|
|
19056
|
-
...event.inputTokens != null && { inputTokens: event.inputTokens },
|
|
19057
|
-
...event.outputTokens != null && { outputTokens: event.outputTokens },
|
|
19058
|
-
...event.costUsd != null && { costUsd: event.costUsd }
|
|
19059
|
-
})
|
|
19320
|
+
const currentTaskId = env["VIAGEN_TASK_ID"];
|
|
19321
|
+
if (viagenClient && projectId && currentTaskId && (event.inputTokens || event.outputTokens)) {
|
|
19322
|
+
viagenClient.tasks.update(projectId, currentTaskId, {
|
|
19323
|
+
...event.inputTokens != null && { inputTokens: event.inputTokens },
|
|
19324
|
+
...event.outputTokens != null && { outputTokens: event.outputTokens }
|
|
19060
19325
|
}).catch((err) => {
|
|
19061
|
-
debug("server", `usage
|
|
19326
|
+
debug("server", `usage report failed: ${err}`);
|
|
19062
19327
|
});
|
|
19063
19328
|
}
|
|
19064
19329
|
}
|
|
19065
19330
|
});
|
|
19066
19331
|
}
|
|
19067
|
-
if (opts.ui) {
|
|
19332
|
+
if (opts.ui || previewEnabled) {
|
|
19068
19333
|
return () => {
|
|
19069
|
-
|
|
19334
|
+
if (opts.ui) {
|
|
19335
|
+
server.middlewares.use(createInjectionMiddleware());
|
|
19336
|
+
}
|
|
19337
|
+
if (previewEnabled) {
|
|
19338
|
+
server.middlewares.use(createPreviewInjectionMiddleware());
|
|
19339
|
+
}
|
|
19070
19340
|
};
|
|
19071
19341
|
}
|
|
19072
19342
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "viagen",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.54",
|
|
4
4
|
"description": "Vite dev server plugin that exposes endpoints for chatting with Claude Code SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@vercel/sandbox": "^1",
|
|
45
45
|
"lucide-react": "^0.564.0",
|
|
46
46
|
"simple-git": "^3.31.1",
|
|
47
|
-
"viagen-sdk": "^0.0.
|
|
47
|
+
"viagen-sdk": "^0.0.12"
|
|
48
48
|
},
|
|
49
49
|
"license": "MIT",
|
|
50
50
|
"repository": {
|