@powerhousedao/vetra-builder-package 5.0.2 โ†’ 5.1.0-staging

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 (155) hide show
  1. package/dist/document-models/builder-team/actions.d.ts +28 -0
  2. package/dist/document-models/builder-team/actions.d.ts.map +1 -0
  3. package/dist/document-models/builder-team/actions.js +10 -0
  4. package/dist/document-models/builder-team/builder-team.json +234 -0
  5. package/dist/document-models/builder-team/gen/actions.d.ts +8 -8
  6. package/dist/document-models/builder-team/gen/actions.d.ts.map +1 -1
  7. package/dist/document-models/builder-team/gen/actions.js +4 -4
  8. package/dist/document-models/builder-team/gen/creators.d.ts +8 -4
  9. package/dist/document-models/builder-team/gen/creators.d.ts.map +1 -1
  10. package/dist/document-models/builder-team/gen/creators.js +8 -4
  11. package/dist/document-models/builder-team/gen/document-model.d.ts.map +1 -1
  12. package/dist/document-models/builder-team/gen/document-model.js +220 -220
  13. package/dist/document-models/builder-team/gen/document-schema.d.ts +180 -0
  14. package/dist/document-models/builder-team/gen/document-schema.d.ts.map +1 -0
  15. package/dist/document-models/builder-team/gen/document-schema.js +33 -0
  16. package/dist/document-models/builder-team/gen/document-type.d.ts +2 -0
  17. package/dist/document-models/builder-team/gen/document-type.d.ts.map +1 -0
  18. package/dist/document-models/builder-team/gen/document-type.js +1 -0
  19. package/dist/document-models/builder-team/gen/index.d.ts +14 -5
  20. package/dist/document-models/builder-team/gen/index.d.ts.map +1 -1
  21. package/dist/document-models/builder-team/gen/index.js +14 -5
  22. package/dist/document-models/builder-team/gen/member/actions.d.ts +5 -5
  23. package/dist/document-models/builder-team/gen/member/actions.d.ts.map +1 -1
  24. package/dist/document-models/builder-team/gen/member/actions.js +1 -1
  25. package/dist/document-models/builder-team/gen/member/creators.d.ts +2 -2
  26. package/dist/document-models/builder-team/gen/member/creators.d.ts.map +1 -1
  27. package/dist/document-models/builder-team/gen/member/creators.js +5 -6
  28. package/dist/document-models/builder-team/gen/member/error.d.ts.map +1 -1
  29. package/dist/document-models/builder-team/gen/member/operations.d.ts +3 -3
  30. package/dist/document-models/builder-team/gen/member/operations.d.ts.map +1 -1
  31. package/dist/document-models/builder-team/gen/member/operations.js +3 -3
  32. package/dist/document-models/builder-team/gen/packages/actions.d.ts +6 -6
  33. package/dist/document-models/builder-team/gen/packages/actions.d.ts.map +1 -1
  34. package/dist/document-models/builder-team/gen/packages/actions.js +1 -1
  35. package/dist/document-models/builder-team/gen/packages/creators.d.ts +2 -2
  36. package/dist/document-models/builder-team/gen/packages/creators.d.ts.map +1 -1
  37. package/dist/document-models/builder-team/gen/packages/creators.js +6 -7
  38. package/dist/document-models/builder-team/gen/packages/error.d.ts.map +1 -1
  39. package/dist/document-models/builder-team/gen/packages/operations.d.ts +3 -3
  40. package/dist/document-models/builder-team/gen/packages/operations.d.ts.map +1 -1
  41. package/dist/document-models/builder-team/gen/packages/operations.js +3 -3
  42. package/dist/document-models/builder-team/gen/ph-factories.d.ts.map +1 -1
  43. package/dist/document-models/builder-team/gen/ph-factories.js +16 -14
  44. package/dist/document-models/builder-team/gen/profile/actions.d.ts +7 -7
  45. package/dist/document-models/builder-team/gen/profile/actions.d.ts.map +1 -1
  46. package/dist/document-models/builder-team/gen/profile/actions.js +1 -1
  47. package/dist/document-models/builder-team/gen/profile/creators.d.ts +2 -2
  48. package/dist/document-models/builder-team/gen/profile/creators.d.ts.map +1 -1
  49. package/dist/document-models/builder-team/gen/profile/creators.js +7 -8
  50. package/dist/document-models/builder-team/gen/profile/error.d.ts.map +1 -1
  51. package/dist/document-models/builder-team/gen/profile/operations.d.ts +3 -3
  52. package/dist/document-models/builder-team/gen/profile/operations.d.ts.map +1 -1
  53. package/dist/document-models/builder-team/gen/profile/operations.js +3 -3
  54. package/dist/document-models/builder-team/gen/reducer.d.ts +1 -3
  55. package/dist/document-models/builder-team/gen/reducer.d.ts.map +1 -1
  56. package/dist/document-models/builder-team/gen/reducer.js +38 -38
  57. package/dist/document-models/builder-team/gen/schema/index.d.ts +1 -1
  58. package/dist/document-models/builder-team/gen/schema/index.d.ts.map +1 -1
  59. package/dist/document-models/builder-team/gen/schema/index.js +1 -1
  60. package/dist/document-models/builder-team/gen/spaces/actions.d.ts +6 -6
  61. package/dist/document-models/builder-team/gen/spaces/actions.d.ts.map +1 -1
  62. package/dist/document-models/builder-team/gen/spaces/actions.js +1 -1
  63. package/dist/document-models/builder-team/gen/spaces/creators.d.ts +2 -2
  64. package/dist/document-models/builder-team/gen/spaces/creators.d.ts.map +1 -1
  65. package/dist/document-models/builder-team/gen/spaces/creators.js +6 -7
  66. package/dist/document-models/builder-team/gen/spaces/error.d.ts.map +1 -1
  67. package/dist/document-models/builder-team/gen/spaces/operations.d.ts +3 -3
  68. package/dist/document-models/builder-team/gen/spaces/operations.d.ts.map +1 -1
  69. package/dist/document-models/builder-team/gen/spaces/operations.js +3 -3
  70. package/dist/document-models/builder-team/gen/types.d.ts +4 -5
  71. package/dist/document-models/builder-team/gen/types.d.ts.map +1 -1
  72. package/dist/document-models/builder-team/gen/types.js +1 -2
  73. package/dist/document-models/builder-team/gen/utils.d.ts +7 -4
  74. package/dist/document-models/builder-team/gen/utils.d.ts.map +1 -1
  75. package/dist/document-models/builder-team/gen/utils.js +41 -20
  76. package/dist/document-models/builder-team/hooks.d.ts +11 -0
  77. package/dist/document-models/builder-team/hooks.d.ts.map +1 -0
  78. package/dist/document-models/builder-team/hooks.js +26 -0
  79. package/dist/document-models/builder-team/index.d.ts +5 -41
  80. package/dist/document-models/builder-team/index.d.ts.map +1 -1
  81. package/dist/document-models/builder-team/index.js +5 -18
  82. package/dist/document-models/builder-team/module.d.ts +5 -0
  83. package/dist/document-models/builder-team/module.d.ts.map +1 -0
  84. package/dist/document-models/builder-team/module.js +10 -0
  85. package/dist/document-models/builder-team/src/index.d.ts +2 -0
  86. package/dist/document-models/builder-team/src/index.d.ts.map +1 -0
  87. package/dist/document-models/builder-team/src/index.js +1 -0
  88. package/dist/document-models/builder-team/src/reducers/member.d.ts +2 -2
  89. package/dist/document-models/builder-team/src/reducers/member.d.ts.map +1 -1
  90. package/dist/document-models/builder-team/src/reducers/member.js +4 -4
  91. package/dist/document-models/builder-team/src/reducers/packages.d.ts +2 -2
  92. package/dist/document-models/builder-team/src/reducers/packages.d.ts.map +1 -1
  93. package/dist/document-models/builder-team/src/reducers/packages.js +5 -5
  94. package/dist/document-models/builder-team/src/reducers/profile.d.ts +2 -2
  95. package/dist/document-models/builder-team/src/reducers/profile.d.ts.map +1 -1
  96. package/dist/document-models/builder-team/src/reducers/profile.js +6 -6
  97. package/dist/document-models/builder-team/src/reducers/spaces.d.ts +2 -2
  98. package/dist/document-models/builder-team/src/reducers/spaces.d.ts.map +1 -1
  99. package/dist/document-models/builder-team/src/reducers/spaces.js +5 -5
  100. package/dist/document-models/builder-team/src/tests/document-model.test.js +3 -2
  101. package/dist/document-models/builder-team/src/tests/member.test.js +5 -5
  102. package/dist/document-models/builder-team/src/tests/packages.test.js +5 -5
  103. package/dist/document-models/builder-team/src/tests/profile.test.js +7 -7
  104. package/dist/document-models/builder-team/src/tests/spaces.test.js +5 -5
  105. package/dist/document-models/builder-team/utils.d.ts +14 -0
  106. package/dist/document-models/builder-team/utils.d.ts.map +1 -0
  107. package/dist/document-models/builder-team/utils.js +7 -0
  108. package/dist/document-models/document-models.d.ts +3 -0
  109. package/dist/document-models/document-models.d.ts.map +1 -0
  110. package/dist/document-models/document-models.js +4 -0
  111. package/dist/document-models/index.d.ts +1 -2
  112. package/dist/document-models/index.d.ts.map +1 -1
  113. package/dist/document-models/index.js +1 -2
  114. package/dist/editors/builder-team-editor/components/EditName.d.ts +3 -0
  115. package/dist/editors/builder-team-editor/components/EditName.d.ts.map +1 -0
  116. package/dist/editors/builder-team-editor/components/EditName.js +31 -0
  117. package/dist/editors/builder-team-editor/components/PackagesTable.d.ts.map +1 -1
  118. package/dist/editors/builder-team-editor/components/PackagesTable.js +0 -1
  119. package/dist/editors/builder-team-editor/editor.d.ts +1 -1
  120. package/dist/editors/builder-team-editor/editor.d.ts.map +1 -1
  121. package/dist/editors/builder-team-editor/editor.js +2 -4
  122. package/dist/editors/builder-team-editor/hooks/usePackageHandlers.d.ts +1 -2
  123. package/dist/editors/builder-team-editor/hooks/usePackageHandlers.d.ts.map +1 -1
  124. package/dist/editors/builder-team-editor/hooks/usePackageHandlers.js +1 -1
  125. package/dist/editors/builder-team-editor/module.d.ts +4 -0
  126. package/dist/editors/builder-team-editor/module.d.ts.map +1 -0
  127. package/dist/editors/builder-team-editor/module.js +10 -0
  128. package/dist/editors/editors.d.ts +3 -0
  129. package/dist/editors/editors.d.ts.map +1 -0
  130. package/dist/editors/editors.js +4 -0
  131. package/dist/editors/index.d.ts +1 -2
  132. package/dist/editors/index.d.ts.map +1 -1
  133. package/dist/editors/index.js +1 -2
  134. package/dist/index.d.ts +2 -2
  135. package/dist/index.d.ts.map +1 -1
  136. package/dist/index.js +2 -4
  137. package/dist/processors/vetra-builder-relational-db-processor/builder-team-handlers.d.ts +1 -1
  138. package/dist/processors/vetra-builder-relational-db-processor/builder-team-handlers.d.ts.map +1 -1
  139. package/dist/processors/vetra-builder-relational-db-processor/builder-team-handlers.js +1 -1
  140. package/dist/processors/vetra-builder-relational-db-processor/migrations.d.ts.map +1 -1
  141. package/dist/processors/vetra-builder-relational-db-processor/migrations.js +0 -3
  142. package/dist/scripts/add-builder-team-model.d.ts +2 -0
  143. package/dist/scripts/add-builder-team-model.d.ts.map +1 -0
  144. package/dist/scripts/add-builder-team-model.js +122 -0
  145. package/dist/scripts/create-remote-drives.d.ts +17 -0
  146. package/dist/scripts/create-remote-drives.d.ts.map +1 -0
  147. package/dist/scripts/create-remote-drives.js +946 -0
  148. package/dist/scripts/introspect-schema.d.ts +8 -0
  149. package/dist/scripts/introspect-schema.d.ts.map +1 -0
  150. package/dist/scripts/introspect-schema.js +216 -0
  151. package/dist/scripts/upload-to-drive.js +0 -2
  152. package/dist/style.css +8 -1
  153. package/dist/subgraphs/vetra-builders/resolvers.d.ts.map +1 -1
  154. package/dist/subgraphs/vetra-builders/resolvers.js +0 -1
  155. package/package.json +48 -32
@@ -0,0 +1,946 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Create Remote Drives from GitHub Repositories
4
+ *
5
+ * This script:
6
+ * 1. Fetches powerhouse.manifest.json from GitHub repositories
7
+ * 2. Creates remote drives at localhost:4001/graphql
8
+ * 3. Creates package documents in each drive with manifest data
9
+ * 4. Creates document model documents with full specifications
10
+ *
11
+ * Usage (from project root):
12
+ * npm run create-drives
13
+ * OR
14
+ * npx tsx scripts/create-remote-drives.ts
15
+ */
16
+ import { generateId } from "document-model/core";
17
+ import { ReactorBuilder, MemoryStorage, InMemoryCache, SynchronizationManager, DefaultEventEmitter, EventQueueManager, ListenerManager, baseDocumentModels, } from "document-drive";
18
+ import * as documentModels from "../document-models/index.js";
19
+ import * as vetraDocumentModels from "@powerhousedao/vetra/document-models";
20
+ // List of GitHub repository URLs to process
21
+ const GITHUB_REPOS = [
22
+ // "https://github.com/powerhouse-inc/vetra-builder-package",
23
+ // "https://github.com/powerhouse-inc/network-admin",
24
+ // "https://github.com/powerhouse-inc/project-management",
25
+ // "https://github.com/powerhouse-inc/contributor-billing",
26
+ // "https://github.com/powerhouse-inc/effective-octo-adventure",
27
+ // "https://github.com/powerhouse-inc/renown-package",
28
+ "https://github.com/powerhouse-inc/chatroom-demo",
29
+ // Add more repository URLs here
30
+ ];
31
+ // GraphQL endpoint
32
+ const GRAPHQL_ENDPOINT = "https://switchboard.vetra.io/graphql";
33
+ // const GRAPHQL_ENDPOINT = "http://localhost:4001/graphql";
34
+ /**
35
+ * Convert string to kebab-case
36
+ */
37
+ function toKebabCase(str) {
38
+ return (str
39
+ // Insert hyphen before uppercase letters
40
+ .replace(/([a-z])([A-Z])/g, "$1-$2")
41
+ // Convert to lowercase
42
+ .toLowerCase()
43
+ // Replace spaces and other non-alphanumeric chars with hyphens
44
+ .replace(/[^a-z0-9]+/g, "-")
45
+ // Remove leading/trailing hyphens
46
+ .replace(/^-+|-+$/g, ""));
47
+ }
48
+ /**
49
+ * Fetch document model specs that exist in the repository
50
+ */
51
+ async function fetchDocumentModelSpecs(repoUrl, documentModels) {
52
+ const results = [];
53
+ const seenSpecs = new Set();
54
+ // Try to fetch each document model JSON file
55
+ for (const model of documentModels) {
56
+ try {
57
+ // Convert model name to kebab-case for filename
58
+ const filename = toKebabCase(model.name);
59
+ const rawUrl = repoUrl
60
+ .replace("https://github.com/", "https://raw.githubusercontent.com/")
61
+ .replace(/\/$/, "") +
62
+ `/main/document-models/${filename}/${filename}.json`;
63
+ const response = await fetch(rawUrl);
64
+ if (response.ok) {
65
+ const spec = await response.json();
66
+ // Deduplicate by spec.id to avoid showing same model multiple times
67
+ if (!seenSpecs.has(spec.id)) {
68
+ seenSpecs.add(spec.id);
69
+ results.push({ id: spec.id, name: spec.name, spec });
70
+ }
71
+ }
72
+ else {
73
+ // Try master branch as fallback
74
+ const masterUrl = rawUrl.replace("/main/", "/master/");
75
+ const masterResponse = await fetch(masterUrl);
76
+ if (masterResponse.ok) {
77
+ const spec = await masterResponse.json();
78
+ if (!seenSpecs.has(spec.id)) {
79
+ seenSpecs.add(spec.id);
80
+ results.push({ id: spec.id, name: spec.name, spec });
81
+ }
82
+ }
83
+ }
84
+ }
85
+ catch (error) {
86
+ // Skip models that don't exist in this repo
87
+ continue;
88
+ }
89
+ }
90
+ return results;
91
+ }
92
+ /**
93
+ * Fetch powerhouse.manifest.json from GitHub repository
94
+ */
95
+ async function fetchManifest(repoUrl) {
96
+ // Convert GitHub URL to raw content URL
97
+ // https://github.com/user/repo -> https://raw.githubusercontent.com/user/repo/main/powerhouse.manifest.json
98
+ const rawUrl = repoUrl
99
+ .replace("https://github.com/", "https://raw.githubusercontent.com/")
100
+ .replace(/\/$/, "") + "/main/powerhouse.manifest.json";
101
+ console.log(`๐Ÿ“ฅ Fetching manifest from: ${rawUrl}`);
102
+ try {
103
+ const response = await fetch(rawUrl);
104
+ if (!response.ok) {
105
+ // Try with 'master' branch if 'main' fails
106
+ const masterUrl = rawUrl.replace("/main/", "/master/");
107
+ console.log(` โš  Main branch failed, trying master: ${masterUrl}`);
108
+ const masterResponse = await fetch(masterUrl);
109
+ if (!masterResponse.ok) {
110
+ throw new Error(`Failed to fetch manifest: ${response.status} ${response.statusText}`);
111
+ }
112
+ const manifest = await masterResponse.json();
113
+ console.log(` โœ“ Fetched manifest: ${manifest.name}`);
114
+ return manifest;
115
+ }
116
+ const manifest = await response.json();
117
+ console.log(` โœ“ Fetched manifest: ${manifest.name}`);
118
+ return manifest;
119
+ }
120
+ catch (error) {
121
+ console.error(` โœ— Error fetching manifest:`, error);
122
+ throw error;
123
+ }
124
+ }
125
+ /**
126
+ * Create a remote drive via GraphQL mutation (or skip if it already exists)
127
+ */
128
+ async function createDrive(manifest, repoUrl) {
129
+ const driveName = manifest.name;
130
+ const driveId = toKebabCase(manifest.name);
131
+ const driveSlug = driveId;
132
+ console.log(`\n๐Ÿ“ Creating drive: ${driveName}`);
133
+ console.log(` ID: ${driveId}`);
134
+ console.log(` Slug: ${driveSlug}`);
135
+ const mutation = `
136
+ mutation AddDrive($id: String, $name: String!, $slug: String, $preferredEditor: String) {
137
+ addDrive(
138
+ id: $id
139
+ name: $name
140
+ slug: $slug
141
+ preferredEditor: $preferredEditor
142
+ ) {
143
+ id
144
+ name
145
+ slug
146
+ preferredEditor
147
+ }
148
+ }
149
+ `;
150
+ const variables = {
151
+ id: driveId,
152
+ name: driveName,
153
+ slug: driveSlug,
154
+ preferredEditor: "vetra-drive-app",
155
+ };
156
+ try {
157
+ const response = await fetch(GRAPHQL_ENDPOINT, {
158
+ method: "POST",
159
+ headers: {
160
+ "Content-Type": "application/json",
161
+ },
162
+ body: JSON.stringify({
163
+ query: mutation,
164
+ variables,
165
+ }),
166
+ });
167
+ const result = await response.json();
168
+ if (result.errors) {
169
+ // Check if the error is about drive already existing
170
+ const alreadyExistsError = result.errors.find((err) => err.message?.includes("uses id that already exists"));
171
+ if (alreadyExistsError) {
172
+ console.log(` โ„น Drive already exists, skipping creation`);
173
+ return driveId;
174
+ }
175
+ console.error(` โœ— GraphQL errors:`, JSON.stringify(result.errors, null, 2));
176
+ throw new Error(`Failed to create drive: ${JSON.stringify(result.errors)}`);
177
+ }
178
+ const createdDrive = result.data?.addDrive;
179
+ console.log(` โœ“ Drive created successfully`);
180
+ console.log(` ID: ${createdDrive.id}`);
181
+ console.log(` Name: ${createdDrive.name}`);
182
+ console.log(` Slug: ${createdDrive.slug}`);
183
+ return createdDrive.id;
184
+ }
185
+ catch (error) {
186
+ console.error(` โœ— Error creating drive:`, error);
187
+ throw error;
188
+ }
189
+ }
190
+ /**
191
+ * Create a package document in the drive
192
+ */
193
+ async function createPackageDocument(driveId, manifest, repoUrl) {
194
+ console.log(`\n๐Ÿ“ฆ Creating package document in drive: ${driveId}`);
195
+ // Using VetraPackage_createDocument mutation
196
+ const mutation = `
197
+ mutation VetraPackage_createDocument($driveId: String, $name: String) {
198
+ VetraPackage_createDocument(driveId: $driveId, name: $name)
199
+ }
200
+ `;
201
+ const variables = {
202
+ name: manifest.name,
203
+ driveId: driveId,
204
+ };
205
+ try {
206
+ const response = await fetch(GRAPHQL_ENDPOINT, {
207
+ method: "POST",
208
+ headers: {
209
+ "Content-Type": "application/json",
210
+ },
211
+ body: JSON.stringify({
212
+ query: mutation,
213
+ variables,
214
+ }),
215
+ });
216
+ const result = await response.json();
217
+ if (result.errors) {
218
+ console.error(` โœ— GraphQL errors:`, JSON.stringify(result.errors, null, 2));
219
+ throw new Error(`Failed to create package document: ${JSON.stringify(result.errors)}`);
220
+ }
221
+ const documentId = result.data?.VetraPackage_createDocument;
222
+ console.log(` โœ“ Package document created`);
223
+ console.log(` Document ID (PHID): ${documentId}`);
224
+ return documentId;
225
+ }
226
+ catch (error) {
227
+ console.error(` โœ— Error creating package document:`, error);
228
+ throw error;
229
+ }
230
+ }
231
+ /**
232
+ * Create an editor document in the drive
233
+ */
234
+ async function createEditorDocument(driveId, editor) {
235
+ // Create the editor document
236
+ const createMutation = `
237
+ mutation DocumentEditor_createDocument($driveId: String, $name: String!) {
238
+ DocumentEditor_createDocument(driveId: $driveId, name: $name)
239
+ }
240
+ `;
241
+ const createResponse = await fetch(GRAPHQL_ENDPOINT, {
242
+ method: "POST",
243
+ headers: {
244
+ "Content-Type": "application/json",
245
+ },
246
+ body: JSON.stringify({
247
+ query: createMutation,
248
+ variables: {
249
+ driveId,
250
+ name: editor.name,
251
+ },
252
+ }),
253
+ });
254
+ const createResult = await createResponse.json();
255
+ if (createResult.errors) {
256
+ throw new Error(`Failed to create editor: ${JSON.stringify(createResult.errors)}`);
257
+ }
258
+ const documentId = createResult.data?.DocumentEditor_createDocument;
259
+ // Set editor name (using ID as fallback)
260
+ const setNameMutation = `
261
+ mutation DocumentEditor_setEditorName($driveId: String, $docId: PHID, $input: DocumentEditor_SetEditorNameInput) {
262
+ DocumentEditor_setEditorName(driveId: $driveId, docId: $docId, input: $input)
263
+ }
264
+ `;
265
+ await fetch(GRAPHQL_ENDPOINT, {
266
+ method: "POST",
267
+ headers: {
268
+ "Content-Type": "application/json",
269
+ },
270
+ body: JSON.stringify({
271
+ query: setNameMutation,
272
+ variables: {
273
+ driveId,
274
+ docId: documentId,
275
+ input: { name: editor.id },
276
+ },
277
+ }),
278
+ });
279
+ // Add document types
280
+ for (const docType of editor.documentTypes) {
281
+ const addTypeMutation = `
282
+ mutation DocumentEditor_addDocumentType($driveId: String, $docId: PHID, $input: DocumentEditor_AddDocumentTypeInput) {
283
+ DocumentEditor_addDocumentType(driveId: $driveId, docId: $docId, input: $input)
284
+ }
285
+ `;
286
+ await fetch(GRAPHQL_ENDPOINT, {
287
+ method: "POST",
288
+ headers: {
289
+ "Content-Type": "application/json",
290
+ },
291
+ body: JSON.stringify({
292
+ query: addTypeMutation,
293
+ variables: {
294
+ driveId,
295
+ docId: documentId,
296
+ input: { documentType: docType },
297
+ },
298
+ }),
299
+ });
300
+ }
301
+ return documentId;
302
+ }
303
+ /**
304
+ * Create a subgraph document in the drive
305
+ */
306
+ async function createSubgraphDocument(driveId, subgraph) {
307
+ // Create the subgraph document
308
+ const createMutation = `
309
+ mutation SubgraphModule_createDocument($driveId: String, $name: String!) {
310
+ SubgraphModule_createDocument(driveId: $driveId, name: $name)
311
+ }
312
+ `;
313
+ const createResponse = await fetch(GRAPHQL_ENDPOINT, {
314
+ method: "POST",
315
+ headers: {
316
+ "Content-Type": "application/json",
317
+ },
318
+ body: JSON.stringify({
319
+ query: createMutation,
320
+ variables: {
321
+ driveId,
322
+ name: subgraph.name,
323
+ },
324
+ }),
325
+ });
326
+ const createResult = await createResponse.json();
327
+ if (createResult.errors) {
328
+ throw new Error(`Failed to create subgraph: ${JSON.stringify(createResult.errors)}`);
329
+ }
330
+ const documentId = createResult.data?.SubgraphModule_createDocument;
331
+ // Set subgraph name (using ID)
332
+ const setNameMutation = `
333
+ mutation SubgraphModule_setSubgraphName($driveId: String, $docId: PHID, $input: SubgraphModule_SetSubgraphNameInput) {
334
+ SubgraphModule_setSubgraphName(driveId: $driveId, docId: $docId, input: $input)
335
+ }
336
+ `;
337
+ await fetch(GRAPHQL_ENDPOINT, {
338
+ method: "POST",
339
+ headers: {
340
+ "Content-Type": "application/json",
341
+ },
342
+ body: JSON.stringify({
343
+ query: setNameMutation,
344
+ variables: {
345
+ driveId,
346
+ docId: documentId,
347
+ input: { name: subgraph.id },
348
+ },
349
+ }),
350
+ });
351
+ return documentId;
352
+ }
353
+ /**
354
+ * Retry with exponential backoff
355
+ */
356
+ async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
357
+ let lastError;
358
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
359
+ try {
360
+ return await fn();
361
+ }
362
+ catch (error) {
363
+ lastError = error;
364
+ if (attempt < maxRetries - 1) {
365
+ const delay = baseDelay * Math.pow(2, attempt);
366
+ console.log(` โš  Attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
367
+ await new Promise((resolve) => setTimeout(resolve, delay));
368
+ }
369
+ }
370
+ }
371
+ throw lastError;
372
+ }
373
+ /**
374
+ * Wait for sync to complete by checking drive state
375
+ */
376
+ async function waitForSync(driveServer, driveId, timeout = 15000) {
377
+ const startTime = Date.now();
378
+ while (Date.now() - startTime < timeout) {
379
+ try {
380
+ // Try to get the drive - if it succeeds, sync is complete
381
+ await driveServer.getDrive(driveId);
382
+ // Add an additional small delay after drive is accessible
383
+ await new Promise((resolve) => setTimeout(resolve, 500));
384
+ return;
385
+ }
386
+ catch (error) {
387
+ // Drive not ready yet, wait a bit
388
+ await new Promise((resolve) => setTimeout(resolve, 300));
389
+ }
390
+ }
391
+ throw new Error(`Sync timeout after ${timeout}ms`);
392
+ }
393
+ // Shared drive server instance
394
+ let sharedDriveServer = null;
395
+ /**
396
+ * Get or create the shared drive server instance
397
+ */
398
+ async function getSharedDriveServer() {
399
+ if (!sharedDriveServer) {
400
+ const allDocumentModels = [
401
+ ...baseDocumentModels,
402
+ ...Object.values(documentModels),
403
+ ...Object.values(vetraDocumentModels),
404
+ ];
405
+ sharedDriveServer = new ReactorBuilder(allDocumentModels).build();
406
+ await sharedDriveServer.initialize();
407
+ }
408
+ return sharedDriveServer;
409
+ }
410
+ /**
411
+ * Cleanup the shared drive server
412
+ */
413
+ async function cleanupSharedDriveServer() {
414
+ if (sharedDriveServer) {
415
+ if (typeof sharedDriveServer.shutdown === "function") {
416
+ await sharedDriveServer.shutdown();
417
+ }
418
+ sharedDriveServer = null;
419
+ }
420
+ }
421
+ /**
422
+ * Register remote drive with local drive server to enable processors
423
+ */
424
+ async function registerRemoteDrive(driveId) {
425
+ const SWITCHBOARD_URL = "https://switchboard.vetra.io";
426
+ const remoteDriveUrl = `${SWITCHBOARD_URL}/d/${driveId}`;
427
+ // Get shared drive server
428
+ const driveServer = await getSharedDriveServer();
429
+ // Add remote drive (with retry logic)
430
+ await retryWithBackoff(async () => {
431
+ try {
432
+ // Check if drive already exists
433
+ await driveServer.getDrive(driveId);
434
+ console.log(` โœ“ Drive ${driveId} already registered in local server`);
435
+ }
436
+ catch (error) {
437
+ // Drive doesn't exist, add it
438
+ console.log(` ๐Ÿ“ก Registering remote drive: ${remoteDriveUrl}`);
439
+ try {
440
+ await driveServer.addRemoteDrive(remoteDriveUrl, {
441
+ availableOffline: true,
442
+ listeners: [
443
+ {
444
+ block: true,
445
+ callInfo: {
446
+ data: remoteDriveUrl,
447
+ name: "switchboard-push",
448
+ transmitterType: "SwitchboardPush",
449
+ },
450
+ filter: {
451
+ branch: ["main"],
452
+ documentId: ["*"],
453
+ documentType: ["*"],
454
+ scope: ["global"],
455
+ },
456
+ listenerId: generateId(),
457
+ label: "Switchboard Sync",
458
+ system: true,
459
+ },
460
+ ],
461
+ sharingType: "public",
462
+ triggers: [],
463
+ });
464
+ // Wait for initial sync
465
+ console.log(` โณ Waiting for drive sync...`);
466
+ await waitForSync(driveServer, driveId);
467
+ console.log(` โœ“ Drive registered and synced`);
468
+ }
469
+ catch (syncError) {
470
+ // If sync fails, it might be because the drive is still being created on remote
471
+ console.log(` โš  Sync warning (may be expected):`, syncError instanceof Error ? syncError.message : String(syncError));
472
+ // Give it a moment and try to get the drive anyway
473
+ await new Promise((resolve) => setTimeout(resolve, 1000));
474
+ }
475
+ }
476
+ }, 5, 1000);
477
+ }
478
+ /**
479
+ * Create document model document using drive server sync approach
480
+ * Steps:
481
+ * 1. Use shared drive server with ALL document models loaded
482
+ * 2. Add remote drive for synchronization (if not already added)
483
+ * 3. Wait for syncing to complete
484
+ * 4. Create new document
485
+ * 5. Add operations to populate document
486
+ * 6. Wait for operations to sync
487
+ */
488
+ async function createDocumentModelDocumentViaDriveServer(driveId, name, spec) {
489
+ const SWITCHBOARD_URL = "http://localhost:4001";
490
+ const remoteDriveUrl = `${SWITCHBOARD_URL}/d/${driveId}`;
491
+ // Step 1: Get shared drive server
492
+ const driveServer = await getSharedDriveServer();
493
+ // Step 2: Add remote drive (with retry logic)
494
+ await retryWithBackoff(async () => {
495
+ try {
496
+ // Check if drive already exists
497
+ await driveServer.getDrive(driveId);
498
+ console.log(` โœ“ Drive ${driveId} already exists in local server`);
499
+ }
500
+ catch (error) {
501
+ // Drive doesn't exist, add it
502
+ console.log(` ๐Ÿ“ก Adding remote drive: ${remoteDriveUrl}`);
503
+ try {
504
+ await driveServer.addRemoteDrive(remoteDriveUrl, {
505
+ availableOffline: true,
506
+ listeners: [
507
+ {
508
+ block: true,
509
+ callInfo: {
510
+ data: remoteDriveUrl,
511
+ name: "switchboard-push",
512
+ transmitterType: "SwitchboardPush",
513
+ },
514
+ filter: {
515
+ branch: ["main"],
516
+ documentId: ["*"],
517
+ documentType: ["*"],
518
+ scope: ["global"],
519
+ },
520
+ listenerId: generateId(),
521
+ label: "Switchboard Sync",
522
+ system: true,
523
+ },
524
+ ],
525
+ sharingType: "public",
526
+ triggers: [],
527
+ });
528
+ // Step 3: Wait for initial sync
529
+ console.log(` โณ Waiting for initial sync...`);
530
+ await waitForSync(driveServer, driveId);
531
+ console.log(` โœ“ Sync complete`);
532
+ }
533
+ catch (syncError) {
534
+ // If sync fails, it might be because the drive is still being created on remote
535
+ console.log(` โš  Sync warning (may be expected):`, syncError instanceof Error ? syncError.message : String(syncError));
536
+ // Give it a moment and try to get the drive anyway
537
+ await new Promise((resolve) => setTimeout(resolve, 1000));
538
+ }
539
+ }
540
+ }, 5, 1000);
541
+ // Step 4: Create new document (with retry)
542
+ const createResult = await retryWithBackoff(async () => {
543
+ return await driveServer.createDocument({
544
+ driveId,
545
+ documentType: "powerhouse/document-model",
546
+ name,
547
+ // Don't pass id - let it be auto-generated with signed header
548
+ });
549
+ });
550
+ const documentId = createResult.documentId || createResult.id || createResult;
551
+ // Small delay to let document creation propagate
552
+ await new Promise((resolve) => setTimeout(resolve, 1000));
553
+ // Step 5: Add operations from spec to populate the document
554
+ if (spec.specifications && spec.specifications.length > 0) {
555
+ const specification = spec.specifications[0];
556
+ const actions = [];
557
+ // Add basic metadata
558
+ actions.push({ type: "SET_MODEL_NAME", input: { name: spec.name } });
559
+ actions.push({ type: "SET_MODEL_ID", input: { id: spec.id } });
560
+ actions.push({
561
+ type: "SET_MODEL_EXTENSION",
562
+ input: { extension: spec.extension },
563
+ });
564
+ actions.push({
565
+ type: "SET_MODEL_DESCRIPTION",
566
+ input: { description: spec.description },
567
+ });
568
+ actions.push({
569
+ type: "SET_AUTHOR_NAME",
570
+ input: { name: spec.author.name },
571
+ });
572
+ actions.push({
573
+ type: "SET_AUTHOR_WEBSITE",
574
+ input: { website: spec.author.website },
575
+ });
576
+ // Add state schema
577
+ if (specification.state?.global?.schema) {
578
+ actions.push({
579
+ type: "SET_STATE_SCHEMA",
580
+ input: {
581
+ schema: specification.state.global.schema,
582
+ scope: "global",
583
+ },
584
+ });
585
+ }
586
+ if (specification.state?.global?.initialValue) {
587
+ actions.push({
588
+ type: "SET_INITIAL_STATE",
589
+ input: {
590
+ initialValue: specification.state.global.initialValue,
591
+ scope: "global",
592
+ },
593
+ });
594
+ }
595
+ // Add modules and operations
596
+ if (specification.modules) {
597
+ for (const module of specification.modules) {
598
+ actions.push({
599
+ type: "ADD_MODULE",
600
+ input: {
601
+ id: module.id,
602
+ name: module.name,
603
+ description: module.description || "",
604
+ },
605
+ });
606
+ if (module.operations) {
607
+ for (const operation of module.operations) {
608
+ actions.push({
609
+ type: "ADD_OPERATION",
610
+ input: {
611
+ id: operation.id,
612
+ moduleId: module.id,
613
+ name: operation.name,
614
+ description: operation.description || "",
615
+ scope: operation.scope,
616
+ },
617
+ });
618
+ if (operation.schema) {
619
+ actions.push({
620
+ type: "SET_OPERATION_SCHEMA",
621
+ input: {
622
+ id: operation.id,
623
+ schema: operation.schema,
624
+ },
625
+ });
626
+ }
627
+ if (operation.reducer) {
628
+ actions.push({
629
+ type: "SET_OPERATION_REDUCER",
630
+ input: {
631
+ id: operation.id,
632
+ reducer: operation.reducer,
633
+ },
634
+ });
635
+ }
636
+ }
637
+ }
638
+ }
639
+ }
640
+ // Apply all actions (with retry)
641
+ await retryWithBackoff(async () => {
642
+ await driveServer.addActions(documentId, actions);
643
+ });
644
+ }
645
+ // Step 6: Wait for operations to sync (increased delay for large documents)
646
+ await new Promise((resolve) => setTimeout(resolve, 2000));
647
+ return documentId;
648
+ }
649
+ /**
650
+ * Create a processor document in the drive
651
+ */
652
+ async function createProcessorDocument(driveId, processor) {
653
+ // Create the processor document
654
+ const createMutation = `
655
+ mutation ProcessorModule_createDocument($driveId: String, $name: String!) {
656
+ ProcessorModule_createDocument(driveId: $driveId, name: $name)
657
+ }
658
+ `;
659
+ const createResponse = await fetch(GRAPHQL_ENDPOINT, {
660
+ method: "POST",
661
+ headers: {
662
+ "Content-Type": "application/json",
663
+ },
664
+ body: JSON.stringify({
665
+ query: createMutation,
666
+ variables: {
667
+ driveId,
668
+ name: processor.name,
669
+ },
670
+ }),
671
+ });
672
+ const createResult = await createResponse.json();
673
+ if (createResult.errors) {
674
+ throw new Error(`Failed to create processor: ${JSON.stringify(createResult.errors)}`);
675
+ }
676
+ const documentId = createResult.data?.ProcessorModule_createDocument;
677
+ // Set processor name (using ID)
678
+ const setNameMutation = `
679
+ mutation ProcessorModule_setProcessorName($driveId: String, $docId: PHID, $input: ProcessorModule_SetProcessorNameInput) {
680
+ ProcessorModule_setProcessorName(driveId: $driveId, docId: $docId, input: $input)
681
+ }
682
+ `;
683
+ await fetch(GRAPHQL_ENDPOINT, {
684
+ method: "POST",
685
+ headers: {
686
+ "Content-Type": "application/json",
687
+ },
688
+ body: JSON.stringify({
689
+ query: setNameMutation,
690
+ variables: {
691
+ driveId,
692
+ docId: documentId,
693
+ input: { name: processor.id },
694
+ },
695
+ }),
696
+ });
697
+ // Add document types
698
+ for (const docType of processor.documentTypes) {
699
+ const addTypeMutation = `
700
+ mutation ProcessorModule_addDocumentType($driveId: String, $docId: PHID, $input: ProcessorModule_AddDocumentTypeInput) {
701
+ ProcessorModule_addDocumentType(driveId: $driveId, docId: $docId, input: $input)
702
+ }
703
+ `;
704
+ await fetch(GRAPHQL_ENDPOINT, {
705
+ method: "POST",
706
+ headers: {
707
+ "Content-Type": "application/json",
708
+ },
709
+ body: JSON.stringify({
710
+ query: addTypeMutation,
711
+ variables: {
712
+ driveId,
713
+ docId: documentId,
714
+ input: { documentType: docType },
715
+ },
716
+ }),
717
+ });
718
+ }
719
+ return documentId;
720
+ }
721
+ /**
722
+ * Set package document fields via GraphQL mutations
723
+ */
724
+ async function updatePackageDocument(driveId, documentId, manifest, repoUrl) {
725
+ console.log(`\nโš™๏ธ Updating package document fields...`);
726
+ // Set basic package info
727
+ const operations = [
728
+ {
729
+ name: "SET_PACKAGE_NAME",
730
+ mutation: "VetraPackage_setPackageName",
731
+ input: { name: manifest.name },
732
+ },
733
+ {
734
+ name: "SET_PACKAGE_DESCRIPTION",
735
+ mutation: "VetraPackage_setPackageDescription",
736
+ input: { description: manifest.description },
737
+ },
738
+ {
739
+ name: "SET_PACKAGE_CATEGORY",
740
+ mutation: "VetraPackage_setPackageCategory",
741
+ input: { category: manifest.category },
742
+ },
743
+ {
744
+ name: "SET_PACKAGE_AUTHOR",
745
+ mutation: "VetraPackage_setPackageAuthor",
746
+ input: {
747
+ name: manifest.publisher.name,
748
+ website: manifest.publisher.url,
749
+ },
750
+ },
751
+ {
752
+ name: "SET_PACKAGE_GITHUB_URL",
753
+ mutation: "VetraPackage_setPackageGithubUrl",
754
+ input: { url: repoUrl },
755
+ },
756
+ ];
757
+ // Add npm URL if available
758
+ if (manifest.npmUrl) {
759
+ operations.push({
760
+ name: "SET_PACKAGE_NPM_URL",
761
+ mutation: "VetraPackage_setPackageNpmUrl",
762
+ input: { url: manifest.npmUrl },
763
+ });
764
+ }
765
+ for (const op of operations) {
766
+ try {
767
+ const mutation = `
768
+ mutation Update($driveId: String, $docId: PHID, $input: VetraPackage_${op.name
769
+ .split("_")
770
+ .map((p) => p.charAt(0) + p.slice(1).toLowerCase())
771
+ .join("")}Input) {
772
+ ${op.mutation}(driveId: $driveId, docId: $docId, input: $input)
773
+ }
774
+ `;
775
+ const variables = {
776
+ driveId,
777
+ docId: documentId,
778
+ input: op.input,
779
+ };
780
+ const response = await fetch(GRAPHQL_ENDPOINT, {
781
+ method: "POST",
782
+ headers: {
783
+ "Content-Type": "application/json",
784
+ },
785
+ body: JSON.stringify({
786
+ query: mutation,
787
+ variables,
788
+ }),
789
+ });
790
+ const result = await response.json();
791
+ if (result.errors) {
792
+ console.log(` โš  Skipping ${op.name}: ${result.errors[0]?.message}`);
793
+ }
794
+ else {
795
+ console.log(` โœ“ ${op.name}`);
796
+ }
797
+ }
798
+ catch (error) {
799
+ console.log(` โš  Skipping ${op.name}: ${error instanceof Error ? error.message : String(error)}`);
800
+ }
801
+ }
802
+ // Create editor documents
803
+ if (manifest.editors && manifest.editors.length > 0) {
804
+ console.log(`\n ๐Ÿ“ Creating ${manifest.editors.length} editor documents...`);
805
+ for (let i = 0; i < manifest.editors.length; i++) {
806
+ const editor = manifest.editors[i];
807
+ try {
808
+ await createEditorDocument(driveId, editor);
809
+ console.log(` โœ“ ${editor.name}`);
810
+ // Add delay between editor creations (except after the last one)
811
+ if (i < manifest.editors.length - 1) {
812
+ await new Promise((resolve) => setTimeout(resolve, 500));
813
+ }
814
+ }
815
+ catch (error) {
816
+ console.log(` โš  Failed to create ${editor.name}: ${error instanceof Error ? error.message : String(error)}`);
817
+ }
818
+ }
819
+ }
820
+ // Create subgraph documents
821
+ if (manifest.subgraphs && manifest.subgraphs.length > 0) {
822
+ console.log(`\n ๐Ÿ“Š Creating ${manifest.subgraphs.length} subgraph documents...`);
823
+ for (const subgraph of manifest.subgraphs) {
824
+ try {
825
+ await createSubgraphDocument(driveId, subgraph);
826
+ console.log(` โœ“ ${subgraph.name}`);
827
+ }
828
+ catch (error) {
829
+ console.log(` โš  Failed to create ${subgraph.name}: ${error instanceof Error ? error.message : String(error)}`);
830
+ }
831
+ }
832
+ }
833
+ // Create processor documents
834
+ if (manifest.processors && manifest.processors.length > 0) {
835
+ console.log(`\n โš™๏ธ Creating ${manifest.processors.length} processor documents...`);
836
+ for (const processor of manifest.processors) {
837
+ try {
838
+ await createProcessorDocument(driveId, processor);
839
+ console.log(` โœ“ ${processor.name}`);
840
+ }
841
+ catch (error) {
842
+ console.log(` โš  Failed to create ${processor.name}: ${error instanceof Error ? error.message : String(error)}`);
843
+ }
844
+ }
845
+ }
846
+ // Create document model documents using drive server sync
847
+ if (manifest.documentModels && manifest.documentModels.length > 0) {
848
+ console.log(`\n ๐Ÿ“‹ Fetching document model specifications from repository...`);
849
+ const documentModelSpecs = await fetchDocumentModelSpecs(repoUrl, manifest.documentModels);
850
+ if (documentModelSpecs.length > 0) {
851
+ console.log(` Found ${documentModelSpecs.length} document model(s) in this repository`);
852
+ console.log(`\n ๐Ÿ“„ Creating document model documents...`);
853
+ for (let i = 0; i < documentModelSpecs.length; i++) {
854
+ const { id, name, spec } = documentModelSpecs[i];
855
+ try {
856
+ await createDocumentModelDocumentViaDriveServer(driveId, name, spec);
857
+ const version = spec.specifications?.[0]?.version || "unknown";
858
+ const modulesCount = spec.specifications?.[0]?.modules?.length || 0;
859
+ const operationsCount = spec.specifications?.[0]?.modules?.reduce((total, mod) => total + (mod.operations?.length || 0), 0) || 0;
860
+ console.log(` โœ“ ${name} (v${version}) - ${modulesCount} modules, ${operationsCount} operations`);
861
+ // Add delay between document model creations (except after the last one)
862
+ if (i < documentModelSpecs.length - 1) {
863
+ console.log(` โฑ Waiting before next document model...`);
864
+ await new Promise((resolve) => setTimeout(resolve, 1500));
865
+ }
866
+ }
867
+ catch (error) {
868
+ console.log(` โš  Failed to create ${name}: ${error instanceof Error ? error.message : String(error)}`);
869
+ }
870
+ }
871
+ }
872
+ else {
873
+ console.log(` โš  No document model files found in this repository`);
874
+ console.log(` Listed in manifest: ${manifest.documentModels
875
+ .map((m) => m.name)
876
+ .join(", ")}`);
877
+ }
878
+ }
879
+ }
880
+ /**
881
+ * Process a single repository
882
+ */
883
+ async function processRepository(repoUrl) {
884
+ console.log(`\n${"=".repeat(80)}`);
885
+ console.log(`Processing: ${repoUrl}`);
886
+ console.log("=".repeat(80));
887
+ try {
888
+ // Step 1: Fetch manifest
889
+ const manifest = await fetchManifest(repoUrl);
890
+ // Step 2: Create drive
891
+ const driveId = await createDrive(manifest, repoUrl);
892
+ // Step 2.5: Register drive with local server to enable processors
893
+ console.log(`\n๐Ÿ”Œ Registering drive to enable processors...`);
894
+ await registerRemoteDrive(driveId);
895
+ // Step 3: Create package document
896
+ const documentId = await createPackageDocument(driveId, manifest, repoUrl);
897
+ // Step 4: Update package document with manifest data
898
+ await updatePackageDocument(driveId, documentId, manifest, repoUrl);
899
+ console.log(`\nโœ… Successfully processed: ${manifest.name}`);
900
+ console.log(` Drive ID: ${driveId}`);
901
+ console.log(` Document ID: ${documentId}`);
902
+ console.log(` Drive URL: http://localhost:4001/d/${driveId}`);
903
+ }
904
+ catch (error) {
905
+ console.error(`\nโŒ Failed to process ${repoUrl}:`, error);
906
+ }
907
+ }
908
+ /**
909
+ * Main function
910
+ */
911
+ async function main() {
912
+ console.log("=== Create Remote Drives from GitHub Repositories ===");
913
+ console.log(`GraphQL Endpoint: ${GRAPHQL_ENDPOINT}`);
914
+ console.log(`Repositories to process: ${GITHUB_REPOS.length}`);
915
+ let successCount = 0;
916
+ let errorCount = 0;
917
+ try {
918
+ for (const repoUrl of GITHUB_REPOS) {
919
+ try {
920
+ await processRepository(repoUrl);
921
+ successCount++;
922
+ }
923
+ catch (error) {
924
+ errorCount++;
925
+ console.error(`Error processing ${repoUrl}:`, error);
926
+ }
927
+ }
928
+ console.log(`\n${"=".repeat(80)}`);
929
+ console.log("=== Summary ===");
930
+ console.log(`Total repositories: ${GITHUB_REPOS.length}`);
931
+ console.log(`โœ“ Success: ${successCount}`);
932
+ console.log(`โœ— Errors: ${errorCount}`);
933
+ console.log("=".repeat(80));
934
+ }
935
+ finally {
936
+ // Cleanup shared drive server
937
+ console.log("\n๐Ÿงน Cleaning up shared drive server...");
938
+ await cleanupSharedDriveServer();
939
+ console.log("โœ“ Cleanup complete");
940
+ }
941
+ if (errorCount > 0) {
942
+ process.exit(1);
943
+ }
944
+ }
945
+ // Run the script
946
+ main().catch(console.error);