agent-relay 1.2.3 → 1.3.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.
Files changed (200) hide show
  1. package/.trajectories/agent-relay-322-324.md +17 -0
  2. package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.json +49 -0
  3. package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.md +31 -0
  4. package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.json +125 -0
  5. package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.md +62 -0
  6. package/.trajectories/completed/2026-01/traj_33iuy72sezbk.json +49 -0
  7. package/.trajectories/completed/2026-01/traj_33iuy72sezbk.md +31 -0
  8. package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.json +77 -0
  9. package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.md +42 -0
  10. package/.trajectories/completed/2026-01/traj_6mieijqyvaag.json +77 -0
  11. package/.trajectories/completed/2026-01/traj_6mieijqyvaag.md +42 -0
  12. package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.json +77 -0
  13. package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.md +42 -0
  14. package/.trajectories/completed/2026-01/traj_94gnp3k30goq.json +66 -0
  15. package/.trajectories/completed/2026-01/traj_94gnp3k30goq.md +36 -0
  16. package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.json +40 -0
  17. package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.md +22 -0
  18. package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.json +121 -0
  19. package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.md +29 -0
  20. package/.trajectories/completed/2026-01/traj_fhx9irlckht6.json +53 -0
  21. package/.trajectories/completed/2026-01/traj_fhx9irlckht6.md +32 -0
  22. package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.json +101 -0
  23. package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.md +52 -0
  24. package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.json +49 -0
  25. package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.md +31 -0
  26. package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.json +65 -0
  27. package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.md +37 -0
  28. package/.trajectories/completed/2026-01/traj_lq450ly148uw.json +49 -0
  29. package/.trajectories/completed/2026-01/traj_lq450ly148uw.md +31 -0
  30. package/.trajectories/completed/2026-01/traj_multi_server_arch.md +101 -0
  31. package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.json +27 -0
  32. package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.md +14 -0
  33. package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.json +53 -0
  34. package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.md +32 -0
  35. package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.json +186 -0
  36. package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.md +86 -0
  37. package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.json +77 -0
  38. package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.md +42 -0
  39. package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.json +89 -0
  40. package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.md +47 -0
  41. package/.trajectories/completed/2026-01/traj_xy9vifpqet80.json +65 -0
  42. package/.trajectories/completed/2026-01/traj_xy9vifpqet80.md +37 -0
  43. package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.json +49 -0
  44. package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.md +31 -0
  45. package/.trajectories/consolidate-settings-panel.md +24 -0
  46. package/.trajectories/gh-cli-user-token.md +26 -0
  47. package/.trajectories/index.json +155 -1
  48. package/deploy/workspace/codex.config.toml +15 -0
  49. package/deploy/workspace/entrypoint.sh +167 -7
  50. package/deploy/workspace/git-credential-relay +17 -2
  51. package/dist/bridge/spawner.d.ts +7 -0
  52. package/dist/bridge/spawner.js +40 -9
  53. package/dist/bridge/types.d.ts +2 -0
  54. package/dist/cli/index.js +210 -168
  55. package/dist/cloud/api/admin.d.ts +8 -0
  56. package/dist/cloud/api/admin.js +212 -0
  57. package/dist/cloud/api/auth.js +8 -0
  58. package/dist/cloud/api/billing.d.ts +0 -10
  59. package/dist/cloud/api/billing.js +248 -58
  60. package/dist/cloud/api/codex-auth-helper.d.ts +10 -4
  61. package/dist/cloud/api/codex-auth-helper.js +215 -8
  62. package/dist/cloud/api/coordinators.js +402 -0
  63. package/dist/cloud/api/daemons.js +15 -11
  64. package/dist/cloud/api/git.js +104 -17
  65. package/dist/cloud/api/github-app.js +42 -8
  66. package/dist/cloud/api/nango-auth.js +297 -16
  67. package/dist/cloud/api/onboarding.js +97 -33
  68. package/dist/cloud/api/providers.js +12 -16
  69. package/dist/cloud/api/repos.js +200 -124
  70. package/dist/cloud/api/test-helpers.js +40 -0
  71. package/dist/cloud/api/usage.js +13 -0
  72. package/dist/cloud/api/webhooks.js +1 -1
  73. package/dist/cloud/api/workspaces.d.ts +18 -0
  74. package/dist/cloud/api/workspaces.js +945 -15
  75. package/dist/cloud/config.d.ts +8 -0
  76. package/dist/cloud/config.js +15 -0
  77. package/dist/cloud/db/drizzle.d.ts +5 -2
  78. package/dist/cloud/db/drizzle.js +27 -20
  79. package/dist/cloud/db/schema.d.ts +19 -51
  80. package/dist/cloud/db/schema.js +5 -4
  81. package/dist/cloud/index.d.ts +0 -1
  82. package/dist/cloud/index.js +0 -1
  83. package/dist/cloud/provisioner/index.d.ts +93 -1
  84. package/dist/cloud/provisioner/index.js +608 -63
  85. package/dist/cloud/server.js +156 -16
  86. package/dist/cloud/services/compute-enforcement.d.ts +57 -0
  87. package/dist/cloud/services/compute-enforcement.js +175 -0
  88. package/dist/cloud/services/index.d.ts +2 -0
  89. package/dist/cloud/services/index.js +4 -0
  90. package/dist/cloud/services/intro-expiration.d.ts +55 -0
  91. package/dist/cloud/services/intro-expiration.js +211 -0
  92. package/dist/cloud/services/nango.d.ts +14 -0
  93. package/dist/cloud/services/nango.js +74 -14
  94. package/dist/cloud/services/ssh-security.d.ts +31 -0
  95. package/dist/cloud/services/ssh-security.js +63 -0
  96. package/dist/continuity/manager.d.ts +5 -0
  97. package/dist/continuity/manager.js +56 -2
  98. package/dist/daemon/api.d.ts +2 -0
  99. package/dist/daemon/api.js +214 -5
  100. package/dist/daemon/cli-auth.d.ts +13 -1
  101. package/dist/daemon/cli-auth.js +166 -47
  102. package/dist/daemon/connection.d.ts +7 -1
  103. package/dist/daemon/connection.js +15 -0
  104. package/dist/daemon/orchestrator.d.ts +2 -0
  105. package/dist/daemon/orchestrator.js +26 -0
  106. package/dist/daemon/repo-manager.d.ts +116 -0
  107. package/dist/daemon/repo-manager.js +384 -0
  108. package/dist/daemon/router.d.ts +60 -1
  109. package/dist/daemon/router.js +281 -20
  110. package/dist/daemon/user-directory.d.ts +111 -0
  111. package/dist/daemon/user-directory.js +233 -0
  112. package/dist/dashboard/out/404.html +1 -1
  113. package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_ssgManifest.js +1 -0
  114. package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +9 -0
  115. package/dist/dashboard/out/_next/static/chunks/766-b54f0853794b78c3.js +1 -0
  116. package/dist/dashboard/out/_next/static/chunks/83-b51836037078006c.js +1 -0
  117. package/dist/dashboard/out/_next/static/chunks/891-6cd50de1224f70bb.js +1 -0
  118. package/dist/dashboard/out/_next/static/chunks/899-bb19a9b3d9b39ea6.js +1 -0
  119. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-8939b0fc700f7eca.js +1 -0
  120. package/dist/dashboard/out/_next/static/chunks/app/app/page-5af1b6b439858aa6.js +1 -0
  121. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-f45ecbc3e06134fc.js +1 -0
  122. package/dist/dashboard/out/_next/static/chunks/app/history/{page-abb9ab2d329f56e9.js → page-8c8bed33beb2bf1c.js} +1 -1
  123. package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +1 -0
  124. package/dist/dashboard/out/_next/static/chunks/app/login/{page-c22d080201cbd9fb.js → page-16f3b49e55b1e0ed.js} +1 -1
  125. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-ac39dc0cc3c26fa7.js +1 -0
  126. package/dist/dashboard/out/_next/static/chunks/app/{page-77e9c65420a06cfb.js → page-4a5938c18a11a654.js} +1 -1
  127. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-982a7000fee44014.js +1 -0
  128. package/dist/dashboard/out/_next/static/chunks/app/providers/page-ac3a6ac433fd6001.js +1 -0
  129. package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-09f9caae98a18c09.js +1 -0
  130. package/dist/dashboard/out/_next/static/chunks/app/signup/{page-68d34f50baa8ab6b.js → page-547dd0ca55ecd0ba.js} +1 -1
  131. package/dist/dashboard/out/_next/static/chunks/{main-ed4e1fb6f29c34cf.js → main-2ee6beb2ae96d210.js} +1 -1
  132. package/dist/dashboard/out/_next/static/chunks/{main-app-6e8e8d3ef4e0192a.js → main-app-5d692157a8eb1fd9.js} +1 -1
  133. package/dist/dashboard/out/_next/static/css/85d2af9c7ac74d62.css +1 -0
  134. package/dist/dashboard/out/_next/static/css/fe4b28883eeff359.css +1 -0
  135. package/dist/dashboard/out/app/onboarding.html +1 -1
  136. package/dist/dashboard/out/app/onboarding.txt +3 -3
  137. package/dist/dashboard/out/app.html +1 -1
  138. package/dist/dashboard/out/app.txt +3 -3
  139. package/dist/dashboard/out/apple-icon.png +0 -0
  140. package/dist/dashboard/out/connect-repos.html +1 -1
  141. package/dist/dashboard/out/connect-repos.txt +3 -3
  142. package/dist/dashboard/out/history.html +1 -1
  143. package/dist/dashboard/out/history.txt +3 -3
  144. package/dist/dashboard/out/index.html +1 -1
  145. package/dist/dashboard/out/index.txt +3 -3
  146. package/dist/dashboard/out/login.html +2 -2
  147. package/dist/dashboard/out/login.txt +3 -3
  148. package/dist/dashboard/out/metrics.html +1 -1
  149. package/dist/dashboard/out/metrics.txt +3 -3
  150. package/dist/dashboard/out/pricing.html +2 -2
  151. package/dist/dashboard/out/pricing.txt +3 -3
  152. package/dist/dashboard/out/providers/setup/claude.html +1 -0
  153. package/dist/dashboard/out/providers/setup/claude.txt +8 -0
  154. package/dist/dashboard/out/providers/setup/codex.html +1 -0
  155. package/dist/dashboard/out/providers/setup/codex.txt +8 -0
  156. package/dist/dashboard/out/providers.html +1 -1
  157. package/dist/dashboard/out/providers.txt +3 -3
  158. package/dist/dashboard/out/signup.html +2 -2
  159. package/dist/dashboard/out/signup.txt +3 -3
  160. package/dist/dashboard-server/server.js +316 -12
  161. package/dist/dashboard-server/user-bridge.d.ts +103 -0
  162. package/dist/dashboard-server/user-bridge.js +189 -0
  163. package/dist/protocol/channels.d.ts +205 -0
  164. package/dist/protocol/channels.js +154 -0
  165. package/dist/protocol/types.d.ts +13 -1
  166. package/dist/resiliency/provider-context.js +2 -0
  167. package/dist/shared/cli-auth-config.d.ts +19 -0
  168. package/dist/shared/cli-auth-config.js +58 -2
  169. package/dist/utils/agent-config.js +1 -1
  170. package/dist/wrapper/auth-detection.d.ts +49 -0
  171. package/dist/wrapper/auth-detection.js +192 -0
  172. package/dist/wrapper/base-wrapper.d.ts +153 -0
  173. package/dist/wrapper/base-wrapper.js +393 -0
  174. package/dist/wrapper/client.d.ts +7 -1
  175. package/dist/wrapper/client.js +3 -0
  176. package/dist/wrapper/index.d.ts +1 -0
  177. package/dist/wrapper/index.js +4 -3
  178. package/dist/wrapper/pty-wrapper.d.ts +62 -84
  179. package/dist/wrapper/pty-wrapper.js +154 -180
  180. package/dist/wrapper/tmux-wrapper.d.ts +41 -66
  181. package/dist/wrapper/tmux-wrapper.js +90 -134
  182. package/package.json +4 -2
  183. package/scripts/postinstall.js +11 -155
  184. package/scripts/test-interactive-terminal.sh +248 -0
  185. package/dist/cloud/vault/index.d.ts +0 -76
  186. package/dist/cloud/vault/index.js +0 -219
  187. package/dist/dashboard/out/_next/static/chunks/699-3b1cd6618a45d259.js +0 -1
  188. package/dist/dashboard/out/_next/static/chunks/724-2dae7627550ab88f.js +0 -9
  189. package/dist/dashboard/out/_next/static/chunks/766-1f2dd8cb7f766b0b.js +0 -1
  190. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-3fdfa60e53f2810d.js +0 -1
  191. package/dist/dashboard/out/_next/static/chunks/app/app/page-e6381e5a6e1fbcfd.js +0 -1
  192. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-3538dfe0ffe984b8.js +0 -1
  193. package/dist/dashboard/out/_next/static/chunks/app/layout-c0d118c0f92d969c.js +0 -1
  194. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-67a3e98d9a43a6ed.js +0 -1
  195. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-b08ed1c34d14434a.js +0 -1
  196. package/dist/dashboard/out/_next/static/chunks/app/providers/page-e88bc117ef7671c3.js +0 -1
  197. package/dist/dashboard/out/_next/static/css/29852f26181969a0.css +0 -1
  198. package/dist/dashboard/out/_next/static/css/7c3ae9e8617d42a5.css +0 -1
  199. package/dist/dashboard/out/_next/static/wPgKJtcOmTFLpUncDg16A/_ssgManifest.js +0 -1
  200. /package/dist/dashboard/out/_next/static/{wPgKJtcOmTFLpUncDg16A → T1tgCqVWHFIkV7ClEtzD7}/_buildManifest.js +0 -0
@@ -4,6 +4,7 @@
4
4
  export interface CloudConfig {
5
5
  port: number;
6
6
  publicUrl: string;
7
+ appUrl: string;
7
8
  sessionSecret: string;
8
9
  databaseUrl: string;
9
10
  redisUrl: string;
@@ -38,6 +39,8 @@ export interface CloudConfig {
38
39
  username: string;
39
40
  password: string;
40
41
  };
42
+ snapshotRetentionDays?: number;
43
+ volumeSizeGb?: number;
41
44
  };
42
45
  railway?: {
43
46
  apiToken: string;
@@ -60,7 +63,12 @@ export interface CloudConfig {
60
63
  enterpriseYearly?: string;
61
64
  };
62
65
  };
66
+ adminUsers: string[];
63
67
  }
64
68
  export declare function loadConfig(): CloudConfig;
69
+ /**
70
+ * Check if a GitHub username is an admin user
71
+ */
72
+ export declare function isAdminUser(githubUsername: string): boolean;
65
73
  export declare function getConfig(): CloudConfig;
66
74
  //# sourceMappingURL=config.d.ts.map
@@ -15,6 +15,7 @@ export function loadConfig() {
15
15
  return {
16
16
  port: parseInt(process.env.PORT || '4567', 10),
17
17
  publicUrl: process.env.PUBLIC_URL || 'http://localhost:4567',
18
+ appUrl: process.env.APP_URL || process.env.PUBLIC_URL || 'http://localhost:4567',
18
19
  sessionSecret: requireEnv('SESSION_SECRET'),
19
20
  databaseUrl: requireEnv('DATABASE_URL'),
20
21
  redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',
@@ -54,6 +55,8 @@ export function loadConfig() {
54
55
  password: optionalEnv('GHCR_TOKEN'),
55
56
  }
56
57
  : undefined,
58
+ snapshotRetentionDays: parseInt(optionalEnv('FLY_SNAPSHOT_RETENTION_DAYS') || '14', 10),
59
+ volumeSizeGb: parseInt(optionalEnv('FLY_VOLUME_SIZE_GB') || '10', 10),
57
60
  }
58
61
  : undefined,
59
62
  railway: optionalEnv('RAILWAY_API_TOKEN')
@@ -79,8 +82,20 @@ export function loadConfig() {
79
82
  enterpriseYearly: optionalEnv('STRIPE_ENTERPRISE_YEARLY_PRICE_ID'),
80
83
  },
81
84
  },
85
+ // Admin users - comma-separated GitHub usernames (e.g., "khaliqgant,admin2")
86
+ adminUsers: (optionalEnv('ADMIN_USERS') || '')
87
+ .split(',')
88
+ .map((u) => u.trim().toLowerCase())
89
+ .filter(Boolean),
82
90
  };
83
91
  }
92
+ /**
93
+ * Check if a GitHub username is an admin user
94
+ */
95
+ export function isAdminUser(githubUsername) {
96
+ const config = getConfig();
97
+ return config.adminUsers.includes(githubUsername.toLowerCase());
98
+ }
84
99
  // Singleton config instance
85
100
  let _config = null;
86
101
  export function getConfig() {
@@ -18,6 +18,7 @@ export interface UserQueries {
18
18
  findByEmail(email: string): Promise<schema.User | null>;
19
19
  findByNangoConnectionId(connectionId: string): Promise<schema.User | null>;
20
20
  findByIncomingConnectionId(connectionId: string): Promise<schema.User | null>;
21
+ findByPlan(plan: string): Promise<schema.User[]>;
21
22
  upsert(data: schema.NewUser): Promise<schema.User>;
22
23
  update(id: string, data: Partial<Omit<schema.User, 'id' | 'createdAt'>>): Promise<void>;
23
24
  completeOnboarding(userId: string): Promise<void>;
@@ -43,7 +44,6 @@ export interface CredentialQueries {
43
44
  findByUserId(userId: string): Promise<schema.Credential[]>;
44
45
  findByUserAndProvider(userId: string, provider: string): Promise<schema.Credential | null>;
45
46
  upsert(data: schema.NewCredential): Promise<schema.Credential>;
46
- updateTokens(userId: string, provider: string, accessToken: string, refreshToken?: string, expiresAt?: Date): Promise<void>;
47
47
  delete(userId: string, provider: string): Promise<void>;
48
48
  }
49
49
  export declare const credentialQueries: CredentialQueries;
@@ -51,7 +51,9 @@ export interface WorkspaceQueries {
51
51
  findById(id: string): Promise<schema.Workspace | null>;
52
52
  findByUserId(userId: string): Promise<schema.Workspace[]>;
53
53
  findByCustomDomain(domain: string): Promise<schema.Workspace | null>;
54
+ findAll(): Promise<schema.Workspace[]>;
54
55
  create(data: schema.NewWorkspace): Promise<schema.Workspace>;
56
+ update(id: string, data: Partial<Pick<schema.Workspace, 'name' | 'config'>>): Promise<void>;
55
57
  updateStatus(id: string, status: string, options?: {
56
58
  computeId?: string;
57
59
  publicUrl?: string;
@@ -137,11 +139,12 @@ export declare const projectGroupQueries: ProjectGroupQueries;
137
139
  export interface RepositoryQueries {
138
140
  findById(id: string): Promise<schema.Repository | null>;
139
141
  findByFullName(fullName: string): Promise<schema.Repository | null>;
142
+ findByGithubFullName(fullName: string): Promise<schema.Repository[]>;
140
143
  findByUserId(userId: string): Promise<schema.Repository[]>;
141
144
  findByWorkspaceId(workspaceId: string): Promise<schema.Repository[]>;
142
145
  findByProjectGroupId(projectGroupId: string): Promise<schema.Repository[]>;
143
146
  upsert(data: schema.NewRepository): Promise<schema.Repository>;
144
- assignToWorkspace(repoId: string, workspaceId: string): Promise<void>;
147
+ assignToWorkspace(repoId: string, workspaceId: string | null): Promise<void>;
145
148
  assignToGroup(repoId: string, projectGroupId: string | null): Promise<void>;
146
149
  updateProjectAgent(id: string, config: schema.ProjectAgentConfig): Promise<void>;
147
150
  updateSyncStatus(id: string, status: string, lastSyncedAt?: Date): Promise<void>;
@@ -61,6 +61,11 @@ export const userQueries = {
61
61
  const result = await db.select().from(schema.users).where(eq(schema.users.incomingConnectionId, connectionId));
62
62
  return result[0] ?? null;
63
63
  },
64
+ async findByPlan(plan) {
65
+ const db = getDb();
66
+ const result = await db.select().from(schema.users).where(eq(schema.users.plan, plan));
67
+ return result;
68
+ },
64
69
  async upsert(data) {
65
70
  const db = getDb();
66
71
  const result = await db
@@ -214,9 +219,6 @@ export const credentialQueries = {
214
219
  .onConflictDoUpdate({
215
220
  target: [schema.credentials.userId, schema.credentials.provider],
216
221
  set: {
217
- accessToken: data.accessToken,
218
- refreshToken: data.refreshToken ?? sql `credentials.refresh_token`,
219
- tokenExpiresAt: data.tokenExpiresAt,
220
222
  scopes: data.scopes,
221
223
  providerAccountId: data.providerAccountId,
222
224
  providerAccountEmail: data.providerAccountEmail,
@@ -226,23 +228,6 @@ export const credentialQueries = {
226
228
  .returning();
227
229
  return result[0];
228
230
  },
229
- async updateTokens(userId, provider, accessToken, refreshToken, expiresAt) {
230
- const db = getDb();
231
- const updates = {
232
- accessToken,
233
- updatedAt: new Date(),
234
- };
235
- if (refreshToken !== undefined) {
236
- updates.refreshToken = refreshToken;
237
- }
238
- if (expiresAt !== undefined) {
239
- updates.tokenExpiresAt = expiresAt;
240
- }
241
- await db
242
- .update(schema.credentials)
243
- .set(updates)
244
- .where(and(eq(schema.credentials.userId, userId), eq(schema.credentials.provider, provider)));
245
- },
246
231
  async delete(userId, provider) {
247
232
  const db = getDb();
248
233
  await db
@@ -272,11 +257,25 @@ export const workspaceQueries = {
272
257
  .where(eq(schema.workspaces.customDomain, domain));
273
258
  return result[0] ?? null;
274
259
  },
260
+ async findAll() {
261
+ const db = getDb();
262
+ return db
263
+ .select()
264
+ .from(schema.workspaces)
265
+ .orderBy(desc(schema.workspaces.createdAt));
266
+ },
275
267
  async create(data) {
276
268
  const db = getDb();
277
269
  const result = await db.insert(schema.workspaces).values(data).returning();
278
270
  return result[0];
279
271
  },
272
+ async update(id, data) {
273
+ const db = getDb();
274
+ await db
275
+ .update(schema.workspaces)
276
+ .set({ ...data, updatedAt: new Date() })
277
+ .where(eq(schema.workspaces.id, id));
278
+ },
280
279
  async updateStatus(id, status, options) {
281
280
  const db = getDb();
282
281
  await db
@@ -652,6 +651,14 @@ export const repositoryQueries = {
652
651
  .where(eq(schema.repositories.githubFullName, fullName));
653
652
  return result[0] ?? null;
654
653
  },
654
+ async findByGithubFullName(fullName) {
655
+ const db = getDb();
656
+ // Use case-insensitive match since GitHub repo names are case-insensitive
657
+ return db
658
+ .select()
659
+ .from(schema.repositories)
660
+ .where(sql `LOWER(${schema.repositories.githubFullName}) = LOWER(${fullName})`);
661
+ },
655
662
  async findByUserId(userId) {
656
663
  const db = getDb();
657
664
  return db
@@ -121,6 +121,25 @@ export declare const users: import("drizzle-orm/pg-core").PgTableWithColumns<{
121
121
  }, {}, {
122
122
  length: 50;
123
123
  }>;
124
+ stripeCustomerId: import("drizzle-orm/pg-core").PgColumn<{
125
+ name: "stripe_customer_id";
126
+ tableName: "users";
127
+ dataType: "string";
128
+ columnType: "PgVarchar";
129
+ data: string;
130
+ driverParam: string;
131
+ notNull: false;
132
+ hasDefault: false;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: [string, ...string[]];
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {
141
+ length: 255;
142
+ }>;
124
143
  nangoConnectionId: import("drizzle-orm/pg-core").PgColumn<{
125
144
  name: "nango_connection_id";
126
145
  tableName: "users";
@@ -565,57 +584,6 @@ export declare const credentials: import("drizzle-orm/pg-core").PgTableWithColum
565
584
  }, {}, {
566
585
  length: 50;
567
586
  }>;
568
- accessToken: import("drizzle-orm/pg-core").PgColumn<{
569
- name: "access_token";
570
- tableName: "credentials";
571
- dataType: "string";
572
- columnType: "PgText";
573
- data: string;
574
- driverParam: string;
575
- notNull: true;
576
- hasDefault: false;
577
- isPrimaryKey: false;
578
- isAutoincrement: false;
579
- hasRuntimeDefault: false;
580
- enumValues: [string, ...string[]];
581
- baseColumn: never;
582
- identity: undefined;
583
- generated: undefined;
584
- }, {}, {}>;
585
- refreshToken: import("drizzle-orm/pg-core").PgColumn<{
586
- name: "refresh_token";
587
- tableName: "credentials";
588
- dataType: "string";
589
- columnType: "PgText";
590
- data: string;
591
- driverParam: string;
592
- notNull: false;
593
- hasDefault: false;
594
- isPrimaryKey: false;
595
- isAutoincrement: false;
596
- hasRuntimeDefault: false;
597
- enumValues: [string, ...string[]];
598
- baseColumn: never;
599
- identity: undefined;
600
- generated: undefined;
601
- }, {}, {}>;
602
- tokenExpiresAt: import("drizzle-orm/pg-core").PgColumn<{
603
- name: "token_expires_at";
604
- tableName: "credentials";
605
- dataType: "date";
606
- columnType: "PgTimestamp";
607
- data: Date;
608
- driverParam: string;
609
- notNull: false;
610
- hasDefault: false;
611
- isPrimaryKey: false;
612
- isAutoincrement: false;
613
- hasRuntimeDefault: false;
614
- enumValues: undefined;
615
- baseColumn: never;
616
- identity: undefined;
617
- generated: undefined;
618
- }, {}, {}>;
619
587
  scopes: import("drizzle-orm/pg-core").PgColumn<{
620
588
  name: "scopes";
621
589
  tableName: "credentials";
@@ -17,6 +17,8 @@ export const users = pgTable('users', {
17
17
  email: varchar('email', { length: 255 }),
18
18
  avatarUrl: varchar('avatar_url', { length: 512 }),
19
19
  plan: varchar('plan', { length: 50 }).notNull().default('free'),
20
+ // Stripe billing
21
+ stripeCustomerId: varchar('stripe_customer_id', { length: 255 }),
20
22
  // Nango OAuth connections
21
23
  nangoConnectionId: varchar('nango_connection_id', { length: 255 }), // Permanent login connection
22
24
  incomingConnectionId: varchar('incoming_connection_id', { length: 255 }), // Temp polling connection
@@ -68,15 +70,14 @@ export const githubInstallationsRelations = relations(githubInstallations, ({ on
68
70
  repositories: many(repositories),
69
71
  }));
70
72
  // ============================================================================
71
- // Credentials (provider tokens)
73
+ // Credentials (connected provider registry - no token storage)
74
+ // Note: Tokens are not stored centrally. CLI tools authenticate directly
75
+ // on workspace instances. This table tracks which providers a user has connected.
72
76
  // ============================================================================
73
77
  export const credentials = pgTable('credentials', {
74
78
  id: uuid('id').primaryKey().defaultRandom(),
75
79
  userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
76
80
  provider: varchar('provider', { length: 50 }).notNull(),
77
- accessToken: text('access_token').notNull(),
78
- refreshToken: text('refresh_token'),
79
- tokenExpiresAt: timestamp('token_expires_at'),
80
81
  scopes: text('scopes').array(),
81
82
  providerAccountId: varchar('provider_account_id', { length: 255 }),
82
83
  providerAccountEmail: varchar('provider_account_email', { length: 255 }),
@@ -5,7 +5,6 @@
5
5
  */
6
6
  export { createServer } from './server.js';
7
7
  export { getConfig, loadConfig, CloudConfig } from './config.js';
8
- export { CredentialVault } from './vault/index.js';
9
8
  export { WorkspaceProvisioner, ProvisionConfig, Workspace, WorkspaceStatus } from './provisioner/index.js';
10
9
  export { ScalingPolicyService, ScalingThresholds, ScalingPolicy, ScalingDecision, WorkspaceMetrics, getScalingPolicyService, AutoScaler, ScalingOperation, getAutoScaler, createAutoScaler, CapacityManager, WorkspaceCapacity, PlacementRecommendation, CapacityForecast, getCapacityManager, createCapacityManager, ScalingOrchestrator, ScalingEvent, getScalingOrchestrator, createScalingOrchestrator, } from './services/index.js';
11
10
  export * from './billing/index.js';
@@ -7,7 +7,6 @@ import { fileURLToPath } from 'node:url';
7
7
  export { createServer } from './server.js';
8
8
  export { getConfig, loadConfig } from './config.js';
9
9
  // Services
10
- export { CredentialVault } from './vault/index.js';
11
10
  export { WorkspaceProvisioner } from './provisioner/index.js';
12
11
  // Scaling infrastructure
13
12
  export { ScalingPolicyService, getScalingPolicyService, AutoScaler, getAutoScaler, createAutoScaler, CapacityManager, getCapacityManager, createCapacityManager, ScalingOrchestrator, getScalingOrchestrator, createScalingOrchestrator, } from './services/index.js';
@@ -76,8 +76,9 @@ export declare class WorkspaceProvisioner {
76
76
  stop(workspaceId: string): Promise<void>;
77
77
  /**
78
78
  * Resize a workspace (vertical scaling)
79
+ * @param skipRestart - If true, config is saved but machine won't restart (changes apply on next start)
79
80
  */
80
- resize(workspaceId: string, tier: ResourceTier): Promise<void>;
81
+ resize(workspaceId: string, tier: ResourceTier, skipRestart?: boolean): Promise<void>;
81
82
  /**
82
83
  * Update the max agent limit for a workspace
83
84
  */
@@ -102,6 +103,97 @@ export declare class WorkspaceProvisioner {
102
103
  currentTier?: string;
103
104
  targetTier?: string;
104
105
  }>;
106
+ /**
107
+ * Create an on-demand snapshot of a workspace's volume
108
+ * Use before risky operations (e.g., major refactors, untrusted code execution)
109
+ */
110
+ createSnapshot(workspaceId: string): Promise<{
111
+ snapshotId: string;
112
+ } | null>;
113
+ /**
114
+ * List available snapshots for a workspace
115
+ * Includes both automatic daily snapshots and on-demand snapshots
116
+ */
117
+ listSnapshots(workspaceId: string): Promise<Array<{
118
+ id: string;
119
+ createdAt: string;
120
+ sizeBytes: number;
121
+ }>>;
122
+ /**
123
+ * Get the volume ID for a workspace (needed for restore operations)
124
+ */
125
+ getVolumeId(workspaceId: string): Promise<string | null>;
126
+ /**
127
+ * Result of a graceful update attempt
128
+ */
129
+ static readonly UpdateResult: {
130
+ readonly UPDATED: "updated";
131
+ readonly UPDATED_PENDING_RESTART: "updated_pending_restart";
132
+ readonly SKIPPED_ACTIVE_AGENTS: "skipped_active_agents";
133
+ readonly SKIPPED_NOT_RUNNING: "skipped_not_running";
134
+ readonly NOT_SUPPORTED: "not_supported";
135
+ readonly ERROR: "error";
136
+ };
137
+ /**
138
+ * Gracefully update a single workspace's image
139
+ *
140
+ * Behavior:
141
+ * - If workspace is stopped: Update config, will use new image on next wake
142
+ * - If workspace is running with no agents: Update config and restart
143
+ * - If workspace is running with active agents: Skip (or force if specified)
144
+ *
145
+ * @param workspaceId - Workspace to update
146
+ * @param newImage - New Docker image to use
147
+ * @param options - Update options
148
+ * @returns Update result with details
149
+ */
150
+ gracefulUpdateImage(workspaceId: string, newImage: string, options?: {
151
+ force?: boolean;
152
+ skipRestart?: boolean;
153
+ }): Promise<{
154
+ result: string;
155
+ workspaceId: string;
156
+ machineState?: string;
157
+ agentCount?: number;
158
+ agents?: Array<{
159
+ name: string;
160
+ status: string;
161
+ }>;
162
+ error?: string;
163
+ }>;
164
+ /**
165
+ * Gracefully update all workspaces to a new image
166
+ *
167
+ * Processes workspaces in batches, respecting active agents unless forced.
168
+ * Returns detailed results for each workspace.
169
+ *
170
+ * @param newImage - New Docker image to use
171
+ * @param options - Update options
172
+ * @returns Summary and per-workspace results
173
+ */
174
+ gracefulUpdateAllImages(newImage: string, options?: {
175
+ force?: boolean;
176
+ skipRestart?: boolean;
177
+ batchSize?: number;
178
+ userIds?: string[];
179
+ workspaceIds?: string[];
180
+ }): Promise<{
181
+ summary: {
182
+ total: number;
183
+ updated: number;
184
+ pendingRestart: number;
185
+ skippedActiveAgents: number;
186
+ skippedNotRunning: number;
187
+ errors: number;
188
+ };
189
+ results: Array<{
190
+ result: string;
191
+ workspaceId: string;
192
+ machineState?: string;
193
+ agentCount?: number;
194
+ error?: string;
195
+ }>;
196
+ }>;
105
197
  }
106
198
  export declare function getProvisioner(): WorkspaceProvisioner;
107
199
  //# sourceMappingURL=index.d.ts.map