viagen-sdk 0.0.0

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 ADDED
@@ -0,0 +1,209 @@
1
+ # viagen-sdk
2
+
3
+ TypeScript client for the viagen platform API. Used by sandboxes, the CLI, and any external consumer that needs to talk to the platform.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install viagen-sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### Web (cookie-based)
14
+
15
+ ```ts
16
+ import { createViagen } from 'viagen-sdk'
17
+
18
+ const viagen = createViagen({ baseUrl: 'https://app.viagen.dev' })
19
+
20
+ // Redirect to login
21
+ viagen.auth.login('github')
22
+
23
+ // Get current user (uses session cookie)
24
+ const user = await viagen.auth.me()
25
+ ```
26
+
27
+ ### CLI / Node.js (token-based)
28
+
29
+ ```ts
30
+ import { createViagen, saveCredentials, createViagenFromCredentials } from 'viagen-sdk'
31
+
32
+ // First time: open browser to authorize
33
+ const viagen = createViagen({ baseUrl: 'https://app.viagen.dev' })
34
+ const { token } = await viagen.auth.loginCli()
35
+
36
+ // Save token for future sessions
37
+ await saveCredentials({ token, baseUrl: 'https://app.viagen.dev' })
38
+
39
+ // Later: create client from stored credentials
40
+ const client = await createViagenFromCredentials()
41
+ const projects = await client.projects.list()
42
+ ```
43
+
44
+ ## API Reference
45
+
46
+ ### `createViagen(config)`
47
+
48
+ Creates a client instance.
49
+
50
+ ```ts
51
+ // Web: uses cookies
52
+ const viagen = createViagen({ baseUrl: 'http://localhost:5173' })
53
+
54
+ // CLI: uses Bearer token
55
+ const viagen = createViagen({ baseUrl: 'https://app.viagen.dev', token: 'your-api-token' })
56
+ ```
57
+
58
+ | Option | Type | Description |
59
+ |---|---|---|
60
+ | `baseUrl` | `string` | Platform URL |
61
+ | `token` | `string?` | API token. When set, uses `Authorization: Bearer` instead of cookies |
62
+
63
+ ### `createViagenFromCredentials(overrides?)`
64
+
65
+ Creates a client from stored credentials (`~/.config/viagen/credentials.json`). Returns `null` if no credentials found.
66
+
67
+ ---
68
+
69
+ ### `viagen.auth`
70
+
71
+ | Method | Description |
72
+ |---|---|
73
+ | `login(provider?)` | Redirects the browser to OAuth login. Default: `'github'`. Options: `'github'`, `'google'`, `'microsoft'`. Web only |
74
+ | `loginCli(options?)` | Opens browser to authorize, starts localhost server, captures API token. Node.js only |
75
+ | `me()` | Returns the current user + orgs, or `null` if not authenticated |
76
+ | `logout()` | Ends the session. In a browser, reloads the page |
77
+ | `listTokens()` | List the current user's API tokens |
78
+ | `revokeToken(tokenId)` | Revoke an API token by ID |
79
+
80
+ **`loginCli` options:**
81
+
82
+ ```ts
83
+ {
84
+ port?: number // Preferred localhost port (default: random)
85
+ onOpenUrl?: (url: string) => void // Custom handler to open the URL (default: system browser)
86
+ }
87
+ ```
88
+
89
+ ---
90
+
91
+ ### `viagen.orgs`
92
+
93
+ | Method | Description |
94
+ |---|---|
95
+ | `list()` | List the current user's org memberships |
96
+ | `create({ name })` | Create a new org. The caller becomes admin |
97
+ | `addMember({ email })` | Add a member by email. Admin only |
98
+
99
+ ---
100
+
101
+ ### `viagen.projects`
102
+
103
+ | Method | Description |
104
+ |---|---|
105
+ | `list()` | List all projects in the current org |
106
+ | `create(input)` | Create a project. Admin only |
107
+ | `get(id)` | Get a single project by ID |
108
+ | `update(id, input)` | Update a project. Admin only |
109
+ | `delete(id)` | Delete a project. Admin only |
110
+
111
+ **`CreateProjectInput`**
112
+
113
+ ```ts
114
+ {
115
+ name: string
116
+ templateId?: string // e.g. 'react-router'
117
+ vercelProjectId?: string // link to existing Vercel project
118
+ githubRepo?: string // 'owner/repo'
119
+ }
120
+ ```
121
+
122
+ **`UpdateProjectInput`**
123
+
124
+ ```ts
125
+ {
126
+ name?: string
127
+ vercelProjectId?: string | null
128
+ githubRepo?: string | null
129
+ }
130
+ ```
131
+
132
+ ---
133
+
134
+ ### `viagen.vercel`
135
+
136
+ | Method | Description |
137
+ |---|---|
138
+ | `integrationStatus()` | Returns `{ github: boolean, vercel: boolean }` for the current org |
139
+ | `disconnect()` | Remove the org's Vercel connection. Admin only |
140
+ | `listProjects(params?)` | List Vercel projects. Params: `{ search?, limit? }` |
141
+
142
+ > Connecting Vercel is done via OAuth redirect (`/api/integrations/vercel/start`), not through the SDK.
143
+
144
+ ---
145
+
146
+ ### `viagen.github`
147
+
148
+ | Method | Description |
149
+ |---|---|
150
+ | `listRepos(params?)` | List repos from the org's connected GitHub account. Params: `{ page?, perPage? }` |
151
+
152
+ > Connecting GitHub is done via OAuth redirect (`/api/integrations/github/start`), not through the SDK.
153
+
154
+ ---
155
+
156
+ ## Credentials
157
+
158
+ Utilities for managing stored CLI credentials (`~/.config/viagen/credentials.json`):
159
+
160
+ ```ts
161
+ import { saveCredentials, loadCredentials, clearCredentials } from 'viagen-sdk'
162
+
163
+ await saveCredentials({ token: '...', baseUrl: 'https://app.viagen.dev' })
164
+ const creds = await loadCredentials() // { token, baseUrl } | null
165
+ await clearCredentials() // deletes the file
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Error Handling
171
+
172
+ All methods throw `ViagenApiError` on failure:
173
+
174
+ ```ts
175
+ import { ViagenApiError } from 'viagen-sdk'
176
+
177
+ try {
178
+ await viagen.projects.create({ name: 'my-app' })
179
+ } catch (err) {
180
+ if (err instanceof ViagenApiError) {
181
+ console.error(err.status, err.message)
182
+ }
183
+ }
184
+ ```
185
+
186
+ ## API Routes
187
+
188
+ The SDK maps to these platform resource routes:
189
+
190
+ | SDK | Method | Route |
191
+ |---|---|---|
192
+ | `auth.login` | GET (redirect) | `/api/auth/login/:provider` |
193
+ | `auth.loginCli` | GET (browser) | `/cli/authorize?port=...` |
194
+ | `auth.me` | GET | `/api/auth/me` |
195
+ | `auth.logout` | POST | `/api/auth/logout` |
196
+ | `auth.listTokens` | GET | `/api/auth/tokens` |
197
+ | `auth.revokeToken` | DELETE | `/api/auth/tokens` |
198
+ | `orgs.list` | GET | `/api/orgs` |
199
+ | `orgs.create` | POST | `/api/orgs` |
200
+ | `orgs.addMember` | POST | `/api/orgs/members` |
201
+ | `projects.list` | GET | `/api/projects` |
202
+ | `projects.create` | POST | `/api/projects` |
203
+ | `projects.get` | GET | `/api/projects/:id` |
204
+ | `projects.update` | PATCH | `/api/projects/:id` |
205
+ | `projects.delete` | DELETE | `/api/projects/:id` |
206
+ | `vercel.integrationStatus` | GET | `/api/integrations/status` |
207
+ | `vercel.disconnect` | DELETE | `/api/integrations/vercel` |
208
+ | `vercel.listProjects` | GET | `/api/vercel/projects` |
209
+ | `github.listRepos` | GET | `/api/github/repos` |
@@ -0,0 +1,40 @@
1
+ // src/credentials.ts
2
+ import { join } from "path";
3
+ import { readFile, writeFile, mkdir, unlink } from "fs/promises";
4
+ import { homedir } from "os";
5
+ function credentialsDir() {
6
+ return join(
7
+ process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"),
8
+ "viagen"
9
+ );
10
+ }
11
+ function credentialsPath() {
12
+ return join(credentialsDir(), "credentials.json");
13
+ }
14
+ async function loadCredentials() {
15
+ try {
16
+ const raw = await readFile(credentialsPath(), "utf-8");
17
+ return JSON.parse(raw);
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+ async function saveCredentials(creds) {
23
+ const dir = credentialsDir();
24
+ await mkdir(dir, { recursive: true });
25
+ await writeFile(credentialsPath(), JSON.stringify(creds, null, 2) + "\n", {
26
+ mode: 384
27
+ });
28
+ }
29
+ async function clearCredentials() {
30
+ try {
31
+ await unlink(credentialsPath());
32
+ } catch {
33
+ }
34
+ }
35
+
36
+ export {
37
+ loadCredentials,
38
+ saveCredentials,
39
+ clearCredentials
40
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ clearCredentials,
3
+ loadCredentials,
4
+ saveCredentials
5
+ } from "./chunk-TG7LNDF6.js";
6
+ export {
7
+ clearCredentials,
8
+ loadCredentials,
9
+ saveCredentials
10
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/credentials.ts
34
+ var credentials_exports = {};
35
+ __export(credentials_exports, {
36
+ clearCredentials: () => clearCredentials,
37
+ loadCredentials: () => loadCredentials,
38
+ saveCredentials: () => saveCredentials
39
+ });
40
+ function credentialsDir() {
41
+ return (0, import_node_path.join)(
42
+ process.env.XDG_CONFIG_HOME ?? (0, import_node_path.join)((0, import_node_os.homedir)(), ".config"),
43
+ "viagen"
44
+ );
45
+ }
46
+ function credentialsPath() {
47
+ return (0, import_node_path.join)(credentialsDir(), "credentials.json");
48
+ }
49
+ async function loadCredentials() {
50
+ try {
51
+ const raw = await (0, import_promises.readFile)(credentialsPath(), "utf-8");
52
+ return JSON.parse(raw);
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+ async function saveCredentials(creds) {
58
+ const dir = credentialsDir();
59
+ await (0, import_promises.mkdir)(dir, { recursive: true });
60
+ await (0, import_promises.writeFile)(credentialsPath(), JSON.stringify(creds, null, 2) + "\n", {
61
+ mode: 384
62
+ });
63
+ }
64
+ async function clearCredentials() {
65
+ try {
66
+ await (0, import_promises.unlink)(credentialsPath());
67
+ } catch {
68
+ }
69
+ }
70
+ var import_node_path, import_promises, import_node_os;
71
+ var init_credentials = __esm({
72
+ "src/credentials.ts"() {
73
+ "use strict";
74
+ import_node_path = require("path");
75
+ import_promises = require("fs/promises");
76
+ import_node_os = require("os");
77
+ }
78
+ });
79
+
80
+ // src/index.ts
81
+ var index_exports = {};
82
+ __export(index_exports, {
83
+ ViagenApiError: () => ViagenApiError,
84
+ clearCredentials: () => clearCredentials,
85
+ createViagen: () => createViagen,
86
+ createViagenFromCredentials: () => createViagenFromCredentials,
87
+ loadCredentials: () => loadCredentials,
88
+ saveCredentials: () => saveCredentials
89
+ });
90
+ module.exports = __toCommonJS(index_exports);
91
+
92
+ // src/auth.ts
93
+ function createAuthClient(baseUrl, request) {
94
+ return {
95
+ login(provider = "github") {
96
+ window.location.href = `${baseUrl}/api/auth/login/${provider}`;
97
+ },
98
+ async me() {
99
+ try {
100
+ const data = await request("/api/auth/me");
101
+ if (!data.authenticated) return null;
102
+ return { ...data.user, organizations: data.organizations };
103
+ } catch {
104
+ return null;
105
+ }
106
+ },
107
+ async logout() {
108
+ await request("/api/auth/logout", { method: "POST" });
109
+ if (typeof window !== "undefined") {
110
+ window.location.reload();
111
+ }
112
+ },
113
+ async loginCli(options = {}) {
114
+ const { port: preferredPort, onOpenUrl } = options;
115
+ const http = await import("http");
116
+ return new Promise((resolve, reject) => {
117
+ const server = http.createServer();
118
+ const timeout = setTimeout(() => {
119
+ server.close();
120
+ reject(new Error("Login timed out"));
121
+ }, 5 * 60 * 1e3);
122
+ server.listen(preferredPort ?? 0, "127.0.0.1", () => {
123
+ const address = server.address();
124
+ const port = address.port;
125
+ const loginUrl = `${baseUrl}/cli/authorize?port=${port}`;
126
+ if (onOpenUrl) {
127
+ onOpenUrl(loginUrl);
128
+ } else {
129
+ import("child_process").then(({ exec }) => {
130
+ const cmd = process.platform === "darwin" ? `open "${loginUrl}"` : process.platform === "win32" ? `start "${loginUrl}"` : `xdg-open "${loginUrl}"`;
131
+ exec(cmd);
132
+ }).catch(() => {
133
+ console.log(`Open this URL in your browser:
134
+ ${loginUrl}`);
135
+ });
136
+ }
137
+ });
138
+ server.on("request", (req, res) => {
139
+ const url = new URL(req.url, `http://127.0.0.1`);
140
+ if (url.pathname === "/callback") {
141
+ const token = url.searchParams.get("token");
142
+ const error = url.searchParams.get("error");
143
+ if (error || !token) {
144
+ res.writeHead(200, { "Content-Type": "text/html" });
145
+ res.end(
146
+ '<html><body style="font-family:system-ui;display:flex;justify-content:center;padding-top:40vh"><div><h2>Login failed</h2><p>You can close this tab.</p></div></body></html>'
147
+ );
148
+ clearTimeout(timeout);
149
+ server.close();
150
+ reject(new Error(error ?? "No token received"));
151
+ return;
152
+ }
153
+ res.writeHead(200, { "Content-Type": "text/html" });
154
+ res.end(
155
+ '<html><body style="font-family:system-ui;display:flex;justify-content:center;padding-top:40vh"><div><h2>Login successful!</h2><p>You can close this tab.</p></div></body></html>'
156
+ );
157
+ clearTimeout(timeout);
158
+ server.close();
159
+ resolve({ token, expiresAt: "" });
160
+ } else {
161
+ res.writeHead(404);
162
+ res.end();
163
+ }
164
+ });
165
+ });
166
+ },
167
+ async listTokens() {
168
+ const data = await request("/api/auth/tokens");
169
+ return data.tokens;
170
+ },
171
+ async revokeToken(tokenId) {
172
+ await request("/api/auth/tokens", {
173
+ method: "DELETE",
174
+ body: JSON.stringify({ tokenId })
175
+ });
176
+ }
177
+ };
178
+ }
179
+
180
+ // src/orgs.ts
181
+ function createOrgsClient(_baseUrl, request) {
182
+ return {
183
+ async list() {
184
+ const data = await request("/api/orgs");
185
+ return data.memberships;
186
+ },
187
+ async create(input) {
188
+ const data = await request("/api/orgs", {
189
+ method: "POST",
190
+ body: JSON.stringify(input)
191
+ });
192
+ return data.organization;
193
+ },
194
+ async addMember(input) {
195
+ await request("/api/orgs/members", {
196
+ method: "POST",
197
+ body: JSON.stringify(input)
198
+ });
199
+ }
200
+ };
201
+ }
202
+
203
+ // src/projects.ts
204
+ function createProjectsClient(_baseUrl, request) {
205
+ return {
206
+ async list() {
207
+ const data = await request("/api/projects");
208
+ return data.projects;
209
+ },
210
+ async create(input) {
211
+ const data = await request("/api/projects", {
212
+ method: "POST",
213
+ body: JSON.stringify(input)
214
+ });
215
+ return data.project;
216
+ },
217
+ async get(id) {
218
+ const data = await request(`/api/projects/${id}`);
219
+ return data.project;
220
+ },
221
+ async update(id, input) {
222
+ const data = await request(`/api/projects/${id}`, {
223
+ method: "PATCH",
224
+ body: JSON.stringify(input)
225
+ });
226
+ return data.project;
227
+ },
228
+ async delete(id) {
229
+ await request(`/api/projects/${id}`, { method: "DELETE" });
230
+ }
231
+ };
232
+ }
233
+
234
+ // src/vercel.ts
235
+ function createVercelClient(_baseUrl, request) {
236
+ return {
237
+ async integrationStatus() {
238
+ return request("/api/integrations/status");
239
+ },
240
+ async disconnect() {
241
+ await request("/api/integrations/vercel", { method: "DELETE" });
242
+ },
243
+ async listProjects(params) {
244
+ const qs = new URLSearchParams();
245
+ if (params?.search) qs.set("search", params.search);
246
+ if (params?.limit) qs.set("limit", String(params.limit));
247
+ const query = qs.toString();
248
+ const data = await request(`/api/vercel/projects${query ? `?${query}` : ""}`);
249
+ return data.projects;
250
+ }
251
+ };
252
+ }
253
+
254
+ // src/github.ts
255
+ function createGitHubClient(_baseUrl, request) {
256
+ return {
257
+ async listRepos(params) {
258
+ const qs = new URLSearchParams();
259
+ if (params?.page) qs.set("page", String(params.page));
260
+ if (params?.perPage) qs.set("per_page", String(params.perPage));
261
+ const query = qs.toString();
262
+ const data = await request(`/api/github/repos${query ? `?${query}` : ""}`);
263
+ return data.repos;
264
+ }
265
+ };
266
+ }
267
+
268
+ // src/index.ts
269
+ init_credentials();
270
+ var ViagenApiError = class extends Error {
271
+ constructor(status, message, detail) {
272
+ super(message);
273
+ this.status = status;
274
+ this.detail = detail;
275
+ this.name = "ViagenApiError";
276
+ }
277
+ };
278
+ function createViagen(config) {
279
+ const baseUrl = config.baseUrl.replace(/\/+$/, "");
280
+ async function request(path, options) {
281
+ const headers = {
282
+ "Content-Type": "application/json",
283
+ ...options?.headers
284
+ };
285
+ if (config.token) {
286
+ headers["Authorization"] = `Bearer ${config.token}`;
287
+ }
288
+ const res = await fetch(`${baseUrl}${path}`, {
289
+ ...options,
290
+ ...config.token ? {} : { credentials: "include" },
291
+ headers
292
+ });
293
+ if (!res.ok) {
294
+ const body = await res.json().catch(() => ({}));
295
+ throw new ViagenApiError(res.status, body.error ?? "Request failed", body.message);
296
+ }
297
+ return res.json();
298
+ }
299
+ return {
300
+ auth: createAuthClient(baseUrl, request),
301
+ orgs: createOrgsClient(baseUrl, request),
302
+ projects: createProjectsClient(baseUrl, request),
303
+ vercel: createVercelClient(baseUrl, request),
304
+ github: createGitHubClient(baseUrl, request)
305
+ };
306
+ }
307
+ async function createViagenFromCredentials(overrides) {
308
+ const { loadCredentials: loadCredentials2 } = await Promise.resolve().then(() => (init_credentials(), credentials_exports));
309
+ const creds = await loadCredentials2();
310
+ if (!creds) return null;
311
+ return createViagen({
312
+ baseUrl: creds.baseUrl,
313
+ token: creds.token,
314
+ ...overrides
315
+ });
316
+ }
317
+ // Annotate the CommonJS export names for ESM import in node:
318
+ 0 && (module.exports = {
319
+ ViagenApiError,
320
+ clearCredentials,
321
+ createViagen,
322
+ createViagenFromCredentials,
323
+ loadCredentials,
324
+ saveCredentials
325
+ });
@@ -0,0 +1,189 @@
1
+ interface Project {
2
+ id: string;
3
+ organizationId: string;
4
+ name: string;
5
+ templateId: string | null;
6
+ vercelProjectId: string | null;
7
+ githubRepo: string | null;
8
+ createdAt: string;
9
+ updatedAt: string;
10
+ }
11
+ interface CreateProjectInput {
12
+ name: string;
13
+ templateId?: string;
14
+ vercelProjectId?: string;
15
+ githubRepo?: string;
16
+ }
17
+ interface UpdateProjectInput {
18
+ name?: string;
19
+ vercelProjectId?: string | null;
20
+ githubRepo?: string | null;
21
+ }
22
+ interface ProjectsClient {
23
+ /** List all projects in the current organization. */
24
+ list(): Promise<Project[]>;
25
+ /** Create a new project. Admin only. */
26
+ create(input: CreateProjectInput): Promise<Project>;
27
+ /** Get a single project by ID. */
28
+ get(id: string): Promise<Project>;
29
+ /** Update a project. Admin only. */
30
+ update(id: string, input: UpdateProjectInput): Promise<Project>;
31
+ /** Delete a project by ID. Admin only. */
32
+ delete(id: string): Promise<void>;
33
+ }
34
+
35
+ interface ViagenUser {
36
+ id: string;
37
+ email: string;
38
+ name: string | null;
39
+ avatarUrl: string | null;
40
+ }
41
+ interface OrgInfo {
42
+ id: string;
43
+ name: string;
44
+ role: string;
45
+ }
46
+ interface AuthResult {
47
+ authenticated: true;
48
+ user: ViagenUser;
49
+ organizations: OrgInfo[];
50
+ }
51
+ interface ApiTokenInfo {
52
+ id: string;
53
+ name: string;
54
+ prefix: string;
55
+ expiresAt: string;
56
+ lastUsedAt: string | null;
57
+ createdAt: string;
58
+ }
59
+ interface AuthClient {
60
+ /** Redirect the browser to a provider login page. (Web only) */
61
+ login(provider?: 'google' | 'github' | 'microsoft'): void;
62
+ /** Get the current authenticated user. Works with both cookie and token auth. */
63
+ me(): Promise<(ViagenUser & {
64
+ organizations: OrgInfo[];
65
+ }) | null>;
66
+ /** Log out the current user. In a browser, reloads the page. */
67
+ logout(): Promise<void>;
68
+ /**
69
+ * CLI login: opens browser to authorize page, starts localhost server, captures token.
70
+ * Only available in Node.js environments.
71
+ */
72
+ loginCli(options?: {
73
+ port?: number;
74
+ onOpenUrl?: (url: string) => void;
75
+ }): Promise<{
76
+ token: string;
77
+ expiresAt: string;
78
+ }>;
79
+ /** List the current user's API tokens. */
80
+ listTokens(): Promise<ApiTokenInfo[]>;
81
+ /** Revoke an API token by its hashed ID. */
82
+ revokeToken(tokenId: string): Promise<void>;
83
+ }
84
+
85
+ interface OrgMembership {
86
+ id: string;
87
+ organizationId: string;
88
+ organizationName: string;
89
+ role: string | undefined;
90
+ status: string;
91
+ }
92
+ interface Org {
93
+ id: string;
94
+ name: string;
95
+ }
96
+ interface OrgsClient {
97
+ /** List the current user's organization memberships. */
98
+ list(): Promise<OrgMembership[]>;
99
+ /** Create a new organization. The current user becomes admin. */
100
+ create(input: {
101
+ name: string;
102
+ }): Promise<Org>;
103
+ /** Add a member by email to the current organization. Admin only. */
104
+ addMember(input: {
105
+ email: string;
106
+ }): Promise<void>;
107
+ }
108
+
109
+ interface VercelProject {
110
+ id: string;
111
+ name: string;
112
+ framework: string | null;
113
+ link?: {
114
+ type: string;
115
+ org: string;
116
+ repo: string;
117
+ productionBranch: string;
118
+ };
119
+ }
120
+ interface VercelListProjectsParams {
121
+ search?: string;
122
+ limit?: number;
123
+ }
124
+ interface IntegrationStatus {
125
+ github: boolean;
126
+ vercel: boolean;
127
+ }
128
+ interface VercelClient {
129
+ /** Check integration connection status for the current org. */
130
+ integrationStatus(): Promise<IntegrationStatus>;
131
+ /** Disconnect the org's Vercel integration. Admin only. */
132
+ disconnect(): Promise<void>;
133
+ /** List Vercel projects for the org. */
134
+ listProjects(params?: VercelListProjectsParams): Promise<VercelProject[]>;
135
+ }
136
+
137
+ interface GitHubRepo {
138
+ id: number;
139
+ fullName: string;
140
+ name: string;
141
+ owner: string;
142
+ private: boolean;
143
+ defaultBranch: string;
144
+ url: string;
145
+ }
146
+ interface GitHubListReposParams {
147
+ page?: number;
148
+ perPage?: number;
149
+ }
150
+ interface GitHubClient {
151
+ /** List GitHub repos accessible to the org's connected GitHub account. */
152
+ listRepos(params?: GitHubListReposParams): Promise<GitHubRepo[]>;
153
+ }
154
+
155
+ interface StoredCredentials {
156
+ token: string;
157
+ baseUrl: string;
158
+ expiresAt?: string;
159
+ }
160
+ declare function loadCredentials(): Promise<StoredCredentials | null>;
161
+ declare function saveCredentials(creds: StoredCredentials): Promise<void>;
162
+ declare function clearCredentials(): Promise<void>;
163
+
164
+ interface ViagenConfig {
165
+ baseUrl: string;
166
+ /** API token for CLI/server-side usage. When set, uses Bearer auth instead of cookies. */
167
+ token?: string;
168
+ }
169
+ interface ViagenClient {
170
+ auth: AuthClient;
171
+ orgs: OrgsClient;
172
+ projects: ProjectsClient;
173
+ vercel: VercelClient;
174
+ github: GitHubClient;
175
+ }
176
+ declare class ViagenApiError extends Error {
177
+ status: number;
178
+ detail?: string | undefined;
179
+ constructor(status: number, message: string, detail?: string | undefined);
180
+ }
181
+ type RequestFn = <T>(path: string, options?: RequestInit) => Promise<T>;
182
+ declare function createViagen(config: ViagenConfig): ViagenClient;
183
+ /**
184
+ * Create a Viagen client from stored CLI credentials.
185
+ * Returns null if no credentials are stored.
186
+ */
187
+ declare function createViagenFromCredentials(overrides?: Partial<ViagenConfig>): Promise<ViagenClient | null>;
188
+
189
+ export { type ApiTokenInfo, type AuthClient, type AuthResult, type CreateProjectInput, type GitHubClient, type GitHubListReposParams, type GitHubRepo, type IntegrationStatus, type Org, type OrgInfo, type OrgMembership, type OrgsClient, type Project, type ProjectsClient, type RequestFn, type StoredCredentials, type UpdateProjectInput, type VercelClient, type VercelListProjectsParams, type VercelProject, ViagenApiError, type ViagenClient, type ViagenConfig, type ViagenUser, clearCredentials, createViagen, createViagenFromCredentials, loadCredentials, saveCredentials };
@@ -0,0 +1,189 @@
1
+ interface Project {
2
+ id: string;
3
+ organizationId: string;
4
+ name: string;
5
+ templateId: string | null;
6
+ vercelProjectId: string | null;
7
+ githubRepo: string | null;
8
+ createdAt: string;
9
+ updatedAt: string;
10
+ }
11
+ interface CreateProjectInput {
12
+ name: string;
13
+ templateId?: string;
14
+ vercelProjectId?: string;
15
+ githubRepo?: string;
16
+ }
17
+ interface UpdateProjectInput {
18
+ name?: string;
19
+ vercelProjectId?: string | null;
20
+ githubRepo?: string | null;
21
+ }
22
+ interface ProjectsClient {
23
+ /** List all projects in the current organization. */
24
+ list(): Promise<Project[]>;
25
+ /** Create a new project. Admin only. */
26
+ create(input: CreateProjectInput): Promise<Project>;
27
+ /** Get a single project by ID. */
28
+ get(id: string): Promise<Project>;
29
+ /** Update a project. Admin only. */
30
+ update(id: string, input: UpdateProjectInput): Promise<Project>;
31
+ /** Delete a project by ID. Admin only. */
32
+ delete(id: string): Promise<void>;
33
+ }
34
+
35
+ interface ViagenUser {
36
+ id: string;
37
+ email: string;
38
+ name: string | null;
39
+ avatarUrl: string | null;
40
+ }
41
+ interface OrgInfo {
42
+ id: string;
43
+ name: string;
44
+ role: string;
45
+ }
46
+ interface AuthResult {
47
+ authenticated: true;
48
+ user: ViagenUser;
49
+ organizations: OrgInfo[];
50
+ }
51
+ interface ApiTokenInfo {
52
+ id: string;
53
+ name: string;
54
+ prefix: string;
55
+ expiresAt: string;
56
+ lastUsedAt: string | null;
57
+ createdAt: string;
58
+ }
59
+ interface AuthClient {
60
+ /** Redirect the browser to a provider login page. (Web only) */
61
+ login(provider?: 'google' | 'github' | 'microsoft'): void;
62
+ /** Get the current authenticated user. Works with both cookie and token auth. */
63
+ me(): Promise<(ViagenUser & {
64
+ organizations: OrgInfo[];
65
+ }) | null>;
66
+ /** Log out the current user. In a browser, reloads the page. */
67
+ logout(): Promise<void>;
68
+ /**
69
+ * CLI login: opens browser to authorize page, starts localhost server, captures token.
70
+ * Only available in Node.js environments.
71
+ */
72
+ loginCli(options?: {
73
+ port?: number;
74
+ onOpenUrl?: (url: string) => void;
75
+ }): Promise<{
76
+ token: string;
77
+ expiresAt: string;
78
+ }>;
79
+ /** List the current user's API tokens. */
80
+ listTokens(): Promise<ApiTokenInfo[]>;
81
+ /** Revoke an API token by its hashed ID. */
82
+ revokeToken(tokenId: string): Promise<void>;
83
+ }
84
+
85
+ interface OrgMembership {
86
+ id: string;
87
+ organizationId: string;
88
+ organizationName: string;
89
+ role: string | undefined;
90
+ status: string;
91
+ }
92
+ interface Org {
93
+ id: string;
94
+ name: string;
95
+ }
96
+ interface OrgsClient {
97
+ /** List the current user's organization memberships. */
98
+ list(): Promise<OrgMembership[]>;
99
+ /** Create a new organization. The current user becomes admin. */
100
+ create(input: {
101
+ name: string;
102
+ }): Promise<Org>;
103
+ /** Add a member by email to the current organization. Admin only. */
104
+ addMember(input: {
105
+ email: string;
106
+ }): Promise<void>;
107
+ }
108
+
109
+ interface VercelProject {
110
+ id: string;
111
+ name: string;
112
+ framework: string | null;
113
+ link?: {
114
+ type: string;
115
+ org: string;
116
+ repo: string;
117
+ productionBranch: string;
118
+ };
119
+ }
120
+ interface VercelListProjectsParams {
121
+ search?: string;
122
+ limit?: number;
123
+ }
124
+ interface IntegrationStatus {
125
+ github: boolean;
126
+ vercel: boolean;
127
+ }
128
+ interface VercelClient {
129
+ /** Check integration connection status for the current org. */
130
+ integrationStatus(): Promise<IntegrationStatus>;
131
+ /** Disconnect the org's Vercel integration. Admin only. */
132
+ disconnect(): Promise<void>;
133
+ /** List Vercel projects for the org. */
134
+ listProjects(params?: VercelListProjectsParams): Promise<VercelProject[]>;
135
+ }
136
+
137
+ interface GitHubRepo {
138
+ id: number;
139
+ fullName: string;
140
+ name: string;
141
+ owner: string;
142
+ private: boolean;
143
+ defaultBranch: string;
144
+ url: string;
145
+ }
146
+ interface GitHubListReposParams {
147
+ page?: number;
148
+ perPage?: number;
149
+ }
150
+ interface GitHubClient {
151
+ /** List GitHub repos accessible to the org's connected GitHub account. */
152
+ listRepos(params?: GitHubListReposParams): Promise<GitHubRepo[]>;
153
+ }
154
+
155
+ interface StoredCredentials {
156
+ token: string;
157
+ baseUrl: string;
158
+ expiresAt?: string;
159
+ }
160
+ declare function loadCredentials(): Promise<StoredCredentials | null>;
161
+ declare function saveCredentials(creds: StoredCredentials): Promise<void>;
162
+ declare function clearCredentials(): Promise<void>;
163
+
164
+ interface ViagenConfig {
165
+ baseUrl: string;
166
+ /** API token for CLI/server-side usage. When set, uses Bearer auth instead of cookies. */
167
+ token?: string;
168
+ }
169
+ interface ViagenClient {
170
+ auth: AuthClient;
171
+ orgs: OrgsClient;
172
+ projects: ProjectsClient;
173
+ vercel: VercelClient;
174
+ github: GitHubClient;
175
+ }
176
+ declare class ViagenApiError extends Error {
177
+ status: number;
178
+ detail?: string | undefined;
179
+ constructor(status: number, message: string, detail?: string | undefined);
180
+ }
181
+ type RequestFn = <T>(path: string, options?: RequestInit) => Promise<T>;
182
+ declare function createViagen(config: ViagenConfig): ViagenClient;
183
+ /**
184
+ * Create a Viagen client from stored CLI credentials.
185
+ * Returns null if no credentials are stored.
186
+ */
187
+ declare function createViagenFromCredentials(overrides?: Partial<ViagenConfig>): Promise<ViagenClient | null>;
188
+
189
+ export { type ApiTokenInfo, type AuthClient, type AuthResult, type CreateProjectInput, type GitHubClient, type GitHubListReposParams, type GitHubRepo, type IntegrationStatus, type Org, type OrgInfo, type OrgMembership, type OrgsClient, type Project, type ProjectsClient, type RequestFn, type StoredCredentials, type UpdateProjectInput, type VercelClient, type VercelListProjectsParams, type VercelProject, ViagenApiError, type ViagenClient, type ViagenConfig, type ViagenUser, clearCredentials, createViagen, createViagenFromCredentials, loadCredentials, saveCredentials };
package/dist/index.js ADDED
@@ -0,0 +1,238 @@
1
+ import {
2
+ clearCredentials,
3
+ loadCredentials,
4
+ saveCredentials
5
+ } from "./chunk-TG7LNDF6.js";
6
+
7
+ // src/auth.ts
8
+ function createAuthClient(baseUrl, request) {
9
+ return {
10
+ login(provider = "github") {
11
+ window.location.href = `${baseUrl}/api/auth/login/${provider}`;
12
+ },
13
+ async me() {
14
+ try {
15
+ const data = await request("/api/auth/me");
16
+ if (!data.authenticated) return null;
17
+ return { ...data.user, organizations: data.organizations };
18
+ } catch {
19
+ return null;
20
+ }
21
+ },
22
+ async logout() {
23
+ await request("/api/auth/logout", { method: "POST" });
24
+ if (typeof window !== "undefined") {
25
+ window.location.reload();
26
+ }
27
+ },
28
+ async loginCli(options = {}) {
29
+ const { port: preferredPort, onOpenUrl } = options;
30
+ const http = await import("http");
31
+ return new Promise((resolve, reject) => {
32
+ const server = http.createServer();
33
+ const timeout = setTimeout(() => {
34
+ server.close();
35
+ reject(new Error("Login timed out"));
36
+ }, 5 * 60 * 1e3);
37
+ server.listen(preferredPort ?? 0, "127.0.0.1", () => {
38
+ const address = server.address();
39
+ const port = address.port;
40
+ const loginUrl = `${baseUrl}/cli/authorize?port=${port}`;
41
+ if (onOpenUrl) {
42
+ onOpenUrl(loginUrl);
43
+ } else {
44
+ import("child_process").then(({ exec }) => {
45
+ const cmd = process.platform === "darwin" ? `open "${loginUrl}"` : process.platform === "win32" ? `start "${loginUrl}"` : `xdg-open "${loginUrl}"`;
46
+ exec(cmd);
47
+ }).catch(() => {
48
+ console.log(`Open this URL in your browser:
49
+ ${loginUrl}`);
50
+ });
51
+ }
52
+ });
53
+ server.on("request", (req, res) => {
54
+ const url = new URL(req.url, `http://127.0.0.1`);
55
+ if (url.pathname === "/callback") {
56
+ const token = url.searchParams.get("token");
57
+ const error = url.searchParams.get("error");
58
+ if (error || !token) {
59
+ res.writeHead(200, { "Content-Type": "text/html" });
60
+ res.end(
61
+ '<html><body style="font-family:system-ui;display:flex;justify-content:center;padding-top:40vh"><div><h2>Login failed</h2><p>You can close this tab.</p></div></body></html>'
62
+ );
63
+ clearTimeout(timeout);
64
+ server.close();
65
+ reject(new Error(error ?? "No token received"));
66
+ return;
67
+ }
68
+ res.writeHead(200, { "Content-Type": "text/html" });
69
+ res.end(
70
+ '<html><body style="font-family:system-ui;display:flex;justify-content:center;padding-top:40vh"><div><h2>Login successful!</h2><p>You can close this tab.</p></div></body></html>'
71
+ );
72
+ clearTimeout(timeout);
73
+ server.close();
74
+ resolve({ token, expiresAt: "" });
75
+ } else {
76
+ res.writeHead(404);
77
+ res.end();
78
+ }
79
+ });
80
+ });
81
+ },
82
+ async listTokens() {
83
+ const data = await request("/api/auth/tokens");
84
+ return data.tokens;
85
+ },
86
+ async revokeToken(tokenId) {
87
+ await request("/api/auth/tokens", {
88
+ method: "DELETE",
89
+ body: JSON.stringify({ tokenId })
90
+ });
91
+ }
92
+ };
93
+ }
94
+
95
+ // src/orgs.ts
96
+ function createOrgsClient(_baseUrl, request) {
97
+ return {
98
+ async list() {
99
+ const data = await request("/api/orgs");
100
+ return data.memberships;
101
+ },
102
+ async create(input) {
103
+ const data = await request("/api/orgs", {
104
+ method: "POST",
105
+ body: JSON.stringify(input)
106
+ });
107
+ return data.organization;
108
+ },
109
+ async addMember(input) {
110
+ await request("/api/orgs/members", {
111
+ method: "POST",
112
+ body: JSON.stringify(input)
113
+ });
114
+ }
115
+ };
116
+ }
117
+
118
+ // src/projects.ts
119
+ function createProjectsClient(_baseUrl, request) {
120
+ return {
121
+ async list() {
122
+ const data = await request("/api/projects");
123
+ return data.projects;
124
+ },
125
+ async create(input) {
126
+ const data = await request("/api/projects", {
127
+ method: "POST",
128
+ body: JSON.stringify(input)
129
+ });
130
+ return data.project;
131
+ },
132
+ async get(id) {
133
+ const data = await request(`/api/projects/${id}`);
134
+ return data.project;
135
+ },
136
+ async update(id, input) {
137
+ const data = await request(`/api/projects/${id}`, {
138
+ method: "PATCH",
139
+ body: JSON.stringify(input)
140
+ });
141
+ return data.project;
142
+ },
143
+ async delete(id) {
144
+ await request(`/api/projects/${id}`, { method: "DELETE" });
145
+ }
146
+ };
147
+ }
148
+
149
+ // src/vercel.ts
150
+ function createVercelClient(_baseUrl, request) {
151
+ return {
152
+ async integrationStatus() {
153
+ return request("/api/integrations/status");
154
+ },
155
+ async disconnect() {
156
+ await request("/api/integrations/vercel", { method: "DELETE" });
157
+ },
158
+ async listProjects(params) {
159
+ const qs = new URLSearchParams();
160
+ if (params?.search) qs.set("search", params.search);
161
+ if (params?.limit) qs.set("limit", String(params.limit));
162
+ const query = qs.toString();
163
+ const data = await request(`/api/vercel/projects${query ? `?${query}` : ""}`);
164
+ return data.projects;
165
+ }
166
+ };
167
+ }
168
+
169
+ // src/github.ts
170
+ function createGitHubClient(_baseUrl, request) {
171
+ return {
172
+ async listRepos(params) {
173
+ const qs = new URLSearchParams();
174
+ if (params?.page) qs.set("page", String(params.page));
175
+ if (params?.perPage) qs.set("per_page", String(params.perPage));
176
+ const query = qs.toString();
177
+ const data = await request(`/api/github/repos${query ? `?${query}` : ""}`);
178
+ return data.repos;
179
+ }
180
+ };
181
+ }
182
+
183
+ // src/index.ts
184
+ var ViagenApiError = class extends Error {
185
+ constructor(status, message, detail) {
186
+ super(message);
187
+ this.status = status;
188
+ this.detail = detail;
189
+ this.name = "ViagenApiError";
190
+ }
191
+ };
192
+ function createViagen(config) {
193
+ const baseUrl = config.baseUrl.replace(/\/+$/, "");
194
+ async function request(path, options) {
195
+ const headers = {
196
+ "Content-Type": "application/json",
197
+ ...options?.headers
198
+ };
199
+ if (config.token) {
200
+ headers["Authorization"] = `Bearer ${config.token}`;
201
+ }
202
+ const res = await fetch(`${baseUrl}${path}`, {
203
+ ...options,
204
+ ...config.token ? {} : { credentials: "include" },
205
+ headers
206
+ });
207
+ if (!res.ok) {
208
+ const body = await res.json().catch(() => ({}));
209
+ throw new ViagenApiError(res.status, body.error ?? "Request failed", body.message);
210
+ }
211
+ return res.json();
212
+ }
213
+ return {
214
+ auth: createAuthClient(baseUrl, request),
215
+ orgs: createOrgsClient(baseUrl, request),
216
+ projects: createProjectsClient(baseUrl, request),
217
+ vercel: createVercelClient(baseUrl, request),
218
+ github: createGitHubClient(baseUrl, request)
219
+ };
220
+ }
221
+ async function createViagenFromCredentials(overrides) {
222
+ const { loadCredentials: loadCredentials2 } = await import("./credentials-44RC7NYI.js");
223
+ const creds = await loadCredentials2();
224
+ if (!creds) return null;
225
+ return createViagen({
226
+ baseUrl: creds.baseUrl,
227
+ token: creds.token,
228
+ ...overrides
229
+ });
230
+ }
231
+ export {
232
+ ViagenApiError,
233
+ clearCredentials,
234
+ createViagen,
235
+ createViagenFromCredentials,
236
+ loadCredentials,
237
+ saveCredentials
238
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "viagen-sdk",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "test": "vitest run"
21
+ },
22
+ "devDependencies": {
23
+ "tsup": "^8.0.0",
24
+ "typescript": "^5.0.0",
25
+ "vitest": "^3.0.0"
26
+ }
27
+ }