t44 0.2.0-rc.1

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.

Potentially problematic release.


This version of t44 might be problematic. Click here for more details.

Files changed (86) hide show
  1. package/LICENSE.md +203 -0
  2. package/README.md +154 -0
  3. package/bin/activate +36 -0
  4. package/bin/activate.ts +30 -0
  5. package/bin/postinstall.sh +19 -0
  6. package/bin/shell +27 -0
  7. package/bin/t44 +27 -0
  8. package/caps/HomeRegistry.v0.ts +298 -0
  9. package/caps/OpenApiSchema.v0.ts +192 -0
  10. package/caps/ProjectDeployment.v0.ts +363 -0
  11. package/caps/ProjectDevelopment.v0.ts +246 -0
  12. package/caps/ProjectPublishing.v0.ts +307 -0
  13. package/caps/ProjectRack.v0.ts +128 -0
  14. package/caps/WorkspaceCli.v0.ts +391 -0
  15. package/caps/WorkspaceConfig.v0.ts +626 -0
  16. package/caps/WorkspaceConfig.yaml +53 -0
  17. package/caps/WorkspaceConnection.v0.ts +240 -0
  18. package/caps/WorkspaceEntityConfig.v0.ts +64 -0
  19. package/caps/WorkspaceEntityFact.v0.ts +193 -0
  20. package/caps/WorkspaceInfo.v0.ts +554 -0
  21. package/caps/WorkspaceInit.v0.ts +30 -0
  22. package/caps/WorkspaceKey.v0.ts +186 -0
  23. package/caps/WorkspaceProjects.v0.ts +455 -0
  24. package/caps/WorkspacePrompt.v0.ts +396 -0
  25. package/caps/WorkspaceShell.sh +39 -0
  26. package/caps/WorkspaceShell.v0.ts +104 -0
  27. package/caps/WorkspaceShell.yaml +65 -0
  28. package/caps/WorkspaceShellCli.v0.ts +109 -0
  29. package/caps/WorkspaceTest.v0.ts +167 -0
  30. package/caps/providers/LICENSE.md +8 -0
  31. package/caps/providers/README.md +2 -0
  32. package/caps/providers/bunny.net/ProjectDeployment.v0.ts +328 -0
  33. package/caps/providers/bunny.net/api-pull.v0.test.ts +319 -0
  34. package/caps/providers/bunny.net/api-pull.v0.ts +161 -0
  35. package/caps/providers/bunny.net/api-storage.v0.test.ts +168 -0
  36. package/caps/providers/bunny.net/api-storage.v0.ts +245 -0
  37. package/caps/providers/bunny.net/api.v0.ts +95 -0
  38. package/caps/providers/dynadot.com/ProjectDeployment.v0.ts +207 -0
  39. package/caps/providers/dynadot.com/api-domains.v0.test.ts +147 -0
  40. package/caps/providers/dynadot.com/api-domains.v0.ts +137 -0
  41. package/caps/providers/dynadot.com/api.v0.ts +88 -0
  42. package/caps/providers/git-scm.com/ProjectPublishing.v0.ts +231 -0
  43. package/caps/providers/github.com/ProjectPublishing.v0.ts +75 -0
  44. package/caps/providers/github.com/api.v0.ts +90 -0
  45. package/caps/providers/npmjs.com/ProjectPublishing.v0.ts +741 -0
  46. package/caps/providers/vercel.com/ProjectDeployment.v0.ts +339 -0
  47. package/caps/providers/vercel.com/api.v0.test.ts +67 -0
  48. package/caps/providers/vercel.com/api.v0.ts +132 -0
  49. package/caps/providers/vercel.com/bun.lock +194 -0
  50. package/caps/providers/vercel.com/package.json +10 -0
  51. package/caps/providers/vercel.com/project.v0.test.ts +108 -0
  52. package/caps/providers/vercel.com/project.v0.ts +150 -0
  53. package/caps/providers/vercel.com/tsconfig.json +28 -0
  54. package/docs/Overview.drawio +189 -0
  55. package/docs/Overview.svg +4 -0
  56. package/lib/crypto.ts +53 -0
  57. package/lib/openapi.ts +132 -0
  58. package/lib/ucan.ts +137 -0
  59. package/package.json +41 -0
  60. package/structs/HomeRegistryConfig.v0.ts +27 -0
  61. package/structs/ProjectDeploymentConfig.v0.ts +27 -0
  62. package/structs/ProjectDeploymentFact.v0.ts +110 -0
  63. package/structs/ProjectPublishingFact.v0.ts +69 -0
  64. package/structs/ProjectRackConfig.v0.ts +27 -0
  65. package/structs/WorkspaceCliConfig.v0.ts +27 -0
  66. package/structs/WorkspaceConfig.v0.ts +27 -0
  67. package/structs/WorkspaceKeyConfig.v0.ts +27 -0
  68. package/structs/WorkspaceMappings.v0.ts +27 -0
  69. package/structs/WorkspaceProjectsConfig.v0.ts +27 -0
  70. package/structs/WorkspaceRepositories.v0.ts +27 -0
  71. package/structs/WorkspaceShellConfig.v0.ts +45 -0
  72. package/structs/providers/LICENSE.md +8 -0
  73. package/structs/providers/README.md +2 -0
  74. package/structs/providers/bunny.net/ProjectDeploymentFact.v0.ts +41 -0
  75. package/structs/providers/bunny.net/WorkspaceConnectionConfig.v0.ts +42 -0
  76. package/structs/providers/dynadot.com/DomainFact.v0.ts +146 -0
  77. package/structs/providers/dynadot.com/WorkspaceConnectionConfig.v0.ts +41 -0
  78. package/structs/providers/git-scm.com/ProjectPublishingFact.v0.ts +46 -0
  79. package/structs/providers/github.com/ProjectPublishingFact.v0.ts +52 -0
  80. package/structs/providers/github.com/WorkspaceConnectionConfig.v0.ts +42 -0
  81. package/structs/providers/npmjs.com/ProjectPublishingFact.v0.ts +48 -0
  82. package/structs/providers/vercel.com/ProjectDeploymentFact.v0.ts +38 -0
  83. package/structs/providers/vercel.com/WorkspaceConnectionConfig.v0.ts +48 -0
  84. package/tsconfig.json +28 -0
  85. package/workspace-rt.ts +134 -0
  86. package/workspace.yaml +5 -0
@@ -0,0 +1,339 @@
1
+
2
+ import { join } from 'path'
3
+ import { $ } from 'bun'
4
+ import { mkdir, writeFile } from 'fs/promises'
5
+
6
+ export async function capsule({
7
+ encapsulate,
8
+ CapsulePropertyTypes,
9
+ makeImportStack
10
+ }: {
11
+ encapsulate: any
12
+ CapsulePropertyTypes: any
13
+ makeImportStack: any
14
+ }) {
15
+ // High level API that deals with everything concerning deployment of projects.
16
+ // NOTE: The API signatures do NOT match the vercel SDK and this is on purpose.
17
+ // The goal is to move towards a standard 'deployment' API that can be used across providers.
18
+ return encapsulate({
19
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
20
+ '#@stream44.studio/encapsulate/structs/Capsule.v0': {},
21
+ '#t44/structs/providers/vercel.com/ProjectDeploymentFact.v0': {
22
+ as: '$ProjectDeploymentFact'
23
+ },
24
+ '#t44/structs/ProjectDeploymentFact.v0': {
25
+ as: '$StatusFact'
26
+ },
27
+ '#': {
28
+ project: {
29
+ type: CapsulePropertyTypes.Mapping,
30
+ value: './project.v0'
31
+ },
32
+ deploy: {
33
+ type: CapsulePropertyTypes.Function,
34
+ value: async function (this: any, { projectionDir, alias, config }: { projectionDir: string, alias: string, config: any }) {
35
+
36
+ const projectName = config.provider.config.ProjectSettings.name
37
+ const projectSettings = {
38
+ ...config.provider.config.ProjectSettings || {},
39
+ name: undefined
40
+ }
41
+
42
+ console.log(`Ensure project '${projectName}' is created on Vercel ...`)
43
+
44
+ const details = await this.project.ensureCreated({
45
+ name: projectName,
46
+ settings: projectSettings
47
+ })
48
+
49
+ console.log(`Project ID: ${details.id}`)
50
+
51
+ // Set environment variables if configured
52
+ if (config.provider.config.ENV) {
53
+ console.log(`Managing environment variables ...`)
54
+
55
+ // Get existing environment variables
56
+ const team = await this.project.vercel.getDefaultTeam()
57
+ const existingVars = await (await this.project.vercel.vercel).projects.filterProjectEnvs({
58
+ idOrName: details.id,
59
+ slug: team
60
+ })
61
+
62
+ const existingVarMap = new Map<string, string>(
63
+ existingVars.envs?.map((env: any) => [env.key as string, env.id as string]) || []
64
+ )
65
+
66
+ const configuredKeys = new Set(Object.keys(config.provider.config.ENV))
67
+
68
+ // Delete variables that are no longer defined
69
+ for (const [key, envId] of existingVarMap.entries()) {
70
+ if (!configuredKeys.has(key)) {
71
+ await (await this.project.vercel.vercel).projects.removeProjectEnv({
72
+ idOrName: details.id,
73
+ slug: team,
74
+ id: envId
75
+ })
76
+ console.log(`Deleted environment variable: ${key}`)
77
+ }
78
+ }
79
+
80
+ // Create or update environment variables
81
+ for (const [key, value] of Object.entries(config.provider.config.ENV)) {
82
+
83
+ // If value is a function (jit expression), execute it
84
+ const resolvedValue = typeof value === 'function'
85
+ ? await value()
86
+ : value as string
87
+
88
+ if (!existingVarMap.has(key)) {
89
+ // Create new variable
90
+ await (await this.project.vercel.vercel).projects.createProjectEnv({
91
+ idOrName: details.id,
92
+ slug: team,
93
+ requestBody: {
94
+ key,
95
+ value: resolvedValue,
96
+ type: 'encrypted',
97
+ target: ['production', 'preview', 'development']
98
+ }
99
+ })
100
+ console.log(`Created environment variable: ${key}`, resolvedValue)
101
+ } else {
102
+ // Update existing variable
103
+ const envId = existingVarMap.get(key)!
104
+ await (await this.project.vercel.vercel).projects.editProjectEnv({
105
+ idOrName: details.id,
106
+ slug: team,
107
+ id: envId,
108
+ requestBody: {
109
+ value: resolvedValue,
110
+ type: 'encrypted',
111
+ target: ['production', 'preview', 'development']
112
+ }
113
+ })
114
+ console.log(`Updated environment variable: ${key}`)
115
+ }
116
+ }
117
+
118
+ console.log(`Environment variables configured.`)
119
+ }
120
+
121
+ const projectSourceDir = join(config.sourceDir)
122
+ const projectProjectionDir = join(projectionDir, 'projects', projectName)
123
+
124
+ await $`rm -Rf "${projectProjectionDir}" && mkdir -p "${projectProjectionDir}" && rsync -a "${projectSourceDir}/" "${projectProjectionDir}/"`
125
+
126
+ const vercelDir = join(projectProjectionDir, '.vercel')
127
+ await mkdir(vercelDir, { recursive: true })
128
+
129
+ const defaultTeam = await this.project.vercel.getDefaultTeam()
130
+ const projectJson = {
131
+ projectId: details.id,
132
+ orgId: await this.project.vercel.orgIdForName({
133
+ name: defaultTeam
134
+ }),
135
+ projectName
136
+ }
137
+ await writeFile(
138
+ join(vercelDir, 'project.json'),
139
+ JSON.stringify(projectJson, null, 4)
140
+ )
141
+
142
+ const vercelJsonConfig = config.provider.config['/vercel.json'] || {}
143
+ const vercelJson = {
144
+ framework: vercelJsonConfig.framework,
145
+ installCommand: vercelJsonConfig.installCommand || 'bun install',
146
+ buildCommand: vercelJsonConfig.buildCommand || 'bun run build',
147
+ outputDirectory: vercelJsonConfig.outputDirectory
148
+ }
149
+ await writeFile(
150
+ join(projectProjectionDir, 'vercel.json'),
151
+ JSON.stringify(vercelJson, null, 4)
152
+ )
153
+
154
+ console.log(`Deploying to vercel ...`)
155
+
156
+ await $`vercel link --yes --project ${projectJson.projectName}`.cwd(projectProjectionDir)
157
+ await $`vercel deploy --force --target=preview`.cwd(projectProjectionDir)
158
+ // TODO: Add a workspace ID: '--meta WORKSPACE_ID=<id>'
159
+
160
+ console.log(`Deployment to vercel done.`)
161
+
162
+ // Fetch and store project details (automatically saved via $ProjectDeploymentFact)
163
+ await this.project.get({
164
+ name: details.name
165
+ })
166
+
167
+ // Write deployment status with updatedAt
168
+ const statusResult = {
169
+ projectName,
170
+ provider: 'vercel.com',
171
+ status: 'READY',
172
+ '#t44/structs/ProjectDeploymentConfig.v0': {
173
+ updatedAt: new Date().toISOString()
174
+ }
175
+ }
176
+ await this.$StatusFact.set('ProjectDeploymentStatus', projectName, 'ProjectDeploymentStatus', statusResult)
177
+ }
178
+ },
179
+ status: {
180
+ type: CapsulePropertyTypes.Function,
181
+ value: async function (this: any, { config, now, passive }: { config: any; now?: boolean; passive?: boolean }) {
182
+ const projectName = config.provider.config.ProjectSettings.name
183
+
184
+ if (!projectName) {
185
+ return {
186
+ projectName: projectName || 'unknown',
187
+ provider: 'vercel.com',
188
+ error: 'No project name configured',
189
+ rawDefinitionFilepaths: []
190
+ }
191
+ }
192
+
193
+ // Raw fact filepaths that this status depends on
194
+ const rawFilepaths = [
195
+ this.$ProjectDeploymentFact.getRelativeFilepath('projects', projectName)
196
+ ]
197
+
198
+ // Try to get cached status if not forcing refresh
199
+ if (!now) {
200
+ const cached = await this.$StatusFact.get('ProjectDeploymentStatus', projectName, 'ProjectDeploymentStatus', rawFilepaths)
201
+ if (cached) {
202
+ return cached.data
203
+ }
204
+ }
205
+
206
+ // In passive mode, don't call the provider if no cache exists
207
+ if (passive) {
208
+ return null
209
+ }
210
+
211
+ const projectDetails = await this.project.get({
212
+ name: projectName
213
+ })
214
+
215
+ if (!projectDetails) {
216
+ const errorResult = {
217
+ projectName,
218
+ provider: 'vercel.com',
219
+ error: 'Project not found',
220
+ rawDefinitionFilepaths: rawFilepaths
221
+ }
222
+ await this.$StatusFact.set('ProjectDeploymentStatus', projectName, 'ProjectDeploymentStatus', errorResult)
223
+ return errorResult
224
+ }
225
+
226
+ const statusTeam = await this.project.vercel.getDefaultTeam()
227
+ const deploymentsResponse = await (await this.project.vercel.vercel).deployments.getDeployments({
228
+ projectId: projectDetails.id,
229
+ teamId: await this.project.vercel.orgIdForName({
230
+ name: statusTeam
231
+ }),
232
+ limit: 1
233
+ })
234
+
235
+ const latestDeployment = deploymentsResponse.deployments?.[0]
236
+
237
+ const statusMap: Record<string, string> = {
238
+ 'READY': 'READY',
239
+ 'BUILDING': 'BUILDING',
240
+ 'ERROR': 'ERROR',
241
+ 'CANCELED': 'ERROR',
242
+ 'QUEUED': 'BUILDING'
243
+ }
244
+
245
+ // Preserve updatedAt from existing cached status
246
+ const existingStatus = await this.$StatusFact.get('ProjectDeploymentStatus', projectName, 'ProjectDeploymentStatus')
247
+ const existingMeta = existingStatus?.data?.['#t44/structs/ProjectDeploymentConfig.v0']
248
+
249
+ const result: Record<string, any> = {
250
+ projectName: projectDetails.name,
251
+ provider: 'vercel.com',
252
+ status: statusMap[latestDeployment?.readyState] || 'UNKNOWN',
253
+ publicUrl: latestDeployment?.url ? `https://${latestDeployment.url}` : undefined,
254
+ createdAt: latestDeployment?.createdAt,
255
+ updatedAt: latestDeployment?.aliasAssigned,
256
+ providerProjectId: projectDetails.id,
257
+ providerPortalUrl: latestDeployment?.inspectorUrl,
258
+ rawDefinitionFilepaths: rawFilepaths
259
+ }
260
+
261
+ if (existingMeta?.updatedAt) {
262
+ result['#t44/structs/ProjectDeploymentConfig.v0'] = {
263
+ updatedAt: existingMeta.updatedAt
264
+ }
265
+ }
266
+
267
+ await this.$StatusFact.set('ProjectDeploymentStatus', projectName, 'ProjectDeploymentStatus', result)
268
+
269
+ return result
270
+ }
271
+ },
272
+ deprovision: {
273
+ type: CapsulePropertyTypes.Function,
274
+ value: async function (this: any, { config }: { config: any }) {
275
+
276
+ const projectName = config.provider.config.ProjectSettings.name
277
+
278
+ console.log(`Deprovisioning project '${projectName}' from Vercel ...`)
279
+
280
+ try {
281
+ // Get project details to verify it exists
282
+ const details = await this.project.get({
283
+ name: projectName
284
+ })
285
+
286
+ if (!details) {
287
+ console.log(`Project '${projectName}' not found on Vercel. Nothing to deprovision.`)
288
+ return
289
+ }
290
+
291
+ console.log(`Found project ID: ${details.id}`)
292
+
293
+ // Delete the project
294
+ const deprovisionTeam = await this.project.vercel.getDefaultTeam()
295
+ await (await this.project.vercel.vercel).projects.deleteProject({
296
+ idOrName: details.id,
297
+ slug: deprovisionTeam
298
+ })
299
+
300
+ console.log(`Successfully deleted project '${projectName}' from Vercel.`)
301
+
302
+ // Delete fact files
303
+ console.log(`Deleting fact files ...`)
304
+ try {
305
+ await this.$ProjectDeploymentFact.delete('projects', projectName)
306
+ await this.$StatusFact.delete('ProjectDeploymentStatus', projectName)
307
+ console.log(`Fact files deleted`)
308
+ } catch (error: any) {
309
+ console.log(`Error deleting fact files: ${error.message}`)
310
+ }
311
+
312
+ } catch (error: any) {
313
+ if (error.message?.includes('not found') || error.status === 404) {
314
+ console.log(`Project '${projectName}' not found on Vercel. Nothing to deprovision.`)
315
+
316
+ // Still delete fact files even if project not found
317
+ console.log(`Deleting fact files ...`)
318
+ try {
319
+ await this.$ProjectDeploymentFact.delete('projects', projectName)
320
+ await this.$StatusFact.delete('ProjectDeploymentStatus', projectName)
321
+ console.log(`Fact files deleted`)
322
+ } catch (factError: any) {
323
+ console.log(`Error deleting fact files: ${factError.message}`)
324
+ }
325
+ } else {
326
+ throw error
327
+ }
328
+ }
329
+ }
330
+ }
331
+ }
332
+ }
333
+ }, {
334
+ importMeta: import.meta,
335
+ importStack: makeImportStack(),
336
+ capsuleName: capsule['#'],
337
+ })
338
+ }
339
+ capsule['#'] = 't44/caps/providers/vercel.com/ProjectDeployment.v0'
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bun test
2
+
3
+ export const testConfig = {
4
+ group: 'vendor',
5
+ runOnAll: false,
6
+ }
7
+
8
+ import * as bunTest from 'bun:test'
9
+ import { run } from '../../../workspace-rt'
10
+
11
+ const {
12
+ test: { describe, it, expect },
13
+ vercel
14
+ } = await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
15
+ const spine = await encapsulate({
16
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
17
+ '#@stream44.studio/encapsulate/structs/Capsule.v0': {},
18
+ '#': {
19
+ test: {
20
+ type: CapsulePropertyTypes.Mapping,
21
+ value: 't44/caps/WorkspaceTest.v0',
22
+ options: {
23
+ '#': {
24
+ bunTest,
25
+ env: {
26
+ VERCEL_TOKEN: { factReference: 't44/structs/providers/vercel.com/WorkspaceConnectionConfig.v0:apiToken' }
27
+ }
28
+ }
29
+ }
30
+ },
31
+ vercel: {
32
+ type: CapsulePropertyTypes.Mapping,
33
+ value: './api.v0'
34
+ },
35
+ }
36
+ }
37
+ }, {
38
+ importMeta: import.meta,
39
+ importStack: makeImportStack(),
40
+ capsuleName: 't44/caps/providers/vercel.com/api.v0.test'
41
+ })
42
+ return { spine }
43
+ }, async ({ spine, apis }: any) => {
44
+ return apis[spine.capsuleSourceLineRef]
45
+ }, {
46
+ importMeta: import.meta
47
+ })
48
+
49
+ describe('Vercel SDK', function () {
50
+
51
+ it('getTeams()', async function () {
52
+
53
+ const result = await vercel.getTeams()
54
+
55
+ expect(result).toBeObject()
56
+ expect(result.teams).toBeArray()
57
+ })
58
+
59
+ it('getProjects()', async function () {
60
+
61
+ const result = await vercel.getProjects()
62
+
63
+ expect(result).toBeObject()
64
+ expect(result.projects).toBeArray()
65
+ })
66
+
67
+ })
@@ -0,0 +1,132 @@
1
+
2
+ // Global promise to track ongoing team selection
3
+ let activeTeamSelection: Promise<string> | null = null
4
+
5
+ export async function capsule({
6
+ encapsulate,
7
+ CapsulePropertyTypes,
8
+ makeImportStack
9
+ }: {
10
+ encapsulate: any
11
+ CapsulePropertyTypes: any
12
+ makeImportStack: any
13
+ }) {
14
+ // Low level API that maps the vercel sdk API.
15
+ return encapsulate({
16
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
17
+ '#@stream44.studio/encapsulate/structs/Capsule.v0': {},
18
+ '#t44/structs/providers/vercel.com/WorkspaceConnectionConfig.v0': {
19
+ as: '$ConnectionConfig'
20
+ },
21
+ '#': {
22
+ // @see https://docs.vercel.com/docs/rest-api/reference/endpoints
23
+ vercel: {
24
+ type: CapsulePropertyTypes.GetterFunction,
25
+ value: async function (this: any) {
26
+
27
+ const { Vercel } = await import('@vercel/sdk');
28
+
29
+ const apiToken = await this.$ConnectionConfig.getConfigValue('apiToken')
30
+
31
+ const vercel = new Vercel({
32
+ bearerToken: apiToken
33
+ });
34
+
35
+ return vercel
36
+ }
37
+ },
38
+ getDefaultTeam: {
39
+ type: CapsulePropertyTypes.Function,
40
+ value: async function (this: any): Promise<string> {
41
+ // Check if already configured in stored config
42
+ const storedConfig = await this.$ConnectionConfig.getStoredConfig() || {}
43
+ if (storedConfig.team) {
44
+ return storedConfig.team
45
+ }
46
+
47
+ // Check if team selection is already in progress
48
+ if (activeTeamSelection) {
49
+ return activeTeamSelection
50
+ }
51
+
52
+ // Start team selection process
53
+ activeTeamSelection = (async () => {
54
+ try {
55
+ // Fetch teams from API
56
+ const chalk = (await import('chalk')).default
57
+ console.log(chalk.gray(' Fetching your Vercel teams...\n'))
58
+
59
+ const teamsResponse = await this.getTeams()
60
+ const teams = teamsResponse.teams || []
61
+
62
+ if (teams.length === 0) {
63
+ throw new Error('No teams found in your Vercel account')
64
+ }
65
+
66
+ // Prepare choices for selection
67
+ const choices = teams.map((team: any) => ({
68
+ name: `${team.name} (${team.slug})`,
69
+ value: team.slug
70
+ }))
71
+
72
+ // Prompt user to select team
73
+ const inquirer = await import('inquirer')
74
+ const { selectedTeam } = await inquirer.default.prompt([
75
+ {
76
+ type: 'list',
77
+ name: 'selectedTeam',
78
+ message: 'Default Team',
79
+ choices
80
+ }
81
+ ])
82
+
83
+ // Store the selected team
84
+ const updatedConfig = await this.$ConnectionConfig.getStoredConfig() || {}
85
+ updatedConfig.team = selectedTeam
86
+ await this.$ConnectionConfig.setStoredConfig(updatedConfig)
87
+
88
+ console.log(chalk.green(`\n ✓ Default Team saved to connection config\n`))
89
+
90
+ return selectedTeam
91
+ } finally {
92
+ activeTeamSelection = null
93
+ }
94
+ })()
95
+
96
+ return activeTeamSelection
97
+ }
98
+ },
99
+ getTeams: {
100
+ type: CapsulePropertyTypes.Function,
101
+ value: async function (this: any) {
102
+ return (await this.vercel).teams.getTeams({})
103
+ }
104
+ },
105
+ getProjects: {
106
+ type: CapsulePropertyTypes.Function,
107
+ value: async function (this: any) {
108
+ return (await this.vercel).projects.getProjects({})
109
+ }
110
+ },
111
+ orgIdForName: {
112
+ type: CapsulePropertyTypes.Function,
113
+ value: async function (this: any, { name }: { name: string }) {
114
+ const teamsResponse = await (await this.vercel).teams.getTeams({})
115
+ const team = teamsResponse.teams?.find((t: any) => t.slug === name || t.name === name)
116
+
117
+ if (!team) {
118
+ throw new Error(`Team '${name}' not found`)
119
+ }
120
+
121
+ return team.id
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }, {
127
+ importMeta: import.meta,
128
+ importStack: makeImportStack(),
129
+ capsuleName: capsule['#'],
130
+ })
131
+ }
132
+ capsule['#'] = 't44/caps/providers/vercel.com/api.v0'