viagen 0.0.10 → 0.0.11
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/README.md +2 -76
- package/dist/cli.js +160 -27
- package/dist/index.d.ts +4 -0
- package/dist/index.js +72 -45
- package/package.json +5 -22
- package/site/.viagen/server.log +1 -0
- package/site/icon.png +0 -0
- package/site/icon.svg +2 -0
- package/site/index.html +591 -0
- package/site/vite.config.ts +7 -0
- package/dist/webpack.cjs +0 -1442
- package/dist/webpack.d.cts +0 -62
- package/dist/webpack.d.ts +0 -62
- package/dist/webpack.js +0 -1422
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# viagen
|
|
2
2
|
|
|
3
|
-
A dev server plugin
|
|
3
|
+
A Vite dev server plugin and CLI tool that enables you to use Claude Code in a sandbox — instantly.
|
|
4
4
|
|
|
5
5
|
## Prerequisites
|
|
6
6
|
|
|
@@ -14,8 +14,6 @@ A dev server plugin (Vite, webpack, Next.js) and CLI tool that enables you to us
|
|
|
14
14
|
npm install --save-dev viagen
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
### Vite
|
|
18
|
-
|
|
19
17
|
```ts
|
|
20
18
|
// vite.config.ts
|
|
21
19
|
import { defineConfig } from 'vite'
|
|
@@ -26,66 +24,6 @@ export default defineConfig({
|
|
|
26
24
|
})
|
|
27
25
|
```
|
|
28
26
|
|
|
29
|
-
### webpack
|
|
30
|
-
|
|
31
|
-
```js
|
|
32
|
-
// webpack.config.js
|
|
33
|
-
const { setupViagen } = require('viagen/webpack')
|
|
34
|
-
|
|
35
|
-
module.exports = {
|
|
36
|
-
devServer: {
|
|
37
|
-
setupMiddlewares: (middlewares, devServer) => {
|
|
38
|
-
setupViagen(devServer)
|
|
39
|
-
return middlewares
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### Next.js
|
|
46
|
-
|
|
47
|
-
Next.js uses its own dev server, so viagen runs via a custom server file.
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
npm install --save-dev viagen connect
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
// server.ts
|
|
55
|
-
import http from 'node:http'
|
|
56
|
-
import next from 'next'
|
|
57
|
-
import connect from 'connect'
|
|
58
|
-
import { setupViagen } from 'viagen/webpack'
|
|
59
|
-
|
|
60
|
-
const dev = process.env.NODE_ENV !== 'production'
|
|
61
|
-
const app = next({ dev, hostname: 'localhost', port: 3000 })
|
|
62
|
-
const handle = app.getRequestHandler()
|
|
63
|
-
|
|
64
|
-
app.prepare().then(() => {
|
|
65
|
-
const server = connect()
|
|
66
|
-
|
|
67
|
-
setupViagen({
|
|
68
|
-
app: server,
|
|
69
|
-
compiler: { hooks: { done: { tap: () => {} } } },
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
server.use((req, res) => handle(req, res))
|
|
73
|
-
http.createServer(server).listen(3000, () => {
|
|
74
|
-
console.log('> Ready on http://localhost:3000')
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Then update your dev script:
|
|
80
|
-
|
|
81
|
-
```json
|
|
82
|
-
{
|
|
83
|
-
"scripts": {
|
|
84
|
-
"dev": "npx tsx server.ts"
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
27
|
## Step 2 — Setup
|
|
90
28
|
|
|
91
29
|
```bash
|
|
@@ -115,7 +53,7 @@ npx viagen sandbox --timeout 60
|
|
|
115
53
|
npx viagen sandbox stop <sandboxId>
|
|
116
54
|
```
|
|
117
55
|
|
|
118
|
-
##
|
|
56
|
+
## Plugin Options
|
|
119
57
|
|
|
120
58
|
```ts
|
|
121
59
|
viagen({
|
|
@@ -154,18 +92,6 @@ viagen({
|
|
|
154
92
|
})
|
|
155
93
|
```
|
|
156
94
|
|
|
157
|
-
## webpack Options
|
|
158
|
-
|
|
159
|
-
```js
|
|
160
|
-
setupViagen(devServer, {
|
|
161
|
-
position: 'bottom-right', // toggle button position
|
|
162
|
-
model: 'sonnet', // claude model
|
|
163
|
-
panelWidth: 420, // chat panel width in px
|
|
164
|
-
ui: true, // inject chat panel into pages
|
|
165
|
-
systemPrompt: '...', // custom system prompt
|
|
166
|
-
})
|
|
167
|
-
```
|
|
168
|
-
|
|
169
95
|
## API
|
|
170
96
|
|
|
171
97
|
Every viagen endpoint is available as an API. Build your own UI, integrate with CI, or script Claude from the command line.
|
package/dist/cli.js
CHANGED
|
@@ -48,20 +48,39 @@ async function deploySandbox(opts) {
|
|
|
48
48
|
const token = randomUUID();
|
|
49
49
|
const useGit = !!opts.git;
|
|
50
50
|
const timeoutMs = (opts.timeoutMinutes ?? 30) * 60 * 1e3;
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
const sourceOpts = opts.git ? {
|
|
52
|
+
source: {
|
|
53
|
+
type: "git",
|
|
54
|
+
url: opts.git.remoteUrl,
|
|
55
|
+
username: "x-access-token",
|
|
56
|
+
password: opts.git.token,
|
|
57
|
+
...opts.git.revision ? { revision: opts.git.revision } : {}
|
|
58
|
+
}
|
|
59
|
+
} : {};
|
|
60
|
+
let sandbox2;
|
|
61
|
+
try {
|
|
62
|
+
sandbox2 = await Sandbox.create({
|
|
63
|
+
runtime: "node22",
|
|
64
|
+
ports: [5173],
|
|
65
|
+
timeout: timeoutMs,
|
|
66
|
+
...sourceOpts
|
|
67
|
+
});
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error("\nSandbox creation failed.");
|
|
70
|
+
if (opts.git) {
|
|
71
|
+
console.error(` URL: ${opts.git.remoteUrl}`);
|
|
72
|
+
console.error(` Revision: ${opts.git.revision ?? "(default branch)"}`);
|
|
73
|
+
console.error(` Branch: ${opts.git.branch}`);
|
|
74
|
+
}
|
|
75
|
+
const apiErr = err;
|
|
76
|
+
if (apiErr.message) console.error(` Message: ${apiErr.message}`);
|
|
77
|
+
if (apiErr.json) {
|
|
78
|
+
console.error(` Response: ${JSON.stringify(apiErr.json, null, 2)}`);
|
|
79
|
+
} else if (apiErr.text) {
|
|
80
|
+
console.error(` Response: ${apiErr.text}`);
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
65
84
|
try {
|
|
66
85
|
if (useGit && opts.git) {
|
|
67
86
|
await sandbox2.runCommand("git", [
|
|
@@ -108,26 +127,26 @@ async function deploySandbox(opts) {
|
|
|
108
127
|
await sandbox2.writeFiles(files);
|
|
109
128
|
}
|
|
110
129
|
}
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
];
|
|
130
|
+
const envMap = { ...opts.envVars ?? {} };
|
|
131
|
+
envMap["VIAGEN_AUTH_TOKEN"] = token;
|
|
132
|
+
envMap["VIAGEN_SESSION_START"] = String(Math.floor(Date.now() / 1e3));
|
|
133
|
+
envMap["VIAGEN_SESSION_TIMEOUT"] = String((opts.timeoutMinutes ?? 30) * 60);
|
|
116
134
|
if (opts.apiKey) {
|
|
117
|
-
|
|
135
|
+
envMap["ANTHROPIC_API_KEY"] = opts.apiKey;
|
|
118
136
|
} else if (opts.oauth) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
137
|
+
envMap["CLAUDE_ACCESS_TOKEN"] = opts.oauth.accessToken;
|
|
138
|
+
envMap["CLAUDE_REFRESH_TOKEN"] = opts.oauth.refreshToken;
|
|
139
|
+
envMap["CLAUDE_TOKEN_EXPIRES"] = opts.oauth.tokenExpires;
|
|
122
140
|
}
|
|
123
141
|
if (opts.git) {
|
|
124
|
-
|
|
142
|
+
envMap["GITHUB_TOKEN"] = opts.git.token;
|
|
125
143
|
}
|
|
126
144
|
if (opts.vercel) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
145
|
+
envMap["VERCEL_TOKEN"] = opts.vercel.token;
|
|
146
|
+
envMap["VERCEL_ORG_ID"] = opts.vercel.teamId;
|
|
147
|
+
envMap["VERCEL_PROJECT_ID"] = opts.vercel.projectId;
|
|
130
148
|
}
|
|
149
|
+
const envLines = Object.entries(envMap).map(([k, v]) => `${k}=${v}`);
|
|
131
150
|
await sandbox2.writeFiles([
|
|
132
151
|
{
|
|
133
152
|
path: ".env",
|
|
@@ -297,6 +316,22 @@ async function oauthConsoleFlow() {
|
|
|
297
316
|
const keyData = await keyRes.json();
|
|
298
317
|
return keyData.raw_key;
|
|
299
318
|
}
|
|
319
|
+
async function refreshAccessToken(refresh) {
|
|
320
|
+
const res = await fetch(TOKEN_ENDPOINT, {
|
|
321
|
+
method: "POST",
|
|
322
|
+
headers: { "Content-Type": "application/json" },
|
|
323
|
+
body: JSON.stringify({
|
|
324
|
+
grant_type: "refresh_token",
|
|
325
|
+
client_id: CLIENT_ID,
|
|
326
|
+
refresh_token: refresh
|
|
327
|
+
})
|
|
328
|
+
});
|
|
329
|
+
if (!res.ok) {
|
|
330
|
+
const text = await res.text();
|
|
331
|
+
throw new Error(`Token refresh failed (${res.status}): ${text}`);
|
|
332
|
+
}
|
|
333
|
+
return await res.json();
|
|
334
|
+
}
|
|
300
335
|
|
|
301
336
|
// src/cli.ts
|
|
302
337
|
function loadDotenv(dir) {
|
|
@@ -334,6 +369,18 @@ function writeEnvVars(dir, vars) {
|
|
|
334
369
|
content += toAdd.join("\n") + "\n";
|
|
335
370
|
writeFileSync(envPath, content);
|
|
336
371
|
}
|
|
372
|
+
function updateEnvVars(dir, vars) {
|
|
373
|
+
const envPath = join2(dir, ".env");
|
|
374
|
+
if (!existsSync(envPath)) return;
|
|
375
|
+
let content = readFileSync2(envPath, "utf-8");
|
|
376
|
+
for (const [key, val] of Object.entries(vars)) {
|
|
377
|
+
const re = new RegExp(`^${key}=.*$`, "m");
|
|
378
|
+
if (re.test(content)) {
|
|
379
|
+
content = content.replace(re, `${key}=${val}`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
writeFileSync(envPath, content);
|
|
383
|
+
}
|
|
337
384
|
function openBrowser2(url) {
|
|
338
385
|
try {
|
|
339
386
|
const platform = process.platform;
|
|
@@ -384,6 +431,42 @@ function getGitInfo(cwd) {
|
|
|
384
431
|
return null;
|
|
385
432
|
}
|
|
386
433
|
}
|
|
434
|
+
function remoteBranchExists(remoteUrl, branch, token) {
|
|
435
|
+
try {
|
|
436
|
+
const url = new URL(remoteUrl);
|
|
437
|
+
url.username = "x-access-token";
|
|
438
|
+
url.password = token;
|
|
439
|
+
const out = execSync2(
|
|
440
|
+
`git ls-remote --heads ${url.toString()} refs/heads/${branch}`,
|
|
441
|
+
{ encoding: "utf-8", stdio: "pipe" }
|
|
442
|
+
).trim();
|
|
443
|
+
return out.length > 0;
|
|
444
|
+
} catch {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function repoNwo(remoteUrl) {
|
|
449
|
+
const m = remoteUrl.match(/github\.com[/:]([^/]+\/[^/.]+?)(?:\.git)?$/);
|
|
450
|
+
return m ? m[1] : null;
|
|
451
|
+
}
|
|
452
|
+
function createRemoteBranch(remoteUrl, branch, token) {
|
|
453
|
+
const nwo = repoNwo(remoteUrl);
|
|
454
|
+
if (!nwo) return false;
|
|
455
|
+
try {
|
|
456
|
+
const sha = execSync2(
|
|
457
|
+
`gh api repos/${nwo}/git/ref/heads/main --jq .object.sha`,
|
|
458
|
+
{ encoding: "utf-8", stdio: "pipe", env: { ...process.env, GH_TOKEN: token } }
|
|
459
|
+
).trim();
|
|
460
|
+
if (!sha) return false;
|
|
461
|
+
execSync2(
|
|
462
|
+
`gh api repos/${nwo}/git/refs -f ref=refs/heads/${branch} -f sha=${sha}`,
|
|
463
|
+
{ encoding: "utf-8", stdio: "pipe", env: { ...process.env, GH_TOKEN: token } }
|
|
464
|
+
);
|
|
465
|
+
return true;
|
|
466
|
+
} catch {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
387
470
|
function promptUser(question) {
|
|
388
471
|
if (!process.stdin.isTTY) return Promise.resolve("");
|
|
389
472
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -650,6 +733,31 @@ async function sandbox(args) {
|
|
|
650
733
|
);
|
|
651
734
|
process.exit(1);
|
|
652
735
|
}
|
|
736
|
+
if (hasOAuth && env["CLAUDE_REFRESH_TOKEN"]) {
|
|
737
|
+
const expires = parseInt(env["CLAUDE_TOKEN_EXPIRES"] || "0", 10);
|
|
738
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
739
|
+
if (nowSec > expires - 300) {
|
|
740
|
+
console.log("Refreshing Claude OAuth tokens...");
|
|
741
|
+
try {
|
|
742
|
+
const tokens = await refreshAccessToken(env["CLAUDE_REFRESH_TOKEN"]);
|
|
743
|
+
env["CLAUDE_ACCESS_TOKEN"] = tokens.access_token;
|
|
744
|
+
env["CLAUDE_REFRESH_TOKEN"] = tokens.refresh_token;
|
|
745
|
+
env["CLAUDE_TOKEN_EXPIRES"] = String(nowSec + tokens.expires_in);
|
|
746
|
+
updateEnvVars(cwd, {
|
|
747
|
+
CLAUDE_ACCESS_TOKEN: tokens.access_token,
|
|
748
|
+
CLAUDE_REFRESH_TOKEN: tokens.refresh_token,
|
|
749
|
+
CLAUDE_TOKEN_EXPIRES: String(nowSec + tokens.expires_in)
|
|
750
|
+
});
|
|
751
|
+
console.log(" Tokens refreshed.");
|
|
752
|
+
} catch (err) {
|
|
753
|
+
console.error(
|
|
754
|
+
"Failed to refresh OAuth tokens. Run `npx viagen setup` to re-authenticate."
|
|
755
|
+
);
|
|
756
|
+
console.error(` ${err instanceof Error ? err.message : String(err)}`);
|
|
757
|
+
process.exit(1);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
653
761
|
const hasOidc = !!env["VERCEL_OIDC_TOKEN"];
|
|
654
762
|
const hasToken = !!env["VERCEL_TOKEN"] && !!env["VERCEL_TEAM_ID"] && !!env["VERCEL_PROJECT_ID"];
|
|
655
763
|
if (!hasOidc && !hasToken) {
|
|
@@ -664,9 +772,33 @@ async function sandbox(args) {
|
|
|
664
772
|
let overlayFiles;
|
|
665
773
|
const branch = branchOverride || (gitInfo ? gitInfo.branch : "main");
|
|
666
774
|
if (gitInfo && githubToken) {
|
|
775
|
+
let branchExistsOnRemote = remoteBranchExists(
|
|
776
|
+
gitInfo.remoteUrl,
|
|
777
|
+
branch,
|
|
778
|
+
githubToken
|
|
779
|
+
);
|
|
780
|
+
if (!branchExistsOnRemote) {
|
|
781
|
+
console.log(
|
|
782
|
+
`Branch "${branch}" not found on remote \u2014 creating it...`
|
|
783
|
+
);
|
|
784
|
+
const created = createRemoteBranch(
|
|
785
|
+
gitInfo.remoteUrl,
|
|
786
|
+
branch,
|
|
787
|
+
githubToken
|
|
788
|
+
);
|
|
789
|
+
if (created) {
|
|
790
|
+
console.log(` Branch "${branch}" created on remote (from main).`);
|
|
791
|
+
branchExistsOnRemote = true;
|
|
792
|
+
} else {
|
|
793
|
+
console.log(
|
|
794
|
+
` Could not create branch on remote \u2014 will clone default branch and create locally.`
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
667
798
|
const makeGitInfo = () => ({
|
|
668
799
|
remoteUrl: gitInfo.remoteUrl,
|
|
669
800
|
branch,
|
|
801
|
+
revision: branchExistsOnRemote ? branch : void 0,
|
|
670
802
|
userName: gitInfo.userName,
|
|
671
803
|
userEmail: gitInfo.userEmail,
|
|
672
804
|
token: githubToken
|
|
@@ -718,6 +850,7 @@ async function sandbox(args) {
|
|
|
718
850
|
} : void 0,
|
|
719
851
|
git: deployGit,
|
|
720
852
|
overlayFiles,
|
|
853
|
+
envVars: dotenv,
|
|
721
854
|
vercel: env["VERCEL_TOKEN"] && env["VERCEL_TEAM_ID"] && env["VERCEL_PROJECT_ID"] ? {
|
|
722
855
|
token: env["VERCEL_TOKEN"],
|
|
723
856
|
teamId: env["VERCEL_TEAM_ID"],
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ interface GitInfo {
|
|
|
7
7
|
remoteUrl: string;
|
|
8
8
|
/** Branch to check out. */
|
|
9
9
|
branch: string;
|
|
10
|
+
/** Revision to clone (branch/tag/sha). If omitted, clones the default branch. */
|
|
11
|
+
revision?: string;
|
|
10
12
|
/** Git user name for commits. */
|
|
11
13
|
userName: string;
|
|
12
14
|
/** Git user email for commits. */
|
|
@@ -40,6 +42,8 @@ interface DeploySandboxOptions {
|
|
|
40
42
|
};
|
|
41
43
|
/** Sandbox timeout in minutes (default: 30, max depends on Vercel plan). */
|
|
42
44
|
timeoutMinutes?: number;
|
|
45
|
+
/** User's .env variables to forward into the sandbox. */
|
|
46
|
+
envVars?: Record<string, string>;
|
|
43
47
|
}
|
|
44
48
|
interface DeploySandboxResult {
|
|
45
49
|
/** Full URL with auth token in query string. */
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/index.ts
|
|
2
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
3
|
+
import { dirname, join as join3 } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
9
5
|
import { loadEnv } from "vite";
|
|
10
6
|
|
|
11
7
|
// src/logger.ts
|
|
@@ -59,12 +55,12 @@ function wrapLogger(logger, buffer) {
|
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
// src/health.ts
|
|
62
|
-
function registerHealthRoutes(
|
|
63
|
-
|
|
58
|
+
function registerHealthRoutes(server, env, errorRef) {
|
|
59
|
+
server.middlewares.use("/via/error", (_req, res) => {
|
|
64
60
|
res.setHeader("Content-Type", "application/json");
|
|
65
61
|
res.end(JSON.stringify({ error: errorRef.get() }));
|
|
66
62
|
});
|
|
67
|
-
|
|
63
|
+
server.middlewares.use("/via/health", (_req, res) => {
|
|
68
64
|
const configured = !!env["ANTHROPIC_API_KEY"] || !!env["CLAUDE_ACCESS_TOKEN"];
|
|
69
65
|
const git = !!env["GITHUB_TOKEN"];
|
|
70
66
|
const sessionStart = env["VIAGEN_SESSION_START"] ? parseInt(env["VIAGEN_SESSION_START"], 10) : null;
|
|
@@ -125,18 +121,18 @@ function readBody(req) {
|
|
|
125
121
|
}
|
|
126
122
|
var DEFAULT_SYSTEM_PROMPT = `You are embedded in a Vite dev server as the "viagen" plugin. Your job is to help build and modify the app. Files you edit will trigger Vite HMR automatically. You can read .viagen/server.log to check recent Vite dev server output (compile errors, HMR updates, warnings). When running in a sandbox with git, the gh CLI is available and authenticated \u2014 you can create pull requests, comment on issues, and manage releases. If Vercel credentials are set, you can run "vercel deploy" to publish a preview and share the URL. Be concise.`;
|
|
127
123
|
function findClaudeBin() {
|
|
128
|
-
const _require =
|
|
124
|
+
const _require = createRequire(import.meta.url);
|
|
129
125
|
const pkgPath = _require.resolve("@anthropic-ai/claude-code/package.json");
|
|
130
126
|
return pkgPath.replace("package.json", "cli.js");
|
|
131
127
|
}
|
|
132
|
-
function registerChatRoutes(
|
|
128
|
+
function registerChatRoutes(server, opts) {
|
|
133
129
|
let sessionId;
|
|
134
|
-
|
|
130
|
+
server.middlewares.use("/via/chat/reset", (_req, res) => {
|
|
135
131
|
sessionId = void 0;
|
|
136
132
|
res.setHeader("Content-Type", "application/json");
|
|
137
133
|
res.end(JSON.stringify({ status: "ok" }));
|
|
138
134
|
});
|
|
139
|
-
|
|
135
|
+
server.middlewares.use("/via/chat", async (req, res) => {
|
|
140
136
|
if (req.method !== "POST") {
|
|
141
137
|
res.statusCode = 405;
|
|
142
138
|
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
@@ -321,7 +317,7 @@ function buildClientScript(opts) {
|
|
|
321
317
|
/* js */
|
|
322
318
|
`
|
|
323
319
|
(function() {
|
|
324
|
-
var OVERLAY_ENABLED = ${opts.overlay
|
|
320
|
+
var OVERLAY_ENABLED = ${opts.overlay};
|
|
325
321
|
var EMBED_MODE = ${opts.embedMode ? "true" : "false"};
|
|
326
322
|
|
|
327
323
|
/* ---- Error overlay: inject Fix button into shadow DOM ---- */
|
|
@@ -387,7 +383,7 @@ function buildClientScript(opts) {
|
|
|
387
383
|
var errorData = await errorRes.json();
|
|
388
384
|
if (!errorData.error) { win.classList.remove('viagen-fixing'); return; }
|
|
389
385
|
var e = errorData.error;
|
|
390
|
-
var prompt = 'Fix this build error in ' +
|
|
386
|
+
var prompt = 'Fix this Vite build error in ' +
|
|
391
387
|
(e.loc ? e.loc.file + ':' + e.loc.line : 'unknown file') +
|
|
392
388
|
':\\n\\n' + e.message +
|
|
393
389
|
(e.frame ? '\\n\\nCode frame:\\n' + e.frame : '');
|
|
@@ -1329,20 +1325,39 @@ async function deploySandbox(opts) {
|
|
|
1329
1325
|
const token = randomUUID();
|
|
1330
1326
|
const useGit = !!opts.git;
|
|
1331
1327
|
const timeoutMs = (opts.timeoutMinutes ?? 30) * 60 * 1e3;
|
|
1332
|
-
const
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1328
|
+
const sourceOpts = opts.git ? {
|
|
1329
|
+
source: {
|
|
1330
|
+
type: "git",
|
|
1331
|
+
url: opts.git.remoteUrl,
|
|
1332
|
+
username: "x-access-token",
|
|
1333
|
+
password: opts.git.token,
|
|
1334
|
+
...opts.git.revision ? { revision: opts.git.revision } : {}
|
|
1335
|
+
}
|
|
1336
|
+
} : {};
|
|
1337
|
+
let sandbox;
|
|
1338
|
+
try {
|
|
1339
|
+
sandbox = await Sandbox.create({
|
|
1340
|
+
runtime: "node22",
|
|
1341
|
+
ports: [5173],
|
|
1342
|
+
timeout: timeoutMs,
|
|
1343
|
+
...sourceOpts
|
|
1344
|
+
});
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
console.error("\nSandbox creation failed.");
|
|
1347
|
+
if (opts.git) {
|
|
1348
|
+
console.error(` URL: ${opts.git.remoteUrl}`);
|
|
1349
|
+
console.error(` Revision: ${opts.git.revision ?? "(default branch)"}`);
|
|
1350
|
+
console.error(` Branch: ${opts.git.branch}`);
|
|
1351
|
+
}
|
|
1352
|
+
const apiErr = err;
|
|
1353
|
+
if (apiErr.message) console.error(` Message: ${apiErr.message}`);
|
|
1354
|
+
if (apiErr.json) {
|
|
1355
|
+
console.error(` Response: ${JSON.stringify(apiErr.json, null, 2)}`);
|
|
1356
|
+
} else if (apiErr.text) {
|
|
1357
|
+
console.error(` Response: ${apiErr.text}`);
|
|
1358
|
+
}
|
|
1359
|
+
throw err;
|
|
1360
|
+
}
|
|
1346
1361
|
try {
|
|
1347
1362
|
if (useGit && opts.git) {
|
|
1348
1363
|
await sandbox.runCommand("git", [
|
|
@@ -1389,26 +1404,26 @@ async function deploySandbox(opts) {
|
|
|
1389
1404
|
await sandbox.writeFiles(files);
|
|
1390
1405
|
}
|
|
1391
1406
|
}
|
|
1392
|
-
const
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
];
|
|
1407
|
+
const envMap = { ...opts.envVars ?? {} };
|
|
1408
|
+
envMap["VIAGEN_AUTH_TOKEN"] = token;
|
|
1409
|
+
envMap["VIAGEN_SESSION_START"] = String(Math.floor(Date.now() / 1e3));
|
|
1410
|
+
envMap["VIAGEN_SESSION_TIMEOUT"] = String((opts.timeoutMinutes ?? 30) * 60);
|
|
1397
1411
|
if (opts.apiKey) {
|
|
1398
|
-
|
|
1412
|
+
envMap["ANTHROPIC_API_KEY"] = opts.apiKey;
|
|
1399
1413
|
} else if (opts.oauth) {
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1414
|
+
envMap["CLAUDE_ACCESS_TOKEN"] = opts.oauth.accessToken;
|
|
1415
|
+
envMap["CLAUDE_REFRESH_TOKEN"] = opts.oauth.refreshToken;
|
|
1416
|
+
envMap["CLAUDE_TOKEN_EXPIRES"] = opts.oauth.tokenExpires;
|
|
1403
1417
|
}
|
|
1404
1418
|
if (opts.git) {
|
|
1405
|
-
|
|
1419
|
+
envMap["GITHUB_TOKEN"] = opts.git.token;
|
|
1406
1420
|
}
|
|
1407
1421
|
if (opts.vercel) {
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1422
|
+
envMap["VERCEL_TOKEN"] = opts.vercel.token;
|
|
1423
|
+
envMap["VERCEL_ORG_ID"] = opts.vercel.teamId;
|
|
1424
|
+
envMap["VERCEL_PROJECT_ID"] = opts.vercel.projectId;
|
|
1411
1425
|
}
|
|
1426
|
+
const envLines = Object.entries(envMap).map(([k, v]) => `${k}=${v}`);
|
|
1412
1427
|
await sandbox.writeFiles([
|
|
1413
1428
|
{
|
|
1414
1429
|
path: ".env",
|
|
@@ -1443,6 +1458,14 @@ async function deploySandbox(opts) {
|
|
|
1443
1458
|
}
|
|
1444
1459
|
|
|
1445
1460
|
// src/index.ts
|
|
1461
|
+
var docsHtmlCache;
|
|
1462
|
+
function getDocsHtml() {
|
|
1463
|
+
if (!docsHtmlCache) {
|
|
1464
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
1465
|
+
docsHtmlCache = readFileSync2(join3(dir, "..", "site", "index.html"), "utf-8");
|
|
1466
|
+
}
|
|
1467
|
+
return docsHtmlCache;
|
|
1468
|
+
}
|
|
1446
1469
|
function viagen(options) {
|
|
1447
1470
|
const opts = {
|
|
1448
1471
|
position: options?.position ?? "bottom-right",
|
|
@@ -1536,10 +1559,14 @@ ${payload.err.frame || ""}`
|
|
|
1536
1559
|
res.setHeader("Content-Type", "text/html");
|
|
1537
1560
|
res.end(buildIframeHtml({ panelWidth: opts.panelWidth }));
|
|
1538
1561
|
});
|
|
1539
|
-
|
|
1562
|
+
server.middlewares.use("/via/docs", (_req, res) => {
|
|
1563
|
+
res.setHeader("Content-Type", "text/html");
|
|
1564
|
+
res.end(getDocsHtml());
|
|
1565
|
+
});
|
|
1566
|
+
registerHealthRoutes(server, env, {
|
|
1540
1567
|
get: () => lastError
|
|
1541
1568
|
});
|
|
1542
|
-
registerChatRoutes(server
|
|
1569
|
+
registerChatRoutes(server, {
|
|
1543
1570
|
env,
|
|
1544
1571
|
projectRoot,
|
|
1545
1572
|
logBuffer,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "viagen",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.11",
|
|
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",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -9,18 +9,14 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"import": "./dist/index.js"
|
|
12
|
-
},
|
|
13
|
-
"./webpack": {
|
|
14
|
-
"types": "./dist/webpack.d.ts",
|
|
15
|
-
"import": "./dist/webpack.js",
|
|
16
|
-
"require": "./dist/webpack.cjs"
|
|
17
12
|
}
|
|
18
13
|
},
|
|
19
14
|
"bin": {
|
|
20
15
|
"viagen": "./dist/cli.js"
|
|
21
16
|
},
|
|
22
17
|
"files": [
|
|
23
|
-
"dist"
|
|
18
|
+
"dist",
|
|
19
|
+
"site"
|
|
24
20
|
],
|
|
25
21
|
"scripts": {
|
|
26
22
|
"build": "tsup",
|
|
@@ -30,20 +26,7 @@
|
|
|
30
26
|
"typecheck": "tsc --noEmit"
|
|
31
27
|
},
|
|
32
28
|
"peerDependencies": {
|
|
33
|
-
"vite": ">=4"
|
|
34
|
-
"webpack": ">=5",
|
|
35
|
-
"webpack-dev-server": ">=4"
|
|
36
|
-
},
|
|
37
|
-
"peerDependenciesMeta": {
|
|
38
|
-
"vite": {
|
|
39
|
-
"optional": true
|
|
40
|
-
},
|
|
41
|
-
"webpack": {
|
|
42
|
-
"optional": true
|
|
43
|
-
},
|
|
44
|
-
"webpack-dev-server": {
|
|
45
|
-
"optional": true
|
|
46
|
-
}
|
|
29
|
+
"vite": ">=4"
|
|
47
30
|
},
|
|
48
31
|
"devDependencies": {
|
|
49
32
|
"@tailwindcss/vite": "^4.1.18",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[2026-02-17T02:31:36.888Z] [INFO] server restarted.
|
package/site/icon.png
ADDED
|
Binary file
|
package/site/icon.svg
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="3.32 4.112 89.828 89.828" width="89.828px" height="89.828px"><defs><radialGradient gradientUnits="userSpaceOnUse" cx="178.98" cy="120.256" r="44.914" id="gradient-0" gradientTransform="matrix(2.018294, -1.392162, 1.294219, 1.876303, -506.948639, 116.481866)"><stop offset="0" style="stop-color: rgb(69, 249, 108);"/><stop offset="1" style="stop-color: rgb(97, 231, 251);"/></radialGradient></defs><g id="object-1" transform="matrix(1, 0, 0, 1, 2.842170943040401e-14, 0)"><rect x="3.32" y="4.112" width="89.828" height="89.828" rx="8" ry="8" style="fill: url("#gradient-0");"/><path d="M 72.501 9.301 C 66.965 31.982 64.475 53.089 71.29 75.791 C 54.089 77.712 30.739 82.181 20.038 86.218 C 22.694 83.19 53.023 40.005 72.501 9.301 Z" style="fill: rgb(255, 255, 255); transform-origin: 46.268px 47.758px 0px;" id="object-0" transform="matrix(0.999326, 0.036701, -0.036701, 0.999326, 0, 0)"/></g></svg>
|