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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # viagen
2
2
 
3
- A dev server plugin (Vite, webpack, Next.js) and CLI tool that enables you to use Claude Code in a sandbox — instantly.
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
- ## Vite Plugin Options
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 sandbox2 = await Sandbox.create({
52
- runtime: "node22",
53
- ports: [5173],
54
- timeout: timeoutMs,
55
- ...opts.git ? {
56
- source: {
57
- type: "git",
58
- url: opts.git.remoteUrl,
59
- username: "x-access-token",
60
- password: opts.git.token,
61
- revision: opts.git.branch
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 envLines = [
112
- `VIAGEN_AUTH_TOKEN=${token}`,
113
- `VIAGEN_SESSION_START=${Math.floor(Date.now() / 1e3)}`,
114
- `VIAGEN_SESSION_TIMEOUT=${(opts.timeoutMinutes ?? 30) * 60}`
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
- envLines.push(`ANTHROPIC_API_KEY=${opts.apiKey}`);
135
+ envMap["ANTHROPIC_API_KEY"] = opts.apiKey;
118
136
  } else if (opts.oauth) {
119
- envLines.push(`CLAUDE_ACCESS_TOKEN=${opts.oauth.accessToken}`);
120
- envLines.push(`CLAUDE_REFRESH_TOKEN=${opts.oauth.refreshToken}`);
121
- envLines.push(`CLAUDE_TOKEN_EXPIRES=${opts.oauth.tokenExpires}`);
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
- envLines.push(`GITHUB_TOKEN=${opts.git.token}`);
142
+ envMap["GITHUB_TOKEN"] = opts.git.token;
125
143
  }
126
144
  if (opts.vercel) {
127
- envLines.push(`VERCEL_TOKEN=${opts.vercel.token}`);
128
- envLines.push(`VERCEL_ORG_ID=${opts.vercel.teamId}`);
129
- envLines.push(`VERCEL_PROJECT_ID=${opts.vercel.projectId}`);
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(app, env, errorRef) {
63
- app.use("/via/error", (_req, res) => {
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
- app.use("/via/health", (_req, res) => {
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 = typeof __require !== "undefined" && typeof __require.resolve === "function" ? __require : createRequire(import.meta.url);
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(app, opts) {
128
+ function registerChatRoutes(server, opts) {
133
129
  let sessionId;
134
- app.use("/via/chat/reset", (_req, res) => {
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
- app.use("/via/chat", async (req, res) => {
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 && (opts.buildTool ?? "vite") === "vite"};
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 sandbox = await Sandbox.create({
1333
- runtime: "node22",
1334
- ports: [5173],
1335
- timeout: timeoutMs,
1336
- ...opts.git ? {
1337
- source: {
1338
- type: "git",
1339
- url: opts.git.remoteUrl,
1340
- username: "x-access-token",
1341
- password: opts.git.token,
1342
- revision: opts.git.branch
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 envLines = [
1393
- `VIAGEN_AUTH_TOKEN=${token}`,
1394
- `VIAGEN_SESSION_START=${Math.floor(Date.now() / 1e3)}`,
1395
- `VIAGEN_SESSION_TIMEOUT=${(opts.timeoutMinutes ?? 30) * 60}`
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
- envLines.push(`ANTHROPIC_API_KEY=${opts.apiKey}`);
1412
+ envMap["ANTHROPIC_API_KEY"] = opts.apiKey;
1399
1413
  } else if (opts.oauth) {
1400
- envLines.push(`CLAUDE_ACCESS_TOKEN=${opts.oauth.accessToken}`);
1401
- envLines.push(`CLAUDE_REFRESH_TOKEN=${opts.oauth.refreshToken}`);
1402
- envLines.push(`CLAUDE_TOKEN_EXPIRES=${opts.oauth.tokenExpires}`);
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
- envLines.push(`GITHUB_TOKEN=${opts.git.token}`);
1419
+ envMap["GITHUB_TOKEN"] = opts.git.token;
1406
1420
  }
1407
1421
  if (opts.vercel) {
1408
- envLines.push(`VERCEL_TOKEN=${opts.vercel.token}`);
1409
- envLines.push(`VERCEL_ORG_ID=${opts.vercel.teamId}`);
1410
- envLines.push(`VERCEL_PROJECT_ID=${opts.vercel.projectId}`);
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
- registerHealthRoutes(server.middlewares, env, {
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.middlewares, {
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.10",
4
- "description": "Dev server plugin (Vite + webpack) that exposes endpoints for chatting with Claude Code SDK",
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(&quot;#gradient-0&quot;);"/><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>