viagen 0.0.13 → 0.0.17

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
@@ -30,7 +30,7 @@ export default defineConfig({
30
30
  npx viagen setup
31
31
  ```
32
32
 
33
- The setup wizard authenticates with Claude then uses GitHub and Vercel to write your local `.env`.
33
+ The setup wizard authenticates with Claude, detects your GitHub and Vercel credentials, and captures your git remote info — all written to your local `.env`. This ensures sandboxes clone the correct repo instead of inferring it at runtime.
34
34
 
35
35
  You can now run `npm run dev` to start the local dev server. At this point you can launch viagen and chat with Claude to make changes to your app.
36
36
 
@@ -62,23 +62,47 @@ viagen({
62
62
  panelWidth: 420, // chat panel width in px
63
63
  overlay: true, // fix button on error overlay
64
64
  ui: true, // inject chat panel into pages
65
+ sandboxFiles: [...], // copy files manually into sandbox
65
66
  systemPrompt: '...', // custom system prompt (see below)
67
+ editable: ['src','conf'], // files/dirs editable in the UI
66
68
  })
67
69
  ```
68
70
 
69
- ### Sandbox Files
71
+ ### SSR Frameworks (React Router, Remix, SvelteKit, etc.)
70
72
 
71
- Gitignored files (credentials, service accounts, etc.) aren't included when cloning from remote. To forward them into the sandbox, add a `viagen.sandboxFiles` array to your `package.json`:
73
+ For plain Vite apps, the chat panel is injected automatically. SSR frameworks render their own HTML, so you need to add one script tag to your root layout:
72
74
 
73
- ```json
74
- {
75
- "viagen": {
76
- "sandboxFiles": ["creds.json", "service-account.json"]
77
- }
75
+ ```html
76
+ <script src="/via/client.js" defer></script>
77
+ ```
78
+
79
+ For React Router, add it to `app/root.tsx`:
80
+
81
+ ```tsx
82
+ export default function Root() {
83
+ return (
84
+ <html>
85
+ <head>
86
+ <script src="/via/client.js" defer />
87
+ {/* ... */}
88
+ </head>
89
+ {/* ... */}
90
+ </html>
91
+ )
78
92
  }
79
93
  ```
80
94
 
81
- These files are always overlaid into the sandbox regardless of deploy mode.
95
+ ### Editable Files
96
+
97
+ Add a file editor panel to the chat UI:
98
+
99
+ ```ts
100
+ viagen({
101
+ editable: ['src/components', '.env', 'vite.config.ts']
102
+ })
103
+ ```
104
+
105
+ Paths can be files or directories (directories include all files within). The editor appears as a "Files" tab in the chat panel.
82
106
 
83
107
  The default system prompt:
84
108
 
@@ -126,6 +150,9 @@ GET /via/health — check API key status
126
150
  GET /via/error — latest build error (if any)
127
151
  GET /via/ui — standalone chat interface
128
152
  GET /via/iframe — split view (app + chat side by side)
153
+ GET /via/files — list editable files (when configured)
154
+ GET /via/file?path= — read file content
155
+ POST /via/file — write file content { path, content }
129
156
  ```
130
157
 
131
158
  When `VIAGEN_AUTH_TOKEN` is set (always on in sandboxes), pass the token as a `Bearer` header or `?token=` query param.
package/dist/cli.js CHANGED
@@ -335,6 +335,13 @@ async function refreshAccessToken(refresh) {
335
335
  }
336
336
 
337
337
  // src/cli.ts
338
+ import {
339
+ createViagen,
340
+ saveCredentials,
341
+ loadCredentials,
342
+ clearCredentials
343
+ } from "viagen-sdk";
344
+ import { spawn as spawnChild } from "child_process";
338
345
  function loadDotenv(dir) {
339
346
  const envPath = join2(dir, ".env");
340
347
  if (!existsSync(envPath)) return {};
@@ -515,9 +522,34 @@ async function setup() {
515
522
  const newVars = {};
516
523
  console.log("viagen setup");
517
524
  console.log("");
518
- if (existing["ANTHROPIC_API_KEY"] || existing["CLAUDE_ACCESS_TOKEN"]) {
525
+ let claudeExpired = false;
526
+ if (existing["CLAUDE_ACCESS_TOKEN"] && existing["CLAUDE_TOKEN_EXPIRES"]) {
527
+ const expires = parseInt(existing["CLAUDE_TOKEN_EXPIRES"], 10);
528
+ const nowSec = Math.floor(Date.now() / 1e3);
529
+ if (nowSec > expires) {
530
+ if (existing["CLAUDE_REFRESH_TOKEN"]) {
531
+ console.log("Claude auth ... token expired, attempting refresh...");
532
+ try {
533
+ const tokens = await refreshAccessToken(existing["CLAUDE_REFRESH_TOKEN"]);
534
+ newVars["CLAUDE_ACCESS_TOKEN"] = tokens.access_token;
535
+ newVars["CLAUDE_REFRESH_TOKEN"] = tokens.refresh_token;
536
+ newVars["CLAUDE_TOKEN_EXPIRES"] = String(nowSec + tokens.expires_in);
537
+ console.log("Claude auth ... refreshed");
538
+ } catch {
539
+ console.log("Claude auth ... refresh failed, need to re-authenticate");
540
+ claudeExpired = true;
541
+ }
542
+ } else {
543
+ console.log("Claude auth ... token expired, need to re-authenticate");
544
+ claudeExpired = true;
545
+ }
546
+ } else {
547
+ console.log("Claude auth ... already configured");
548
+ }
549
+ } else if (existing["ANTHROPIC_API_KEY"]) {
519
550
  console.log("Claude auth ... already configured");
520
- } else {
551
+ }
552
+ if (claudeExpired || !existing["ANTHROPIC_API_KEY"] && !existing["CLAUDE_ACCESS_TOKEN"]) {
521
553
  console.log("How do you want to authenticate with Claude?");
522
554
  console.log("");
523
555
  console.log(" 1) Log in with Claude Max/Pro (recommended)");
@@ -584,8 +616,8 @@ async function setup() {
584
616
  console.log("gh CLI is installed but not logged in.");
585
617
  console.log("Without it, sandboxes can't commit or push changes.");
586
618
  console.log("");
587
- const login = await promptUser("Run gh auth login now? [y/n]: ");
588
- if (login === "y" || login === "yes") {
619
+ const login2 = await promptUser("Run gh auth login now? [y/n]: ");
620
+ if (login2 === "y" || login2 === "yes") {
589
621
  try {
590
622
  execSync2("gh auth login", { stdio: "inherit" });
591
623
  const token = shellOutput("gh auth token");
@@ -601,6 +633,46 @@ async function setup() {
601
633
  }
602
634
  }
603
635
  console.log("");
636
+ {
637
+ const detectedGit = getGitInfo(cwd);
638
+ const savedUrl = existing["GIT_REMOTE_URL"];
639
+ if (savedUrl && detectedGit && savedUrl !== detectedGit.remoteUrl) {
640
+ console.log(`Git repo ... mismatch!`);
641
+ console.log(` .env: ${savedUrl}`);
642
+ console.log(` local: ${detectedGit.remoteUrl}`);
643
+ console.log("");
644
+ const fix = await promptUser("Update .env to match local remote? [y/n]: ");
645
+ if (fix === "y" || fix === "yes" || !fix) {
646
+ newVars["GIT_REMOTE_URL"] = detectedGit.remoteUrl;
647
+ newVars["GIT_BRANCH"] = detectedGit.branch;
648
+ newVars["GIT_USER_NAME"] = detectedGit.userName;
649
+ newVars["GIT_USER_EMAIL"] = detectedGit.userEmail;
650
+ console.log("Git repo ... updated");
651
+ } else {
652
+ console.log("Git repo ... keeping .env value");
653
+ }
654
+ } else if (savedUrl) {
655
+ console.log(`Git repo ... ${savedUrl}`);
656
+ } else if (detectedGit) {
657
+ console.log(`Detected git remote: ${detectedGit.remoteUrl}`);
658
+ console.log(` Branch: ${detectedGit.branch}`);
659
+ console.log(` User: ${detectedGit.userName} <${detectedGit.userEmail}>`);
660
+ console.log("");
661
+ const useIt = await promptUser("Save this to .env? [y/n]: ");
662
+ if (useIt === "y" || useIt === "yes" || !useIt) {
663
+ newVars["GIT_REMOTE_URL"] = detectedGit.remoteUrl;
664
+ newVars["GIT_BRANCH"] = detectedGit.branch;
665
+ newVars["GIT_USER_NAME"] = detectedGit.userName;
666
+ newVars["GIT_USER_EMAIL"] = detectedGit.userEmail;
667
+ console.log("Git repo ... saved");
668
+ } else {
669
+ console.log("Git repo ... skipped");
670
+ }
671
+ } else {
672
+ console.log("Git repo ... not detected (not a git repo or no remote)");
673
+ }
674
+ }
675
+ console.log("");
604
676
  const hasVercel = existing["VERCEL_TOKEN"] && existing["VERCEL_TEAM_ID"] && existing["VERCEL_PROJECT_ID"];
605
677
  if (hasVercel) {
606
678
  console.log("Vercel ... already configured");
@@ -652,8 +724,8 @@ async function setup() {
652
724
  console.log("Vercel CLI is installed but not logged in.");
653
725
  console.log("Sandbox deployment requires Vercel auth.");
654
726
  console.log("");
655
- const login = await promptUser("Run vercel login now? [y/n]: ");
656
- if (login === "y" || login === "yes") {
727
+ const login2 = await promptUser("Run vercel login now? [y/n]: ");
728
+ if (login2 === "y" || login2 === "yes") {
657
729
  try {
658
730
  execSync2("vercel login", { stdio: "inherit" });
659
731
  } catch {
@@ -685,7 +757,17 @@ async function setup() {
685
757
  }
686
758
  console.log("");
687
759
  if (Object.keys(newVars).length > 0) {
688
- writeEnvVars(cwd, newVars);
760
+ const toUpdate = {};
761
+ const toAdd = {};
762
+ for (const [key, val] of Object.entries(newVars)) {
763
+ if (existing[key]) {
764
+ toUpdate[key] = val;
765
+ } else {
766
+ toAdd[key] = val;
767
+ }
768
+ }
769
+ if (Object.keys(toAdd).length > 0) writeEnvVars(cwd, toAdd);
770
+ if (Object.keys(toUpdate).length > 0) updateEnvVars(cwd, toUpdate);
689
771
  console.log("Wrote to .env:");
690
772
  for (const key of Object.keys(newVars)) {
691
773
  const display = key.includes("TOKEN") || key.includes("KEY") || key.includes("SECRET") ? newVars[key].slice(0, 8) + "..." : newVars[key];
@@ -699,6 +781,32 @@ async function setup() {
699
781
  console.log(" npm run dev Start the dev server");
700
782
  console.log(" npx viagen sandbox Deploy to a sandbox");
701
783
  }
784
+ function dev() {
785
+ const child = spawnChild("npx", ["vite"], {
786
+ cwd: process.cwd(),
787
+ stdio: ["inherit", "pipe", "inherit"],
788
+ shell: true
789
+ });
790
+ let opened = false;
791
+ child.stdout?.on("data", (chunk) => {
792
+ const text = chunk.toString();
793
+ process.stdout.write(text);
794
+ if (!opened) {
795
+ const match = text.match(/Local:\s+(https?:\/\/[^\s]+)/);
796
+ if (match) {
797
+ opened = true;
798
+ const baseUrl = match[1].replace(/\/$/, "");
799
+ const iframeUrl = `${baseUrl}/via/iframe`;
800
+ setTimeout(() => openBrowser2(iframeUrl), 500);
801
+ }
802
+ }
803
+ });
804
+ child.on("close", (code) => {
805
+ process.exit(code ?? 0);
806
+ });
807
+ process.on("SIGINT", () => child.kill("SIGINT"));
808
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
809
+ }
702
810
  function parseFlag(args, flag) {
703
811
  const idx = args.indexOf(flag);
704
812
  if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];
@@ -768,7 +876,21 @@ async function sandbox(args) {
768
876
  process.exit(1);
769
877
  }
770
878
  const githubToken = env["GITHUB_TOKEN"];
771
- const gitInfo = getGitInfo(cwd);
879
+ const envRemoteUrl = env["GIT_REMOTE_URL"];
880
+ const envBranch = env["GIT_BRANCH"];
881
+ const envUserName = env["GIT_USER_NAME"];
882
+ const envUserEmail = env["GIT_USER_EMAIL"];
883
+ const gitInfo = envRemoteUrl ? {
884
+ remoteUrl: envRemoteUrl,
885
+ branch: envBranch || "main",
886
+ userName: envUserName || "viagen",
887
+ userEmail: envUserEmail || "noreply@viagen.dev",
888
+ isDirty: false
889
+ // can't know from env, assume clean
890
+ } : getGitInfo(cwd);
891
+ if (envRemoteUrl) {
892
+ console.log("Using git info from .env");
893
+ }
772
894
  let deployGit;
773
895
  let overlayFiles;
774
896
  const branch = branchOverride || (gitInfo ? gitInfo.branch : "main");
@@ -804,7 +926,7 @@ async function sandbox(args) {
804
926
  userEmail: gitInfo.userEmail,
805
927
  token: githubToken
806
928
  });
807
- if (gitInfo.isDirty && !branchOverride) {
929
+ if (!envRemoteUrl && gitInfo.isDirty && !branchOverride) {
808
930
  console.log("");
809
931
  console.log("Your working tree has uncommitted changes.");
810
932
  console.log("");
@@ -835,11 +957,11 @@ async function sandbox(args) {
835
957
  "Note: Not a git repo \u2014 sandbox will use file upload (ephemeral)."
836
958
  );
837
959
  }
838
- const pkgPath = join2(cwd, "package.json");
839
- if (existsSync(pkgPath)) {
960
+ const configPath = join2(cwd, ".viagen", "config.json");
961
+ if (existsSync(configPath)) {
840
962
  try {
841
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
842
- const sandboxFiles = pkg.viagen?.sandboxFiles ?? [];
963
+ const config = JSON.parse(readFileSync2(configPath, "utf-8"));
964
+ const sandboxFiles = config.sandboxFiles ?? [];
843
965
  if (sandboxFiles.length > 0) {
844
966
  const extra = [];
845
967
  for (const file of sandboxFiles) {
@@ -852,7 +974,7 @@ async function sandbox(args) {
852
974
  }
853
975
  if (extra.length > 0) {
854
976
  overlayFiles = [...overlayFiles ?? [], ...extra];
855
- console.log(` Including ${extra.length} extra file(s) from sandboxFiles config.`);
977
+ console.log(` Including ${extra.length} extra file(s) from sandboxFiles.`);
856
978
  }
857
979
  }
858
980
  } catch {
@@ -882,19 +1004,196 @@ async function sandbox(args) {
882
1004
  } : void 0,
883
1005
  timeoutMinutes
884
1006
  });
1007
+ const iframeUrl = result.url.replace("?token=", "/via/iframe?token=");
1008
+ const chatUrl = result.url.replace("?token=", "/via/ui?token=");
885
1009
  console.log("");
886
1010
  console.log("Sandbox deployed!");
887
1011
  console.log("");
888
- console.log(` URL: ${result.url}`);
1012
+ console.log(` App: ${result.url}`);
1013
+ console.log(` Split view: ${iframeUrl}`);
1014
+ console.log(` Chat only: ${chatUrl}`);
1015
+ console.log("");
889
1016
  console.log(` Sandbox ID: ${result.sandboxId}`);
890
1017
  console.log(
891
1018
  ` Mode: ${result.mode === "git" ? "git clone (can push)" : "file upload (ephemeral)"}`
892
1019
  );
893
- console.log(` Token: ${result.token}`);
894
1020
  console.log(` Timeout: ${timeoutMinutes ?? 30} minutes`);
895
1021
  console.log("");
896
1022
  console.log(`Stop with: npx viagen sandbox stop ${result.sandboxId}`);
897
- openBrowser2(result.url);
1023
+ openBrowser2(iframeUrl);
1024
+ }
1025
+ var PLATFORM_URL = "http://localhost:5175";
1026
+ async function login() {
1027
+ const existing = await loadCredentials();
1028
+ if (existing) {
1029
+ const client2 = createViagen({
1030
+ baseUrl: existing.baseUrl,
1031
+ token: existing.token
1032
+ });
1033
+ try {
1034
+ const user2 = await client2.auth.me();
1035
+ if (user2) {
1036
+ console.log(`Already logged in as ${user2.email}`);
1037
+ const answer = await promptUser("Log in again? [y/n]: ");
1038
+ if (answer !== "y" && answer !== "yes") return;
1039
+ }
1040
+ } catch {
1041
+ }
1042
+ }
1043
+ console.log("viagen login");
1044
+ console.log("");
1045
+ const client = createViagen({ baseUrl: PLATFORM_URL });
1046
+ console.log("Opening browser to authorize...");
1047
+ const { token, expiresAt } = await client.auth.loginCli({
1048
+ onOpenUrl: (url) => {
1049
+ openBrowser2(url);
1050
+ console.log("");
1051
+ console.log("If the browser didn't open, visit:");
1052
+ console.log(` ${url}`);
1053
+ console.log("");
1054
+ console.log("Waiting for authorization...");
1055
+ }
1056
+ });
1057
+ await saveCredentials({ token, baseUrl: PLATFORM_URL, expiresAt });
1058
+ const authed = createViagen({ baseUrl: PLATFORM_URL, token });
1059
+ const user = await authed.auth.me();
1060
+ console.log("");
1061
+ if (user) {
1062
+ console.log(`Logged in as ${user.email}`);
1063
+ } else {
1064
+ console.log("Logged in.");
1065
+ }
1066
+ console.log("Credentials saved to ~/.config/viagen/credentials.json");
1067
+ }
1068
+ async function logout() {
1069
+ const existing = await loadCredentials();
1070
+ if (!existing) {
1071
+ console.log("Not logged in.");
1072
+ return;
1073
+ }
1074
+ await clearCredentials();
1075
+ console.log("Logged out. Credentials removed.");
1076
+ }
1077
+ async function whoami() {
1078
+ const existing = await loadCredentials();
1079
+ if (!existing) {
1080
+ console.log("Not logged in. Run `viagen login` to authenticate.");
1081
+ return;
1082
+ }
1083
+ const client = createViagen({
1084
+ baseUrl: existing.baseUrl,
1085
+ token: existing.token
1086
+ });
1087
+ try {
1088
+ const user = await client.auth.me();
1089
+ if (user) {
1090
+ console.log(`${user.email}${user.name ? ` (${user.name})` : ""}`);
1091
+ if (user.organizations.length > 0) {
1092
+ console.log(
1093
+ `Orgs: ${user.organizations.map((o) => o.name).join(", ")}`
1094
+ );
1095
+ }
1096
+ } else {
1097
+ console.log("Session expired. Run `viagen login` to re-authenticate.");
1098
+ }
1099
+ } catch {
1100
+ console.log("Session expired. Run `viagen login` to re-authenticate.");
1101
+ }
1102
+ }
1103
+ async function requireClient() {
1104
+ const creds = await loadCredentials();
1105
+ if (!creds) {
1106
+ console.error("Not logged in. Run `viagen login` first.");
1107
+ process.exit(1);
1108
+ }
1109
+ return createViagen({ baseUrl: creds.baseUrl, token: creds.token });
1110
+ }
1111
+ async function orgs(args) {
1112
+ const sub = args[0];
1113
+ if (sub === "create") {
1114
+ const name = args.slice(1).join(" ");
1115
+ if (!name) {
1116
+ console.error("Usage: viagen orgs create <name>");
1117
+ process.exit(1);
1118
+ }
1119
+ const client2 = await requireClient();
1120
+ const org = await client2.orgs.create({ name });
1121
+ console.log(`Created org "${org.name}" (${org.id})`);
1122
+ return;
1123
+ }
1124
+ if (sub === "invite") {
1125
+ const email = args[1];
1126
+ if (!email) {
1127
+ console.error("Usage: viagen orgs invite <email>");
1128
+ process.exit(1);
1129
+ }
1130
+ const client2 = await requireClient();
1131
+ await client2.orgs.addMember({ email });
1132
+ console.log(`Invited ${email}`);
1133
+ return;
1134
+ }
1135
+ const client = await requireClient();
1136
+ const memberships = await client.orgs.list() ?? [];
1137
+ if (memberships.length === 0) {
1138
+ console.log("No organizations. Create one with `viagen orgs create <name>`.");
1139
+ return;
1140
+ }
1141
+ for (const m of memberships) {
1142
+ const role = m.role ? ` (${m.role})` : "";
1143
+ console.log(` ${m.organizationName}${role}`);
1144
+ }
1145
+ }
1146
+ async function projects(args) {
1147
+ const sub = args[0];
1148
+ if (sub === "create") {
1149
+ const name = args.slice(1).join(" ");
1150
+ if (!name) {
1151
+ console.error("Usage: viagen projects create <name>");
1152
+ process.exit(1);
1153
+ }
1154
+ const client2 = await requireClient();
1155
+ const project = await client2.projects.create({ name });
1156
+ console.log(`Created project "${project.name}" (${project.id})`);
1157
+ return;
1158
+ }
1159
+ if (sub === "get") {
1160
+ const id = args[1];
1161
+ if (!id) {
1162
+ console.error("Usage: viagen projects get <id>");
1163
+ process.exit(1);
1164
+ }
1165
+ const client2 = await requireClient();
1166
+ const project = await client2.projects.get(id);
1167
+ console.log(` Name: ${project.name}`);
1168
+ console.log(` ID: ${project.id}`);
1169
+ if (project.githubRepo) console.log(` GitHub: ${project.githubRepo}`);
1170
+ if (project.templateId) console.log(` Template: ${project.templateId}`);
1171
+ console.log(` Created: ${project.createdAt}`);
1172
+ return;
1173
+ }
1174
+ if (sub === "delete") {
1175
+ const id = args[1];
1176
+ if (!id) {
1177
+ console.error("Usage: viagen projects delete <id>");
1178
+ process.exit(1);
1179
+ }
1180
+ const answer = await promptUser(`Delete project ${id}? [y/n]: `);
1181
+ if (answer !== "y" && answer !== "yes") return;
1182
+ const client2 = await requireClient();
1183
+ await client2.projects.delete(id);
1184
+ console.log("Project deleted.");
1185
+ return;
1186
+ }
1187
+ const client = await requireClient();
1188
+ const list = await client.projects.list() ?? [];
1189
+ if (list.length === 0) {
1190
+ console.log("No projects. Create one with `viagen projects create <name>`.");
1191
+ return;
1192
+ }
1193
+ for (const p of list) {
1194
+ const repo = p.githubRepo ? ` (${p.githubRepo})` : "";
1195
+ console.log(` ${p.name}${repo} ${p.id}`);
1196
+ }
898
1197
  }
899
1198
  function help() {
900
1199
  console.log("viagen \u2014 Claude Code in your Vite dev server");
@@ -903,6 +1202,17 @@ function help() {
903
1202
  console.log(" viagen <command>");
904
1203
  console.log("");
905
1204
  console.log("Commands:");
1205
+ console.log(" login Log in to the viagen platform");
1206
+ console.log(" logout Log out and remove credentials");
1207
+ console.log(" whoami Show current user");
1208
+ console.log(" orgs List your organizations");
1209
+ console.log(" orgs create <name> Create a new organization");
1210
+ console.log(" orgs invite <email> Invite a member to the org");
1211
+ console.log(" projects List projects in current org");
1212
+ console.log(" projects create <name> Create a new project");
1213
+ console.log(" projects get <id> Show project details");
1214
+ console.log(" projects delete <id> Delete a project");
1215
+ console.log(" dev Start Vite and open the split view");
906
1216
  console.log(" setup Set up .env with API keys and tokens");
907
1217
  console.log(" sandbox [-b branch] [-t min] Deploy your project to a Vercel Sandbox");
908
1218
  console.log(" sandbox stop <id> Stop a running sandbox");
@@ -928,6 +1238,18 @@ function help() {
928
1238
  console.log(
929
1239
  " GITHUB_TOKEN Enables git commit+push from sandbox."
930
1240
  );
1241
+ console.log(
1242
+ " GIT_REMOTE_URL Git remote (from setup, overrides runtime detection)."
1243
+ );
1244
+ console.log(
1245
+ " GIT_BRANCH Git branch for sandbox."
1246
+ );
1247
+ console.log(
1248
+ " GIT_USER_NAME Git user name for sandbox commits."
1249
+ );
1250
+ console.log(
1251
+ " GIT_USER_EMAIL Git user email for sandbox commits."
1252
+ );
931
1253
  console.log(
932
1254
  " VIAGEN_AUTH_TOKEN Protects all endpoints with token auth."
933
1255
  );
@@ -946,7 +1268,20 @@ function help() {
946
1268
  async function main() {
947
1269
  const args = process.argv.slice(2);
948
1270
  const command = args[0];
949
- if (command === "setup") {
1271
+ if (command === "login") {
1272
+ await login();
1273
+ } else if (command === "logout") {
1274
+ await logout();
1275
+ } else if (command === "whoami") {
1276
+ await whoami();
1277
+ } else if (command === "orgs") {
1278
+ await orgs(args.slice(1));
1279
+ } else if (command === "projects") {
1280
+ await projects(args.slice(1));
1281
+ } else if (command === "dev") {
1282
+ dev();
1283
+ return;
1284
+ } else if (command === "setup") {
950
1285
  await setup();
951
1286
  } else if (command === "sandbox") {
952
1287
  await sandbox(args.slice(1));
package/dist/index.d.ts CHANGED
@@ -76,6 +76,12 @@ interface ViagenOptions {
76
76
  * @example ["config.json"]
77
77
  */
78
78
  sandboxFiles?: string[];
79
+ /**
80
+ * Files and directories editable through the UI file panel.
81
+ * Paths are relative to the project root. Directories include all files within.
82
+ * @example ['src/components', '.env', 'vite.config.ts']
83
+ */
84
+ editable?: string[];
79
85
  }
80
86
 
81
87
  declare function viagen(options?: ViagenOptions): Plugin;