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 +36 -9
- package/dist/cli.js +353 -18
- package/dist/index.d.ts +6 -0
- package/dist/index.js +656 -62
- package/package.json +5 -5
- package/site/.viagen/server.log +0 -1
- package/site/icon.png +0 -0
- package/site/icon.svg +0 -2
- package/site/index.html +0 -618
- package/site/vite.config.ts +0 -7
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
|
|
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
|
-
###
|
|
71
|
+
### SSR Frameworks (React Router, Remix, SvelteKit, etc.)
|
|
70
72
|
|
|
71
|
-
|
|
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
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
|
588
|
-
if (
|
|
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
|
|
656
|
-
if (
|
|
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
|
-
|
|
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
|
|
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
|
|
839
|
-
if (existsSync(
|
|
960
|
+
const configPath = join2(cwd, ".viagen", "config.json");
|
|
961
|
+
if (existsSync(configPath)) {
|
|
840
962
|
try {
|
|
841
|
-
const
|
|
842
|
-
const 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
|
|
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(`
|
|
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(
|
|
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 === "
|
|
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;
|