gigadrive 1.1.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -24,725 +24,1544 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_commander = require("commander");
27
+ var import_cli8 = require("@effect/cli");
28
+ var import_platform_node = require("@effect/platform-node");
29
+ var import_network_config2 = require("@gigadrive/network-config");
30
+ var import_effect17 = require("effect");
28
31
 
29
32
  // src/commands/build/index.ts
30
- var import_build_utils = require("@gigadrive/build-utils");
31
- var import_fs2 = __toESM(require("fs"), 1);
32
- var import_path2 = __toESM(require("path"), 1);
33
-
34
- // src/util/pm.ts
35
- var import_fs = __toESM(require("fs"), 1);
36
- var import_path = __toESM(require("path"), 1);
33
+ var import_cli = require("@effect/cli");
34
+ var import_platform2 = require("@effect/platform");
35
+ var import_build_utils2 = require("@gigadrive/build-utils");
36
+ var import_effect3 = require("effect");
37
37
 
38
- // ../build-utils/src/exec.ts
39
- var import_child_process = require("child_process");
40
- async function exec({
41
- command,
42
- cwd = process.cwd(),
43
- env,
44
- onOutput = (data) => console.log(data),
45
- onError = (data) => console.error(data)
38
+ // src/errors.ts
39
+ var import_effect = require("effect");
40
+ var OAuthDiscoveryError = class extends import_effect.Schema.TaggedError()("OAuthDiscoveryError", {
41
+ message: import_effect.Schema.String,
42
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
46
43
  }) {
47
- return new Promise((resolve, reject) => {
48
- let spawnCommand;
49
- let spawnArgs = [];
50
- if (typeof command === "string") {
51
- const parts = command.split(" ");
52
- spawnCommand = parts[0];
53
- spawnArgs = parts.slice(1);
54
- } else {
55
- spawnCommand = command.command;
56
- spawnArgs = command.args || [];
57
- }
58
- const childProcess = (0, import_child_process.spawn)(spawnCommand, spawnArgs, {
59
- cwd,
60
- shell: true,
61
- env
62
- });
63
- childProcess.stdout.on("data", (data) => {
64
- onOutput(data.toString().trim());
65
- });
66
- childProcess.stderr.on("data", (data) => {
67
- onError(data.toString().trim());
68
- });
69
- childProcess.on("error", (error2) => {
70
- reject(new Error(`Failed to start process: ${error2.message}`));
71
- });
72
- childProcess.on("close", (code) => {
73
- if (code === 0) {
74
- resolve();
75
- } else {
76
- reject(new Error(`Process exited with code ${code}`));
77
- }
78
- });
79
- });
80
- }
81
-
82
- // src/util/pm.ts
83
- var getPackageManager = async ({ cwd }) => {
84
- const packageJsonPath = import_path.default.join(cwd, "package.json");
85
- if (!import_fs.default.existsSync(packageJsonPath)) {
86
- return null;
87
- }
88
- if (import_fs.default.existsSync(import_path.default.join(cwd, "yarn.lock"))) {
89
- return "yarn";
90
- }
91
- if (import_fs.default.existsSync(import_path.default.join(cwd, "pnpm-lock.yaml"))) {
92
- return "pnpm";
44
+ };
45
+ var AuthStorageReadError = class extends import_effect.Schema.TaggedError()("AuthStorageReadError", {
46
+ message: import_effect.Schema.String
47
+ }) {
48
+ };
49
+ var AuthStorageWriteError = class extends import_effect.Schema.TaggedError()("AuthStorageWriteError", {
50
+ message: import_effect.Schema.String,
51
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
52
+ }) {
53
+ };
54
+ var LoginFlowError = class extends import_effect.Schema.TaggedError()("LoginFlowError", {
55
+ message: import_effect.Schema.String,
56
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
57
+ }) {
58
+ };
59
+ var TokenExchangeError = class extends import_effect.Schema.TaggedError()("TokenExchangeError", {
60
+ message: import_effect.Schema.String,
61
+ statusCode: import_effect.Schema.optional(import_effect.Schema.Number)
62
+ }) {
63
+ };
64
+ var TokenRefreshError = class extends import_effect.Schema.TaggedError()("TokenRefreshError", {
65
+ message: import_effect.Schema.String,
66
+ statusCode: import_effect.Schema.optional(import_effect.Schema.Number)
67
+ }) {
68
+ };
69
+ var NotAuthenticatedError = class extends import_effect.Schema.TaggedError()("NotAuthenticatedError", {
70
+ message: import_effect.Schema.String
71
+ }) {
72
+ };
73
+ var UserInfoFetchError = class extends import_effect.Schema.TaggedError()("UserInfoFetchError", {
74
+ message: import_effect.Schema.String,
75
+ statusCode: import_effect.Schema.optional(import_effect.Schema.Number)
76
+ }) {
77
+ };
78
+ var ConfigNotFoundError = class extends import_effect.Schema.TaggedError()("ConfigNotFoundError", {
79
+ message: import_effect.Schema.String,
80
+ directory: import_effect.Schema.String
81
+ }) {
82
+ };
83
+ var ConfigParseError = class extends import_effect.Schema.TaggedError()("ConfigParseError", {
84
+ message: import_effect.Schema.String,
85
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
86
+ }) {
87
+ };
88
+ var ConfigValidationError = class extends import_effect.Schema.TaggedError()("ConfigValidationError", {
89
+ message: import_effect.Schema.String,
90
+ errors: import_effect.Schema.Array(import_effect.Schema.String)
91
+ }) {
92
+ };
93
+ var PackageJsonNotFoundError = class extends import_effect.Schema.TaggedError()(
94
+ "PackageJsonNotFoundError",
95
+ {
96
+ message: import_effect.Schema.String,
97
+ directory: import_effect.Schema.String
93
98
  }
94
- if (import_fs.default.existsSync(import_path.default.join(cwd, "bun.lockb"))) {
95
- return "bun";
99
+ ) {
100
+ };
101
+ var PackageJsonParseError = class extends import_effect.Schema.TaggedError()("PackageJsonParseError", {
102
+ message: import_effect.Schema.String,
103
+ directory: import_effect.Schema.String,
104
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
105
+ }) {
106
+ };
107
+ var BuildScriptNotFoundError = class extends import_effect.Schema.TaggedError()(
108
+ "BuildScriptNotFoundError",
109
+ {
110
+ message: import_effect.Schema.String
96
111
  }
97
- if (import_fs.default.existsSync(import_path.default.join(cwd, "package-lock.json"))) {
98
- return "npm";
112
+ ) {
113
+ };
114
+ var PackageManagerNotFoundError = class extends import_effect.Schema.TaggedError()(
115
+ "PackageManagerNotFoundError",
116
+ {
117
+ message: import_effect.Schema.String,
118
+ directory: import_effect.Schema.String
99
119
  }
100
- try {
101
- await exec({ command: "bun --version", cwd });
102
- return "bun";
103
- } catch {
104
- return "npm";
120
+ ) {
121
+ };
122
+ var ExecError = class extends import_effect.Schema.TaggedError()("ExecError", {
123
+ message: import_effect.Schema.String,
124
+ command: import_effect.Schema.String,
125
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
126
+ }) {
127
+ };
128
+ var DeploymentCreateError = class extends import_effect.Schema.TaggedError()("DeploymentCreateError", {
129
+ message: import_effect.Schema.String,
130
+ statusCode: import_effect.Schema.optional(import_effect.Schema.Number)
131
+ }) {
132
+ };
133
+ var UploadStartError = class extends import_effect.Schema.TaggedError()("UploadStartError", {
134
+ message: import_effect.Schema.String,
135
+ statusCode: import_effect.Schema.optional(import_effect.Schema.Number)
136
+ }) {
137
+ };
138
+ var PresignedUrlError = class extends import_effect.Schema.TaggedError()("PresignedUrlError", {
139
+ message: import_effect.Schema.String,
140
+ partNumber: import_effect.Schema.Number
141
+ }) {
142
+ };
143
+ var UploadPartError = class extends import_effect.Schema.TaggedError()("UploadPartError", {
144
+ message: import_effect.Schema.String,
145
+ partNumber: import_effect.Schema.Number
146
+ }) {
147
+ };
148
+ var UploadCompleteError = class extends import_effect.Schema.TaggedError()("UploadCompleteError", {
149
+ message: import_effect.Schema.String
150
+ }) {
151
+ };
152
+ var DeploymentStatusError = class extends import_effect.Schema.TaggedError()("DeploymentStatusError", {
153
+ message: import_effect.Schema.String
154
+ }) {
155
+ };
156
+ var DeploymentLogsFetchError = class extends import_effect.Schema.TaggedError()(
157
+ "DeploymentLogsFetchError",
158
+ {
159
+ message: import_effect.Schema.String
105
160
  }
161
+ ) {
162
+ };
163
+ var ArchiveCreateError = class extends import_effect.Schema.TaggedError()("ArchiveCreateError", {
164
+ message: import_effect.Schema.String,
165
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
166
+ }) {
167
+ };
168
+ var DeploymentAuthError = class extends import_effect.Schema.TaggedError()("DeploymentAuthError", {
169
+ message: import_effect.Schema.String
170
+ }) {
106
171
  };
107
- var installCommand = (pm) => {
108
- return {
109
- bun: "bun install",
110
- npm: "npm install",
111
- pnpm: "pnpm install",
112
- yarn: "yarn install"
113
- }[pm] || "npm install";
114
- };
115
- var buildCommand = (pm) => {
116
- return {
117
- bun: "bun run build",
118
- npm: "npm run build",
119
- pnpm: "pnpm build",
120
- yarn: "yarn build"
121
- }[pm] || "npm run build";
172
+ var DeploymentFailedError = class extends import_effect.Schema.TaggedError()("DeploymentFailedError", {
173
+ message: import_effect.Schema.String
174
+ }) {
175
+ };
176
+
177
+ // src/services/package-manager.ts
178
+ var import_platform = require("@effect/platform");
179
+ var import_build_utils = require("@gigadrive/build-utils");
180
+ var import_effect2 = require("effect");
181
+ var PackageManagerService = class extends import_effect2.Effect.Service()("PackageManagerService", {
182
+ accessors: true,
183
+ effect: import_effect2.Effect.gen(function* () {
184
+ const fs = yield* import_platform.FileSystem.FileSystem;
185
+ const pathService = yield* import_platform.Path.Path;
186
+ const detect = import_effect2.Effect.fn("PackageManagerService.detect")(function* (cwd) {
187
+ yield* import_effect2.Effect.annotateCurrentSpan("cwd", cwd);
188
+ const packageJsonPath = pathService.join(cwd, "package.json");
189
+ const exists = yield* fs.exists(packageJsonPath).pipe(
190
+ import_effect2.Effect.mapError(
191
+ () => new PackageManagerNotFoundError({ message: "Failed to check for package.json", directory: cwd })
192
+ )
193
+ );
194
+ if (!exists) {
195
+ return yield* import_effect2.Effect.fail(
196
+ new PackageManagerNotFoundError({
197
+ message: "No package.json found",
198
+ directory: cwd
199
+ })
200
+ );
201
+ }
202
+ const lockChecks = [
203
+ { file: "yarn.lock", pm: "yarn" },
204
+ { file: "pnpm-lock.yaml", pm: "pnpm" },
205
+ { file: "bun.lockb", pm: "bun" },
206
+ { file: "package-lock.json", pm: "npm" }
207
+ ];
208
+ for (const { file, pm: pm2 } of lockChecks) {
209
+ const lockExists = yield* fs.exists(pathService.join(cwd, file)).pipe(import_effect2.Effect.catchAll(() => import_effect2.Effect.succeed(false)));
210
+ if (lockExists) {
211
+ yield* import_effect2.Effect.log("Package manager detected from lock file", { packageManager: pm2, lockFile: file });
212
+ return pm2;
213
+ }
214
+ }
215
+ const bunAvailable = yield* import_effect2.Effect.tryPromise({
216
+ try: () => (0, import_build_utils.exec)({ command: "bun --version", cwd }),
217
+ catch: () => new ExecError({ message: "bun not found", command: "bun --version" })
218
+ }).pipe(
219
+ import_effect2.Effect.as(true),
220
+ import_effect2.Effect.catchTag("ExecError", () => import_effect2.Effect.succeed(false))
221
+ );
222
+ const pm = bunAvailable ? "bun" : "npm";
223
+ yield* import_effect2.Effect.log("Package manager detected via fallback", { packageManager: pm });
224
+ return pm;
225
+ });
226
+ const installCommand = (pm) => ({
227
+ bun: "bun install",
228
+ npm: "npm install",
229
+ pnpm: "pnpm install",
230
+ yarn: "yarn install"
231
+ })[pm];
232
+ const buildCommand2 = (pm) => ({
233
+ bun: "bun run build",
234
+ npm: "npm run build",
235
+ pnpm: "pnpm build",
236
+ yarn: "yarn build"
237
+ })[pm];
238
+ return { detect, installCommand, buildCommand: buildCommand2 };
239
+ })
240
+ }) {
122
241
  };
123
242
 
124
243
  // src/commands/build/index.ts
125
- var build = (parent) => {
126
- parent.command("build").description("Build the current project").action(async () => {
127
- const env = {
244
+ var buildCommand = import_cli.Command.make(
245
+ "build",
246
+ {},
247
+ () => import_effect3.Effect.gen(function* () {
248
+ const pmService = yield* PackageManagerService;
249
+ const fs = yield* import_platform2.FileSystem.FileSystem;
250
+ const pathService = yield* import_platform2.Path.Path;
251
+ const cwd = process.cwd();
252
+ yield* import_effect3.Effect.log("Starting build", { cwd });
253
+ const packageJsonPath = pathService.join(cwd, "package.json");
254
+ const packageJsonExists = yield* fs.exists(packageJsonPath).pipe(
255
+ import_effect3.Effect.mapError(
256
+ () => new PackageJsonNotFoundError({ message: "Failed to check for package.json", directory: cwd })
257
+ )
258
+ );
259
+ if (!packageJsonExists) {
260
+ return yield* import_effect3.Effect.fail(new PackageJsonNotFoundError({ message: "No package.json found", directory: cwd }));
261
+ }
262
+ const packageJsonContent = yield* fs.readFileString(packageJsonPath, "utf-8").pipe(
263
+ import_effect3.Effect.mapError(() => new PackageJsonNotFoundError({ message: "Failed to read package.json", directory: cwd }))
264
+ );
265
+ const packageJson = yield* import_effect3.Effect.try({
266
+ try: () => JSON.parse(packageJsonContent),
267
+ catch: (error) => new PackageJsonParseError({
268
+ message: "Failed to parse package.json",
269
+ directory: cwd,
270
+ cause: error instanceof Error ? error.message : String(error)
271
+ })
272
+ });
273
+ if (!packageJson.scripts?.build) {
274
+ return yield* import_effect3.Effect.fail(new BuildScriptNotFoundError({ message: "No build script found in package.json" }));
275
+ }
276
+ const pm = yield* pmService.detect(cwd);
277
+ const buildEnv = {
128
278
  ...process.env,
129
279
  NODE_ENV: "production",
130
280
  NEBULA: "1",
131
281
  VERCEL: "1",
132
- // Vercel compatibility
133
282
  NOW_BUILDER: "1"
134
283
  };
135
- const cwd = process.cwd();
136
- const packageJsonPath = import_path2.default.join(cwd, "package.json");
137
- if (!import_fs2.default.existsSync(packageJsonPath)) {
138
- console.log("No package.json found");
139
- return;
140
- }
141
- const packageJson = JSON.parse(import_fs2.default.readFileSync(packageJsonPath, "utf-8"));
142
- const buildScript = packageJson.scripts?.build;
143
- if (!buildScript) {
144
- console.log("No build script found in package.json");
145
- return;
146
- }
147
- const packageManager = await getPackageManager({ cwd });
148
- if (!packageManager) {
149
- console.log("No package manager found");
150
- return;
151
- }
152
- await (0, import_build_utils.exec)({
153
- command: installCommand(packageManager),
154
- cwd,
155
- env,
156
- onOutput: (chunk) => {
157
- console.log(chunk);
158
- }
284
+ const installCmd = pmService.installCommand(pm);
285
+ yield* import_effect3.Console.log(`Running: ${installCmd}`);
286
+ yield* import_effect3.Effect.tryPromise({
287
+ try: () => (0, import_build_utils2.exec)({
288
+ command: installCmd,
289
+ cwd,
290
+ env: buildEnv,
291
+ onOutput: (chunk) => {
292
+ import_effect3.Console.log(chunk).pipe(import_effect3.Effect.runSync);
293
+ }
294
+ }),
295
+ catch: (error) => new ExecError({
296
+ message: "Install failed",
297
+ command: installCmd,
298
+ cause: error instanceof Error ? error.message : String(error)
299
+ })
300
+ });
301
+ const buildCmd = pmService.buildCommand(pm);
302
+ yield* import_effect3.Console.log(`Running: ${buildCmd}`);
303
+ yield* import_effect3.Effect.tryPromise({
304
+ try: () => (0, import_build_utils2.exec)({
305
+ command: buildCmd,
306
+ cwd,
307
+ env: buildEnv,
308
+ onOutput: (chunk) => {
309
+ import_effect3.Console.log(chunk).pipe(import_effect3.Effect.runSync);
310
+ }
311
+ }),
312
+ catch: (error) => new ExecError({
313
+ message: "Build failed",
314
+ command: buildCmd,
315
+ cause: error instanceof Error ? error.message : String(error)
316
+ })
159
317
  });
160
- await (0, import_build_utils.exec)({
161
- command: buildCommand(packageManager),
162
- cwd,
163
- env,
164
- onOutput: (chunk) => {
165
- console.log(chunk);
318
+ yield* import_effect3.Console.log("Build completed successfully.");
319
+ }).pipe(
320
+ import_effect3.Effect.catchTags({
321
+ PackageJsonNotFoundError: (err) => import_effect3.Console.error(err.message).pipe(import_effect3.Effect.andThen(import_effect3.Effect.fail(err))),
322
+ PackageJsonParseError: (err) => import_effect3.Console.error(err.message).pipe(import_effect3.Effect.andThen(import_effect3.Effect.fail(err))),
323
+ BuildScriptNotFoundError: (err) => import_effect3.Console.error(err.message).pipe(import_effect3.Effect.andThen(import_effect3.Effect.fail(err))),
324
+ PackageManagerNotFoundError: (err) => import_effect3.Console.error(err.message).pipe(import_effect3.Effect.andThen(import_effect3.Effect.fail(err))),
325
+ ExecError: (err) => import_effect3.Console.error(`${err.message}: ${err.cause ?? err.command}`).pipe(import_effect3.Effect.andThen(import_effect3.Effect.fail(err)))
326
+ })
327
+ )
328
+ );
329
+
330
+ // src/commands/debug/index.ts
331
+ var import_cli3 = require("@effect/cli");
332
+ var import_effect6 = require("effect");
333
+
334
+ // src/commands/debug/config/index.ts
335
+ var import_cli2 = require("@effect/cli");
336
+ var import_effect5 = require("effect");
337
+
338
+ // src/services/project-config.ts
339
+ var import_network_config = require("@gigadrive/network-config");
340
+ var import_effect4 = require("effect");
341
+ var wrapParseErrors = (effect) => effect.pipe(
342
+ import_effect4.Effect.catchTags({
343
+ ConfigFileNotFoundError: (e) => import_effect4.Effect.fail(new ConfigParseError({ message: e.message, cause: e.filePath })),
344
+ ConfigFileEmptyError: (e) => import_effect4.Effect.fail(new ConfigParseError({ message: e.message, cause: e.filePath })),
345
+ ConfigFileParseError: (e) => import_effect4.Effect.fail(new ConfigParseError({ message: e.message, cause: e.cause })),
346
+ ConfigVersionError: (e) => import_effect4.Effect.fail(new ConfigParseError({ message: e.message, cause: e.filePath })),
347
+ ConfigSchemaValidationError: (e) => import_effect4.Effect.fail(new ConfigParseError({ message: e.message, cause: e.validationErrors.join(", ") })),
348
+ FunctionConfigError: (e) => import_effect4.Effect.fail(new ConfigParseError({ message: e.message, cause: e.functionPath }))
349
+ })
350
+ );
351
+ var ProjectConfigService = class extends import_effect4.Effect.Service()("ProjectConfigService", {
352
+ accessors: true,
353
+ dependencies: [import_network_config.NetworkConfigLive],
354
+ effect: import_effect4.Effect.gen(function* () {
355
+ const rawReader = yield* import_network_config.RawConfigReader;
356
+ const resolve = import_effect4.Effect.fn("ProjectConfigService.resolve")(function* (cwd) {
357
+ yield* import_effect4.Effect.annotateCurrentSpan("cwd", cwd);
358
+ yield* import_effect4.Effect.log("Resolving project config", { cwd });
359
+ const configPath = yield* rawReader.findConfig(cwd);
360
+ const detection = yield* (0, import_network_config.detectFramework)(cwd).pipe(
361
+ import_effect4.Effect.catchTag("FrameworkNotDetectedError", () => import_effect4.Effect.succeed(null)),
362
+ import_effect4.Effect.catchTag(
363
+ "ManifestReadError",
364
+ (e) => import_effect4.Effect.fail(new ConfigParseError({ message: `Framework detection failed: ${e.message}`, cause: e.filePath }))
365
+ ),
366
+ import_effect4.Effect.catchTag(
367
+ "ManifestParseError",
368
+ (e) => import_effect4.Effect.fail(new ConfigParseError({ message: `Framework detection failed: ${e.message}`, cause: e.filePath }))
369
+ )
370
+ );
371
+ if (detection) {
372
+ yield* import_effect4.Effect.log("Framework auto-detected", {
373
+ framework: detection.framework.name,
374
+ slug: detection.framework.slug
375
+ });
376
+ }
377
+ let config;
378
+ let resolvedConfigPath = null;
379
+ let framework;
380
+ if (configPath && detection) {
381
+ yield* import_effect4.Effect.log("Config file found, merging with framework defaults", { configPath });
382
+ resolvedConfigPath = configPath;
383
+ const userConfig = yield* wrapParseErrors((0, import_network_config.parseConfigRaw)(configPath, cwd));
384
+ const merged = yield* (0, import_network_config.mergeWithFrameworkDefaults)(userConfig, detection.config);
385
+ config = yield* wrapParseErrors((0, import_network_config.postProcessConfig)(merged, cwd));
386
+ framework = { name: detection.framework.name, slug: detection.framework.slug };
387
+ } else if (configPath) {
388
+ yield* import_effect4.Effect.log("Config file found", { configPath });
389
+ resolvedConfigPath = configPath;
390
+ config = yield* wrapParseErrors((0, import_network_config.parseConfig)(configPath, cwd));
391
+ } else if (detection) {
392
+ config = yield* wrapParseErrors((0, import_network_config.postProcessConfig)(detection.config, cwd));
393
+ framework = { name: detection.framework.name, slug: detection.framework.slug };
394
+ } else {
395
+ return yield* import_effect4.Effect.fail(
396
+ new ConfigNotFoundError({
397
+ message: "No config file found and no framework detected.",
398
+ directory: cwd
399
+ })
400
+ );
401
+ }
402
+ for (const warning of config.warnings) {
403
+ yield* import_effect4.Effect.logWarning(warning);
166
404
  }
405
+ if (config.errors.length > 0) {
406
+ return yield* import_effect4.Effect.fail(
407
+ new ConfigValidationError({
408
+ message: "Config file has validation errors",
409
+ errors: config.errors
410
+ })
411
+ );
412
+ }
413
+ return { config, configPath: resolvedConfigPath, framework };
167
414
  });
168
- });
415
+ return { resolve };
416
+ })
417
+ }) {
169
418
  };
170
419
 
171
420
  // src/commands/debug/config/index.ts
172
- var import_network_config = require("@gigadrive/network-config");
173
- var import_console = require("console");
421
+ var debugConfigCommand = import_cli2.Command.make(
422
+ "config",
423
+ {},
424
+ () => import_effect5.Effect.gen(function* () {
425
+ const projectConfig = yield* ProjectConfigService;
426
+ const cwd = process.cwd();
427
+ const { config, configPath } = yield* projectConfig.resolve(cwd);
428
+ yield* import_effect5.Console.log(`Config file found at: ${configPath}`);
429
+ yield* import_effect5.Console.log(JSON.stringify(config, null, 2));
430
+ }).pipe(
431
+ import_effect5.Effect.catchTags({
432
+ ConfigNotFoundError: (err) => import_effect5.Console.error(err.message),
433
+ ConfigParseError: (err) => import_effect5.Console.error(`Config parse error: ${err.message}`),
434
+ ConfigValidationError: (err) => import_effect5.Effect.gen(function* () {
435
+ for (const e of err.errors) {
436
+ yield* import_effect5.Console.error(e);
437
+ }
438
+ yield* import_effect5.Console.error(err.message);
439
+ })
440
+ })
441
+ )
442
+ );
174
443
 
175
- // src/util/log.ts
176
- var import_consola = require("consola");
177
- var verbose = false;
178
- var consola = (0, import_consola.createConsola)({
179
- level: verbose ? 4 : 3,
180
- // 4 includes debug messages, 3 is the default (info and above)
181
- formatOptions: {
182
- colors: true
183
- }
444
+ // src/commands/debug/index.ts
445
+ var debugBase = import_cli3.Command.make("debug", {}, () => import_effect6.Effect.void);
446
+ var debugCommand = debugBase.pipe(import_cli3.Command.withSubcommands([debugConfigCommand]));
447
+
448
+ // src/commands/login/index.ts
449
+ var import_cli4 = require("@effect/cli");
450
+ var import_effect11 = require("effect");
451
+
452
+ // src/services/auth.ts
453
+ var import_effect10 = require("effect");
454
+ var crypto = __toESM(require("crypto"), 1);
455
+ var http = __toESM(require("http"), 1);
456
+ var import_open = __toESM(require("open"), 1);
457
+
458
+ // src/services/auth-storage.ts
459
+ var import_platform3 = require("@effect/platform");
460
+ var import_effect8 = require("effect");
461
+ var os = __toESM(require("os"), 1);
462
+
463
+ // src/domain.ts
464
+ var import_effect7 = require("effect");
465
+ var DeploymentId = import_effect7.Schema.String.pipe(import_effect7.Schema.brand("@Gigadrive/DeploymentId"));
466
+ var ApplicationId = import_effect7.Schema.String.pipe(import_effect7.Schema.brand("@Gigadrive/ApplicationId"));
467
+ var UploadId = import_effect7.Schema.String.pipe(import_effect7.Schema.brand("@Gigadrive/UploadId"));
468
+ var DeploymentStatus = import_effect7.Schema.Literal("PENDING", "BUILDING", "PROVISIONING", "FAILED", "ACTIVE", "SUSPENDED");
469
+ var DeploymentLogType = import_effect7.Schema.Literal("INFO", "ERROR", "WARN");
470
+ var DeploymentLog = import_effect7.Schema.Struct({
471
+ id: import_effect7.Schema.String,
472
+ message: import_effect7.Schema.String,
473
+ type: DeploymentLogType,
474
+ createdAt: import_effect7.Schema.String
184
475
  });
185
- var updateConsola = () => {
186
- consola.level = verbose ? 4 : 3;
187
- };
188
- var setVerbose = (value) => {
189
- verbose = value;
190
- updateConsola();
191
- };
192
- var log = (message) => {
193
- consola.info(message);
194
- };
195
- var error = (message) => {
196
- consola.error(message);
197
- };
198
- var success = (message) => {
199
- consola.success(message);
476
+ var DeploymentLogPage = import_effect7.Schema.Struct({
477
+ totalItems: import_effect7.Schema.Number,
478
+ limit: import_effect7.Schema.Number,
479
+ offset: import_effect7.Schema.Number,
480
+ items: import_effect7.Schema.Array(DeploymentLog)
481
+ });
482
+ var PackageManager = import_effect7.Schema.Literal("npm", "yarn", "pnpm", "bun");
483
+ var OAuthTokens = import_effect7.Schema.Struct({
484
+ access_token: import_effect7.Schema.String,
485
+ refresh_token: import_effect7.Schema.optional(import_effect7.Schema.String),
486
+ expires_in: import_effect7.Schema.optional(import_effect7.Schema.Number),
487
+ token_type: import_effect7.Schema.optional(import_effect7.Schema.String)
488
+ });
489
+ var OAuthClientConfig = import_effect7.Schema.Struct({
490
+ clientId: import_effect7.Schema.String,
491
+ issuer: import_effect7.Schema.String,
492
+ authorizeUrl: import_effect7.Schema.String,
493
+ tokenUrl: import_effect7.Schema.String,
494
+ scope: import_effect7.Schema.String,
495
+ userinfoUrl: import_effect7.Schema.String
496
+ });
497
+ var StoredAuthData = import_effect7.Schema.Struct({
498
+ refreshToken: import_effect7.Schema.String,
499
+ accessToken: import_effect7.Schema.optional(import_effect7.Schema.String),
500
+ tokenExpirationTime: import_effect7.Schema.optional(import_effect7.Schema.Number)
501
+ });
502
+
503
+ // src/services/auth-storage.ts
504
+ var AuthStorageService = class extends import_effect8.Effect.Service()("AuthStorageService", {
505
+ accessors: true,
506
+ effect: import_effect8.Effect.gen(function* () {
507
+ const fs = yield* import_platform3.FileSystem.FileSystem;
508
+ const pathService = yield* import_platform3.Path.Path;
509
+ const homeDir = os.homedir();
510
+ const authDir = pathService.join(homeDir, ".gigadrive");
511
+ const authFile = pathService.join(authDir, "auth.json");
512
+ const ensureDirectory = import_effect8.Effect.gen(function* () {
513
+ yield* fs.makeDirectory(authDir, { recursive: true }).pipe(
514
+ import_effect8.Effect.mapError(
515
+ (error) => new AuthStorageWriteError({
516
+ message: "Failed to create auth directory",
517
+ cause: String(error)
518
+ })
519
+ )
520
+ );
521
+ yield* fs.chmod(authDir, 448).pipe(
522
+ import_effect8.Effect.mapError(
523
+ (error) => new AuthStorageWriteError({
524
+ message: "Failed to set auth directory permissions",
525
+ cause: String(error)
526
+ })
527
+ )
528
+ );
529
+ });
530
+ const load = import_effect8.Effect.gen(function* () {
531
+ const exists = yield* fs.exists(authFile).pipe(import_effect8.Effect.mapError(() => new AuthStorageReadError({ message: "Failed to check auth file existence" })));
532
+ if (!exists) return import_effect8.Option.none();
533
+ const data = yield* fs.readFileString(authFile, "utf8").pipe(import_effect8.Effect.mapError(() => new AuthStorageReadError({ message: "Failed to read auth file" })));
534
+ const json = yield* import_effect8.Effect.try({
535
+ try: () => JSON.parse(data),
536
+ catch: () => new AuthStorageReadError({ message: "Auth file contains invalid JSON" })
537
+ });
538
+ const authData = yield* import_effect8.Schema.decodeUnknown(StoredAuthData)(json).pipe(
539
+ import_effect8.Effect.mapError(() => new AuthStorageReadError({ message: "Auth file has invalid schema" }))
540
+ );
541
+ return import_effect8.Option.some(authData);
542
+ });
543
+ const save = import_effect8.Effect.fn("AuthStorageService.save")(function* (authData) {
544
+ yield* ensureDirectory;
545
+ yield* fs.writeFileString(authFile, JSON.stringify(authData, null, 2), { mode: 384 }).pipe(
546
+ import_effect8.Effect.mapError(
547
+ (error) => new AuthStorageWriteError({
548
+ message: "Failed to write auth file",
549
+ cause: String(error)
550
+ })
551
+ )
552
+ );
553
+ yield* import_effect8.Effect.log("Auth data saved to local storage");
554
+ });
555
+ const remove = import_effect8.Effect.gen(function* () {
556
+ yield* fs.remove(authFile).pipe(
557
+ import_effect8.Effect.mapError((error) => {
558
+ const cause = error instanceof Error ? error : error.error;
559
+ const code = cause && typeof cause === "object" && "code" in cause ? cause.code : "";
560
+ return { isNotFound: code === "ENOENT" || String(error).includes("ENOENT"), original: error };
561
+ }),
562
+ import_effect8.Effect.catchAll(
563
+ (mapped) => mapped.isNotFound ? import_effect8.Effect.void : import_effect8.Effect.fail(
564
+ new AuthStorageWriteError({
565
+ message: "Failed to remove auth file",
566
+ cause: String(mapped.original)
567
+ })
568
+ )
569
+ )
570
+ );
571
+ yield* import_effect8.Effect.log("Auth data removed from local storage");
572
+ });
573
+ return { load, save, remove };
574
+ })
575
+ }) {
200
576
  };
201
- var warn = (message) => {
202
- consola.warn(message);
577
+
578
+ // src/services/oauth-config.ts
579
+ var import_effect9 = require("effect");
580
+ var OAuthEnvConfig = import_effect9.Config.all({
581
+ issuerUrl: import_effect9.Config.string("GIGADRIVE_NETWORK_OAUTH_ISSUER_URL").pipe(import_effect9.Config.withDefault("https://idp.gigadrive.de")),
582
+ clientId: import_effect9.Config.string("GIGADRIVE_NETWORK_OAUTH_CLIENT_ID").pipe(import_effect9.Config.withDefault("todo_add_client_id"))
583
+ });
584
+ var OpenIdDiscoveryDocument = import_effect9.Schema.Struct({
585
+ issuer: import_effect9.Schema.optional(import_effect9.Schema.String),
586
+ authorization_endpoint: import_effect9.Schema.optional(import_effect9.Schema.String),
587
+ token_endpoint: import_effect9.Schema.optional(import_effect9.Schema.String),
588
+ userinfo_endpoint: import_effect9.Schema.optional(import_effect9.Schema.String)
589
+ });
590
+ var DEFAULT_SCOPE = "offline_access openid profile email";
591
+ var OAuthConfigService = class extends import_effect9.Effect.Service()("OAuthConfigService", {
592
+ accessors: true,
593
+ effect: import_effect9.Effect.gen(function* () {
594
+ const envConfig = yield* OAuthEnvConfig;
595
+ let cached = null;
596
+ const getConfig = import_effect9.Effect.gen(function* () {
597
+ if (cached) return cached;
598
+ const issuerBase = envConfig.issuerUrl.replace(/\/$/, "");
599
+ const discoveryUrl = `${issuerBase}/.well-known/openid-configuration`;
600
+ yield* import_effect9.Effect.log("Fetching OIDC discovery document", { discoveryUrl });
601
+ const response = yield* import_effect9.Effect.tryPromise({
602
+ try: (signal) => fetch(discoveryUrl, { signal }),
603
+ catch: (error) => new OAuthDiscoveryError({
604
+ message: "Failed to fetch OIDC discovery document",
605
+ cause: error instanceof Error ? error.message : String(error)
606
+ })
607
+ });
608
+ if (!response.ok) {
609
+ return yield* import_effect9.Effect.fail(
610
+ new OAuthDiscoveryError({
611
+ message: `OIDC discovery returned ${response.status} ${response.statusText}`
612
+ })
613
+ );
614
+ }
615
+ const json = yield* import_effect9.Effect.tryPromise({
616
+ try: () => response.json(),
617
+ catch: () => new OAuthDiscoveryError({
618
+ message: "Failed to parse OIDC discovery JSON"
619
+ })
620
+ });
621
+ const decoded = yield* import_effect9.Schema.decodeUnknown(OpenIdDiscoveryDocument)(json).pipe(
622
+ import_effect9.Effect.mapError(
623
+ () => new OAuthDiscoveryError({
624
+ message: "Invalid OIDC discovery document schema"
625
+ })
626
+ )
627
+ );
628
+ if (!decoded.authorization_endpoint || !decoded.token_endpoint || !decoded.userinfo_endpoint) {
629
+ return yield* import_effect9.Effect.fail(
630
+ new OAuthDiscoveryError({
631
+ message: "OIDC discovery returned incomplete endpoints"
632
+ })
633
+ );
634
+ }
635
+ const config = {
636
+ clientId: envConfig.clientId,
637
+ issuer: decoded.issuer ?? issuerBase,
638
+ authorizeUrl: decoded.authorization_endpoint,
639
+ tokenUrl: decoded.token_endpoint,
640
+ scope: DEFAULT_SCOPE,
641
+ userinfoUrl: decoded.userinfo_endpoint
642
+ };
643
+ cached = config;
644
+ yield* import_effect9.Effect.log("OIDC discovery completed", { issuer: config.issuer });
645
+ return config;
646
+ });
647
+ return { getConfig };
648
+ })
649
+ }) {
203
650
  };
204
- var debug = (message, ...optionalParams) => {
205
- if (!verbose) {
206
- return;
207
- }
208
- consola.debug("[DEBUG]", message, ...optionalParams);
651
+
652
+ // src/services/auth.ts
653
+ var escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/\//g, "&#x2F;");
654
+ var base64URLEncode = (buffer) => buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
655
+ var generatePKCE = () => {
656
+ const codeVerifier = base64URLEncode(crypto.randomBytes(64));
657
+ const challengeBytes = crypto.createHash("sha256").update(codeVerifier, "utf8").digest();
658
+ const codeChallenge = base64URLEncode(challengeBytes);
659
+ return { codeVerifier, codeChallenge };
209
660
  };
210
- var spinner = (message) => {
211
- log(message);
661
+ var AuthService = class extends import_effect10.Effect.Service()("AuthService", {
662
+ accessors: true,
663
+ dependencies: [OAuthConfigService.Default, AuthStorageService.Default],
664
+ effect: import_effect10.Effect.gen(function* () {
665
+ const oauthConfig = yield* OAuthConfigService;
666
+ const storage = yield* AuthStorageService;
667
+ let accessToken = null;
668
+ let tokenExpirationTime = 0;
669
+ const logout = import_effect10.Effect.gen(function* () {
670
+ accessToken = null;
671
+ tokenExpirationTime = 0;
672
+ yield* storage.remove;
673
+ yield* import_effect10.Effect.log("Logged out");
674
+ });
675
+ const exchangeCodeForTokens = (config, authCode, redirectUri, codeVerifier) => import_effect10.Effect.gen(function* () {
676
+ const response = yield* import_effect10.Effect.tryPromise({
677
+ try: () => fetch(config.tokenUrl, {
678
+ method: "POST",
679
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
680
+ body: new URLSearchParams({
681
+ grant_type: "authorization_code",
682
+ client_id: config.clientId,
683
+ code: authCode,
684
+ redirect_uri: redirectUri,
685
+ code_verifier: codeVerifier
686
+ }).toString()
687
+ }),
688
+ catch: (error) => new TokenExchangeError({
689
+ message: `Token exchange request failed: ${error instanceof Error ? error.message : String(error)}`
690
+ })
691
+ });
692
+ if (!response.ok) {
693
+ const body = yield* import_effect10.Effect.tryPromise({
694
+ try: () => response.text(),
695
+ catch: () => new TokenExchangeError({ message: "Failed to read token exchange error response" })
696
+ });
697
+ return yield* import_effect10.Effect.fail(
698
+ new TokenExchangeError({
699
+ message: `Token exchange failed: ${response.status} ${response.statusText} - ${body}`,
700
+ statusCode: response.status
701
+ })
702
+ );
703
+ }
704
+ const tokens = yield* import_effect10.Effect.tryPromise({
705
+ try: () => response.json(),
706
+ catch: () => new TokenExchangeError({ message: "Failed to parse token exchange response" })
707
+ });
708
+ return tokens;
709
+ });
710
+ const startCallbackServer = (config, codeChallenge, state) => import_effect10.Effect.async((resume) => {
711
+ const server = http.createServer();
712
+ const redirectPath = "/callback";
713
+ server.listen(0, "127.0.0.1", () => {
714
+ const address = server.address();
715
+ if (!address || typeof address !== "object") {
716
+ server.close(() => {
717
+ resume(import_effect10.Effect.fail(new LoginFlowError({ message: "Failed to determine callback server address" })));
718
+ });
719
+ return;
720
+ }
721
+ const port = address.port;
722
+ const redirectUri = `http://127.0.0.1:${port}${redirectPath}`;
723
+ const authUrl = new URL(config.authorizeUrl);
724
+ authUrl.searchParams.append("client_id", config.clientId);
725
+ authUrl.searchParams.append("redirect_uri", redirectUri);
726
+ authUrl.searchParams.append("response_type", "code");
727
+ authUrl.searchParams.append("scope", config.scope);
728
+ authUrl.searchParams.append("code_challenge", codeChallenge);
729
+ authUrl.searchParams.append("code_challenge_method", "S256");
730
+ authUrl.searchParams.append("state", state);
731
+ import_effect10.Console.log(`Please open this URL in your browser to log in:
732
+ ${authUrl.toString()}
733
+ `).pipe(import_effect10.Effect.runSync);
734
+ import_effect10.Console.log("Waiting for login callback...").pipe(import_effect10.Effect.runSync);
735
+ void (0, import_open.default)(authUrl.toString());
736
+ server.on("request", (req, res) => {
737
+ const reqUrl = new URL(req.url || "", redirectUri);
738
+ if (reqUrl.pathname !== "/callback") {
739
+ res.writeHead(404).end("Not Found");
740
+ return;
741
+ }
742
+ const authCode = reqUrl.searchParams.get("code");
743
+ const receivedState = reqUrl.searchParams.get("state");
744
+ const oauthError = reqUrl.searchParams.get("error");
745
+ res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
746
+ if (oauthError) {
747
+ const safeOauthError = escapeHtml(oauthError);
748
+ res.end(`<h1>Login Failed!</h1><p>Error: ${safeOauthError}</p><p>You can close this tab.</p>`);
749
+ server.close();
750
+ resume(import_effect10.Effect.fail(new LoginFlowError({ message: `OAuth error: ${oauthError}` })));
751
+ return;
752
+ }
753
+ if (receivedState !== state) {
754
+ res.end(
755
+ "<h1>Login Failed!</h1><p>State mismatch. Possible CSRF attack.</p><p>You can close this tab.</p>"
756
+ );
757
+ server.close();
758
+ resume(import_effect10.Effect.fail(new LoginFlowError({ message: "State mismatch" })));
759
+ return;
760
+ }
761
+ if (authCode) {
762
+ res.end("<h1>Login Successful!</h1><p>You can close this tab.</p>");
763
+ server.close();
764
+ resume(import_effect10.Effect.succeed({ authCode, redirectUri }));
765
+ } else {
766
+ res.end("<h1>Login Failed!</h1><p>No authorization code received.</p><p>You can close this tab.</p>");
767
+ server.close();
768
+ resume(import_effect10.Effect.fail(new LoginFlowError({ message: "No authorization code received" })));
769
+ }
770
+ });
771
+ server.on("error", (err) => {
772
+ server.close();
773
+ resume(import_effect10.Effect.fail(new LoginFlowError({ message: `Local server error: ${err.message}` })));
774
+ });
775
+ });
776
+ });
777
+ const login = import_effect10.Effect.gen(function* () {
778
+ yield* logout;
779
+ const { codeVerifier, codeChallenge } = generatePKCE();
780
+ const state = crypto.randomBytes(16).toString("hex");
781
+ const config = yield* oauthConfig.getConfig;
782
+ const { authCode, redirectUri } = yield* startCallbackServer(config, codeChallenge, state);
783
+ const tokens = yield* exchangeCodeForTokens(config, authCode, redirectUri, codeVerifier);
784
+ if (!tokens.refresh_token) {
785
+ return yield* import_effect10.Effect.fail(
786
+ new TokenExchangeError({ message: "No refresh token received from identity provider" })
787
+ );
788
+ }
789
+ accessToken = tokens.access_token;
790
+ tokenExpirationTime = Date.now() + (tokens.expires_in || 3600) * 1e3;
791
+ yield* storage.save({
792
+ refreshToken: tokens.refresh_token,
793
+ accessToken,
794
+ tokenExpirationTime
795
+ });
796
+ yield* import_effect10.Effect.log("Login successful");
797
+ return true;
798
+ });
799
+ const refreshAccessToken = import_effect10.Effect.gen(function* () {
800
+ const stored = yield* storage.load.pipe(
801
+ import_effect10.Effect.catchTag("AuthStorageReadError", (err) => import_effect10.Effect.fail(new TokenRefreshError({ message: err.message })))
802
+ );
803
+ const config = yield* oauthConfig.getConfig.pipe(
804
+ import_effect10.Effect.catchTag("OAuthDiscoveryError", (err) => import_effect10.Effect.fail(new TokenRefreshError({ message: err.message })))
805
+ );
806
+ const authData = yield* import_effect10.Option.match(stored, {
807
+ onNone: () => import_effect10.Effect.fail(new TokenRefreshError({ message: "No refresh token available. Please log in." })),
808
+ onSome: (data) => data.refreshToken ? import_effect10.Effect.succeed(data) : import_effect10.Effect.fail(new TokenRefreshError({ message: "No refresh token available. Please log in." }))
809
+ });
810
+ const response = yield* import_effect10.Effect.tryPromise({
811
+ try: () => fetch(config.tokenUrl, {
812
+ method: "POST",
813
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
814
+ body: new URLSearchParams({
815
+ grant_type: "refresh_token",
816
+ client_id: config.clientId,
817
+ refresh_token: authData.refreshToken
818
+ }).toString()
819
+ }),
820
+ catch: (error) => new TokenRefreshError({
821
+ message: `Token refresh request failed: ${error instanceof Error ? error.message : String(error)}`
822
+ })
823
+ });
824
+ if (!response.ok) {
825
+ if (response.status === 400) {
826
+ yield* logout.pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.void));
827
+ return yield* import_effect10.Effect.fail(
828
+ new TokenRefreshError({
829
+ message: "Refresh token is invalid or expired. Please log in again.",
830
+ statusCode: response.status
831
+ })
832
+ );
833
+ }
834
+ const body = yield* import_effect10.Effect.tryPromise({
835
+ try: () => response.text(),
836
+ catch: () => new TokenRefreshError({ message: "Failed to read refresh error response" })
837
+ });
838
+ return yield* import_effect10.Effect.fail(
839
+ new TokenRefreshError({
840
+ message: `Token refresh failed: ${response.status} ${response.statusText} - ${body}`,
841
+ statusCode: response.status
842
+ })
843
+ );
844
+ }
845
+ const tokens = yield* import_effect10.Effect.tryPromise({
846
+ try: () => response.json(),
847
+ catch: () => new TokenRefreshError({ message: "Failed to parse token refresh response" })
848
+ });
849
+ accessToken = tokens.access_token;
850
+ tokenExpirationTime = Date.now() + (tokens.expires_in || 3600) * 1e3;
851
+ const newRefreshToken = tokens.refresh_token || authData.refreshToken;
852
+ yield* storage.save({
853
+ refreshToken: newRefreshToken,
854
+ accessToken,
855
+ tokenExpirationTime
856
+ }).pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.void));
857
+ yield* import_effect10.Effect.log("Access token refreshed successfully");
858
+ return true;
859
+ });
860
+ const getAccessToken = import_effect10.Effect.gen(function* () {
861
+ if (accessToken && Date.now() < tokenExpirationTime) {
862
+ return accessToken;
863
+ }
864
+ const stored = yield* storage.load.pipe(
865
+ import_effect10.Effect.catchAll(() => import_effect10.Effect.succeed(import_effect10.Option.none()))
866
+ );
867
+ import_effect10.Option.match(stored, {
868
+ onNone: () => {
869
+ },
870
+ onSome: (data) => {
871
+ if (data.accessToken && data.tokenExpirationTime && Date.now() < data.tokenExpirationTime) {
872
+ accessToken = data.accessToken;
873
+ tokenExpirationTime = data.tokenExpirationTime;
874
+ }
875
+ }
876
+ });
877
+ if (accessToken && Date.now() < tokenExpirationTime) {
878
+ return accessToken;
879
+ }
880
+ yield* import_effect10.Effect.log("Access token expired or not available. Attempting to refresh...");
881
+ yield* refreshAccessToken.pipe(
882
+ import_effect10.Effect.catchTag("TokenRefreshError", (err) => import_effect10.Effect.fail(new NotAuthenticatedError({ message: err.message })))
883
+ );
884
+ if (!accessToken) {
885
+ return yield* import_effect10.Effect.fail(
886
+ new NotAuthenticatedError({ message: "No valid access token available. Please log in first." })
887
+ );
888
+ }
889
+ return accessToken;
890
+ });
891
+ const getUserInfo = import_effect10.Effect.gen(function* () {
892
+ const config = yield* oauthConfig.getConfig;
893
+ const token = yield* getAccessToken;
894
+ const fetchUserInfo = (t) => import_effect10.Effect.tryPromise({
895
+ try: () => fetch(config.userinfoUrl, {
896
+ headers: {
897
+ Authorization: `Bearer ${t}`,
898
+ Accept: "application/json"
899
+ }
900
+ }),
901
+ catch: (error) => new UserInfoFetchError({
902
+ message: `Failed to fetch user info: ${error instanceof Error ? error.message : String(error)}`
903
+ })
904
+ });
905
+ let response = yield* fetchUserInfo(token);
906
+ if (response.status === 401 || response.status === 403) {
907
+ yield* refreshAccessToken.pipe(
908
+ import_effect10.Effect.catchTag("TokenRefreshError", (err) => import_effect10.Effect.fail(new UserInfoFetchError({ message: err.message })))
909
+ );
910
+ if (!accessToken) {
911
+ return yield* import_effect10.Effect.fail(new UserInfoFetchError({ message: "Token refresh failed" }));
912
+ }
913
+ response = yield* fetchUserInfo(accessToken);
914
+ }
915
+ if (!response.ok) {
916
+ const body = yield* import_effect10.Effect.tryPromise({
917
+ try: () => response.text(),
918
+ catch: () => new UserInfoFetchError({ message: "Failed to read user info error response" })
919
+ });
920
+ return yield* import_effect10.Effect.fail(
921
+ new UserInfoFetchError({
922
+ message: `Failed to fetch user info: ${response.status} ${response.statusText}${body ? ` - ${body}` : ""}`,
923
+ statusCode: response.status
924
+ })
925
+ );
926
+ }
927
+ const data = yield* import_effect10.Effect.tryPromise({
928
+ try: () => response.json(),
929
+ catch: () => new UserInfoFetchError({ message: "Failed to parse user info response" })
930
+ });
931
+ return data;
932
+ });
933
+ const inferUserName = (info) => {
934
+ const isNonEmptyString = (value) => typeof value === "string" && value.trim() !== "";
935
+ const name = info.name;
936
+ const givenName = info.given_name;
937
+ const familyName = info.family_name;
938
+ const preferredUsername = info.preferred_username;
939
+ const email = info.email;
940
+ return isNonEmptyString(name) && name || (isNonEmptyString(givenName) && isNonEmptyString(familyName) ? `${givenName} ${familyName}` : void 0) || isNonEmptyString(preferredUsername) && preferredUsername || isNonEmptyString(email) && email || "your account";
941
+ };
942
+ return { login, logout, getAccessToken, getUserInfo, refreshAccessToken, inferUserName };
943
+ })
944
+ }) {
212
945
  };
213
946
 
214
- // src/commands/debug/config/index.ts
215
- var config = (parent) => {
216
- parent.command("config").description("Provide debug info about the project configuration in the current directory").action(async () => {
217
- const configPath = (0, import_network_config.findConfig)(process.cwd());
218
- if (!configPath) {
219
- debug("No config file found");
220
- throw new Error("The current project folder does not have a valid config file.");
221
- }
222
- const config2 = await (0, import_network_config.parseConfig)(configPath, process.cwd());
223
- (0, import_console.log)(`Config file found at: ${configPath}`);
224
- (0, import_console.log)(JSON.stringify(config2, null, 2));
225
- });
226
- };
947
+ // src/commands/login/index.ts
948
+ var loginCommand = import_cli4.Command.make(
949
+ "login",
950
+ {},
951
+ () => import_effect11.Effect.gen(function* () {
952
+ yield* import_effect11.Console.log("Initiating login...");
953
+ const auth = yield* AuthService;
954
+ yield* auth.login;
955
+ const info = yield* auth.getUserInfo.pipe(import_effect11.Effect.catchAll(() => import_effect11.Effect.succeed({})));
956
+ const name = auth.inferUserName(info);
957
+ yield* import_effect11.Console.log(`You are now logged in as ${name}.`);
958
+ }).pipe(
959
+ import_effect11.Effect.catchTags({
960
+ LoginFlowError: (err) => import_effect11.Console.error(`Login failed: ${err.message}`),
961
+ TokenExchangeError: (err) => import_effect11.Console.error(`Login failed: ${err.message}`),
962
+ OAuthDiscoveryError: (err) => import_effect11.Console.error(`Login failed: ${err.message}`),
963
+ AuthStorageWriteError: (err) => import_effect11.Console.error(`Failed to save credentials: ${err.message}`)
964
+ })
965
+ )
966
+ );
227
967
 
228
- // src/commands/debug/index.ts
229
- var debug2 = (program) => {
230
- const debug3 = program.command("debug").description("Provides various debugging utilities");
231
- config(debug3);
232
- };
968
+ // src/commands/platform/index.ts
969
+ var import_cli6 = require("@effect/cli");
970
+ var import_effect15 = require("effect");
233
971
 
234
972
  // src/commands/platform/deploy/index.ts
235
- var import_commons = require("@gigadrive/commons");
236
- var import_network_config2 = require("@gigadrive/network-config");
237
- var import_fs4 = __toESM(require("fs"), 1);
238
- var import_os = __toESM(require("os"), 1);
239
- var import_path4 = __toESM(require("path"), 1);
973
+ var import_cli5 = require("@effect/cli");
974
+ var import_platform5 = require("@effect/platform");
975
+ var import_commons2 = require("@gigadrive/commons");
976
+ var import_effect14 = require("effect");
977
+ var os2 = __toESM(require("os"), 1);
240
978
 
241
- // src/util/zip.ts
242
- var import_build_utils2 = require("@gigadrive/build-utils");
979
+ // src/services/archive.ts
980
+ var import_platform4 = require("@effect/platform");
981
+ var import_build_utils3 = require("@gigadrive/build-utils");
243
982
  var import_archiver = __toESM(require("archiver"), 1);
244
- var import_fs3 = __toESM(require("fs"), 1);
245
- var import_promises = __toESM(require("fs/promises"), 1);
983
+ var import_effect12 = require("effect");
246
984
  var import_ignore = __toESM(require("ignore"), 1);
247
- var import_path3 = __toESM(require("path"), 1);
248
- var ignoreFileNames = [
249
- /*'.gitignore', */
250
- ".gigadriveignore",
251
- ".vercelignore",
252
- ".dockerignore",
253
- ".nowignore"
254
- ];
255
- async function readIgnoreFile(ignorePath) {
256
- try {
257
- await import_promises.default.access(ignorePath);
258
- const content = await import_promises.default.readFile(ignorePath, "utf8");
259
- return content.split("\n").filter((line) => line.trim() !== "");
260
- } catch {
261
- return [];
262
- }
263
- }
264
- async function collectIgnorePatterns(dir, baseDir, ignoreRules) {
265
- const items = await import_promises.default.readdir(dir);
985
+ var nodeFs = __toESM(require("fs"), 1);
986
+ var nodeFsPromises = __toESM(require("fs/promises"), 1);
987
+ var IGNORE_FILE_NAMES = [".gigadriveignore", ".vercelignore", ".dockerignore", ".nowignore"];
988
+ var readIgnoreFile = (fs, ignorePath) => fs.readFileString(ignorePath, "utf8").pipe(
989
+ import_effect12.Effect.map((content) => content.split("\n").filter((line) => line.trim() !== "")),
990
+ import_effect12.Effect.catchAll(() => import_effect12.Effect.succeed([]))
991
+ );
992
+ var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) => import_effect12.Effect.gen(function* () {
993
+ const items = yield* fs.readDirectory(dir).pipe(import_effect12.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${dir}` })));
266
994
  for (const item of items) {
267
- const fullPath = import_path3.default.join(dir, item);
268
- const relativePath = import_path3.default.relative(baseDir, fullPath);
269
- const stat = await import_promises.default.lstat(fullPath);
995
+ const fullPath = pathService.join(dir, item);
996
+ const relativePath = pathService.relative(baseDir, fullPath);
997
+ const stat = yield* import_effect12.Effect.tryPromise({
998
+ try: () => nodeFsPromises.lstat(fullPath),
999
+ catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
1000
+ });
270
1001
  if (stat.isDirectory()) {
271
- if (ignoreRules.ignores(`${relativePath}/`)) {
272
- continue;
273
- }
274
- for (const ignoreFileName of ignoreFileNames) {
275
- const ignoreFilePath = import_path3.default.join(fullPath, ignoreFileName);
276
- try {
277
- await import_promises.default.access(ignoreFilePath);
278
- const patterns = (await readIgnoreFile(ignoreFilePath)).map((pattern) => {
279
- return pattern.startsWith("!") ? `!${import_path3.default.join(relativePath, pattern.slice(1))}` : import_path3.default.join(relativePath, pattern);
280
- });
281
- ignoreRules.add(patterns);
282
- } catch {
1002
+ if (ignoreRules.ignores(`${relativePath}/`)) continue;
1003
+ for (const ignoreFileName of IGNORE_FILE_NAMES) {
1004
+ const ignoreFilePath = pathService.join(fullPath, ignoreFileName);
1005
+ const patterns = yield* readIgnoreFile(fs, ignoreFilePath);
1006
+ if (patterns.length > 0) {
1007
+ ignoreRules.add(
1008
+ patterns.map(
1009
+ (pattern) => pattern.startsWith("!") ? `!${pathService.join(relativePath, pattern.slice(1))}` : pathService.join(relativePath, pattern)
1010
+ )
1011
+ );
283
1012
  }
284
1013
  }
285
- await collectIgnorePatterns(fullPath, baseDir, ignoreRules);
1014
+ yield* collectIgnorePatterns(fs, pathService, fullPath, baseDir, ignoreRules);
286
1015
  }
287
1016
  }
288
- }
289
- async function initializeIgnoreRules(baseDir) {
1017
+ }).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.void));
1018
+ var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect12.Effect.gen(function* () {
290
1019
  const ignoreRules = (0, import_ignore.default)();
291
1020
  ignoreRules.add([".git/", "**/.DS_Store", "**/Thumbs.db"]);
292
- for (const ignoreFileName of ignoreFileNames) {
293
- const ignoreFilePath = import_path3.default.join(baseDir, ignoreFileName);
294
- try {
295
- await import_promises.default.access(ignoreFilePath);
296
- const patterns = (await readIgnoreFile(ignoreFilePath)).map((pattern) => {
297
- return pattern.startsWith("!") ? `!${pattern.slice(1)}` : pattern;
298
- });
299
- ignoreRules.add(patterns);
300
- } catch {
1021
+ for (const ignoreFileName of IGNORE_FILE_NAMES) {
1022
+ const ignoreFilePath = pathService.join(baseDir, ignoreFileName);
1023
+ const patterns = yield* readIgnoreFile(fs, ignoreFilePath);
1024
+ if (patterns.length > 0) {
1025
+ ignoreRules.add(
1026
+ patterns.map((pattern) => pattern.startsWith("!") ? `!${pattern.slice(1)}` : pattern)
1027
+ );
301
1028
  }
302
1029
  }
303
- await collectIgnorePatterns(baseDir, baseDir, ignoreRules);
1030
+ yield* collectIgnorePatterns(fs, pathService, baseDir, baseDir, ignoreRules);
304
1031
  return ignoreRules;
305
- }
306
- async function getFilesToInclude(dir, ignoreRules, whitelist) {
1032
+ });
1033
+ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect12.Effect.gen(function* () {
307
1034
  const filesToInclude = [];
308
- async function walkDir(currentDir) {
309
- const items = await import_promises.default.readdir(currentDir);
1035
+ const walkDir = (currentDir) => import_effect12.Effect.gen(function* () {
1036
+ const items = yield* fs.readDirectory(currentDir).pipe(import_effect12.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${currentDir}` })));
310
1037
  for (const item of items) {
311
- const fullPath = import_path3.default.join(currentDir, item);
312
- const relativePath = import_path3.default.relative(dir, fullPath);
313
- const stat = await import_promises.default.lstat(fullPath);
1038
+ const fullPath = pathService.join(currentDir, item);
1039
+ const relativePath = pathService.relative(dir, fullPath);
1040
+ const stat = yield* import_effect12.Effect.tryPromise({
1041
+ try: () => nodeFsPromises.lstat(fullPath),
1042
+ catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
1043
+ });
314
1044
  if (stat.isDirectory()) {
315
- if (ignoreRules.ignores(`${relativePath}/`)) {
316
- continue;
1045
+ if (!ignoreRules.ignores(`${relativePath}/`)) {
1046
+ yield* walkDir(fullPath);
317
1047
  }
318
- await walkDir(fullPath);
319
1048
  } else {
320
1049
  if (!ignoreRules.ignores(relativePath)) {
321
1050
  filesToInclude.push(fullPath);
322
- } else if (ignoreRules.ignores(relativePath) && ignoreRules.test(relativePath).unignored) {
323
- filesToInclude.push(fullPath);
324
1051
  }
325
1052
  }
326
1053
  }
327
- }
328
- await walkDir(dir);
1054
+ }).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.void));
1055
+ yield* walkDir(dir);
329
1056
  return filesToInclude;
330
- }
331
- async function createZipArchive(inputDir, outputFile, options = {}) {
332
- const ignoreRules = options.useIgnoreFiles !== false ? await initializeIgnoreRules(inputDir) : (0, import_ignore.default)();
333
- const filesToInclude = options.whitelist ? await Promise.all(options.whitelist.map((file) => (0, import_build_utils2.getFilesForPattern)(inputDir, file))) : await getFilesToInclude(inputDir, ignoreRules);
334
- const output = import_fs3.default.createWriteStream(outputFile);
335
- const archive = (0, import_archiver.default)("zip", { zlib: { level: 9 } });
336
- return new Promise((resolve, reject) => {
337
- output.on("close", () => resolve());
338
- archive.on("error", reject);
339
- archive.pipe(output);
340
- for (const file of filesToInclude.flat()) {
341
- archive.file(file, { name: import_path3.default.relative(inputDir, file) });
342
- }
343
- void archive.finalize();
344
- });
345
- }
346
-
347
- // src/commands/platform/deploy/index.ts
348
- var deploy = (parent) => {
349
- parent.command("deploy").description("Deploy the current project").action(async () => {
350
- debug("Starting deploy command");
351
- const configPath = (0, import_network_config2.findConfig)(process.cwd());
352
- if (!configPath) {
353
- debug("No config file found");
354
- throw new Error("The current project folder does not have a valid config file.");
355
- }
356
- debug(`Found config at: ${configPath}`);
357
- const config2 = await (0, import_network_config2.parseConfig)(configPath, process.cwd());
358
- debug("Parsed config", config2);
359
- if (config2.warnings.length > 0) {
360
- debug(`Found ${config2.warnings.length} warnings in config`);
361
- config2.warnings.forEach((message) => {
362
- warn(message);
1057
+ });
1058
+ var ArchiveService = class extends import_effect12.Effect.Service()("ArchiveService", {
1059
+ accessors: true,
1060
+ effect: import_effect12.Effect.gen(function* () {
1061
+ const fs = yield* import_platform4.FileSystem.FileSystem;
1062
+ const pathService = yield* import_platform4.Path.Path;
1063
+ const createZipArchive = import_effect12.Effect.fn("ArchiveService.createZipArchive")(function* (inputDir, outputFile, options = {}) {
1064
+ yield* import_effect12.Effect.annotateCurrentSpan("inputDir", inputDir);
1065
+ yield* import_effect12.Effect.annotateCurrentSpan("outputFile", outputFile);
1066
+ yield* import_effect12.Effect.log("Creating zip archive", { inputDir, outputFile });
1067
+ const ignoreRules = options.useIgnoreFiles !== false ? yield* initializeIgnoreRules(fs, pathService, inputDir) : (0, import_ignore.default)();
1068
+ const filesToInclude = options.whitelist ? yield* import_effect12.Effect.tryPromise({
1069
+ try: () => Promise.all(
1070
+ options.whitelist.map((file) => (0, import_build_utils3.getFilesForPattern)(file, inputDir, options.excludeFiles ?? []))
1071
+ ),
1072
+ catch: (error) => new ArchiveCreateError({
1073
+ message: "Failed to resolve whitelist patterns",
1074
+ cause: error instanceof Error ? error.message : String(error)
1075
+ })
1076
+ }) : yield* import_effect12.Effect.gen(function* () {
1077
+ if (options.excludeFiles && options.excludeFiles.length > 0) {
1078
+ ignoreRules.add(options.excludeFiles);
1079
+ }
1080
+ return yield* getFilesToInclude(fs, pathService, inputDir, ignoreRules).pipe(
1081
+ import_effect12.Effect.map((files) => [files])
1082
+ );
363
1083
  });
364
- }
365
- if (config2.errors.length > 0) {
366
- debug(`Found ${config2.errors.length} errors in config`);
367
- config2.errors.forEach((message) => {
368
- error(message);
1084
+ const archiveExists = yield* fs.exists(outputFile).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.succeed(false)));
1085
+ if (archiveExists) {
1086
+ yield* fs.remove(outputFile).pipe(
1087
+ import_effect12.Effect.mapError(
1088
+ (error) => new ArchiveCreateError({
1089
+ message: `Failed to remove existing archive: ${String(error)}`
1090
+ })
1091
+ )
1092
+ );
1093
+ }
1094
+ yield* import_effect12.Effect.tryPromise({
1095
+ try: () => new Promise((resolve, reject) => {
1096
+ const output = nodeFs.createWriteStream(outputFile);
1097
+ const archive = (0, import_archiver.default)("zip", { zlib: { level: 9 } });
1098
+ output.on("close", () => resolve());
1099
+ output.on("error", reject);
1100
+ archive.on("error", reject);
1101
+ archive.pipe(output);
1102
+ for (const file of filesToInclude.flat()) {
1103
+ archive.file(file, { name: pathService.relative(inputDir, file) });
1104
+ }
1105
+ void archive.finalize();
1106
+ }),
1107
+ catch: (error) => new ArchiveCreateError({
1108
+ message: "Failed to create zip archive",
1109
+ cause: error instanceof Error ? error.message : String(error)
1110
+ })
369
1111
  });
370
- throw new Error("The current project folder does not have a valid config file.");
371
- }
372
- debug("Creating deployment");
373
- const deploymentId = await createDeployment({
374
- applicationId: "test"
375
- // TODO
1112
+ const fileInfo = yield* fs.stat(outputFile).pipe(
1113
+ import_effect12.Effect.mapError(
1114
+ (error) => new ArchiveCreateError({
1115
+ message: `Failed to stat archive: ${String(error)}`
1116
+ })
1117
+ )
1118
+ );
1119
+ const size = Number(fileInfo.size);
1120
+ yield* import_effect12.Effect.log("Archive created", { size });
1121
+ return { path: outputFile, size };
376
1122
  });
377
- debug(`Deployment ID: ${deploymentId}`);
378
- debug("Starting deployment process");
379
- await handleDeployment(deploymentId, config2);
380
- });
381
- };
382
- var handleDeployment = async (deploymentId, config2) => {
383
- debug("Deployment process initialized");
384
- let status = "PENDING";
385
- const logs = [];
386
- addLog("Deployment ID: " + deploymentId, "INFO", logs);
387
- try {
388
- debug("Starting upload process");
389
- await uploadArchive(deploymentId, logs, config2);
390
- debug("Upload completed successfully");
391
- addLog("Upload complete.", "INFO", logs);
392
- addLog("The deployment pipeline is now being provisioned. This may take a few seconds.", "INFO", logs);
393
- } catch (err) {
394
- debug(`Upload failed with error: ${err.message}`);
395
- addLog(`Upload failed: ${err.message}`, "ERROR", logs);
396
- status = "FAILED";
397
- error("The deployment failed. Please check the logs for more information.");
398
- process.exit(1);
399
- }
400
- const statusInterval = setInterval(() => {
401
- void (async () => {
402
- try {
403
- debug(`Fetching deployment status for ${deploymentId}`);
404
- const newStatus = await getDeploymentStatus(deploymentId);
405
- debug(`Current status: ${status}, New status: ${newStatus}`);
406
- if (newStatus === "ACTIVE" || newStatus === "FAILED") {
407
- debug(`Final status reached: ${newStatus}, clearing interval`);
408
- clearInterval(statusInterval);
409
- clearInterval(logsInterval);
410
- if (newStatus === "ACTIVE") {
411
- success(`Deployed to https://${deploymentId}.gigadrivedev.com`);
412
- } else {
413
- error("The deployment failed. Please check the logs for more information.");
414
- }
415
- return;
416
- }
417
- if (newStatus !== status) {
418
- debug(`Updating status from ${status} to ${newStatus}`);
419
- status = newStatus;
420
- spinner(`Status: ${status.toLowerCase().replace(/^\w/, (c) => c.toUpperCase())}`);
421
- }
422
- } catch (err) {
423
- debug(`Error fetching status: ${err.message}`);
1123
+ return { createZipArchive };
1124
+ })
1125
+ }) {
1126
+ };
1127
+
1128
+ // src/services/deployment-api.ts
1129
+ var import_commons = require("@gigadrive/commons");
1130
+ var import_effect13 = require("effect");
1131
+ var CreateDeploymentResponse = import_effect13.Schema.Struct({ id: import_effect13.Schema.String });
1132
+ var StartUploadResponse = import_effect13.Schema.Struct({ uploadId: import_effect13.Schema.String });
1133
+ var PresignedUrlResponse = import_effect13.Schema.Struct({ url: import_effect13.Schema.String });
1134
+ var DeploymentStatusResponse = import_effect13.Schema.Struct({
1135
+ status: import_effect13.Schema.Literal("PENDING", "BUILDING", "PROVISIONING", "FAILED", "ACTIVE", "SUSPENDED")
1136
+ });
1137
+ var DeploymentLogPageResponse = import_effect13.Schema.Struct({
1138
+ totalItems: import_effect13.Schema.Number,
1139
+ limit: import_effect13.Schema.Number,
1140
+ offset: import_effect13.Schema.Number,
1141
+ items: import_effect13.Schema.Array(
1142
+ import_effect13.Schema.Struct({
1143
+ id: import_effect13.Schema.String,
1144
+ message: import_effect13.Schema.String,
1145
+ type: import_effect13.Schema.Literal("INFO", "ERROR", "WARN"),
1146
+ createdAt: import_effect13.Schema.String
1147
+ })
1148
+ )
1149
+ });
1150
+ var ApiBaseUrl = import_effect13.Config.string("GIGADRIVE_API_BASE_URL").pipe(import_effect13.Config.withDefault("http://localhost:3000"));
1151
+ var DeploymentApiService = class extends import_effect13.Effect.Service()("DeploymentApiService", {
1152
+ accessors: true,
1153
+ effect: import_effect13.Effect.gen(function* () {
1154
+ const baseUrl = yield* ApiBaseUrl;
1155
+ const authService = yield* AuthService;
1156
+ const getAuthHeaders = import_effect13.Effect.gen(function* () {
1157
+ const token = yield* authService.getAccessToken.pipe(
1158
+ import_effect13.Effect.mapError((err) => new DeploymentAuthError({ message: `Authentication failed: ${err.message}` }))
1159
+ );
1160
+ return { Authorization: `Bearer ${token}` };
1161
+ });
1162
+ const createDeployment = import_effect13.Effect.fn("DeploymentApiService.createDeployment")(function* (applicationId) {
1163
+ yield* import_effect13.Effect.annotateCurrentSpan("applicationId", applicationId);
1164
+ yield* import_effect13.Effect.log("Creating deployment", { applicationId });
1165
+ const authHeaders = yield* getAuthHeaders;
1166
+ const response = yield* import_effect13.Effect.tryPromise({
1167
+ try: () => fetch(`${baseUrl}/${applicationId}/deployments`, {
1168
+ method: "POST",
1169
+ headers: { "Content-Type": "application/json", ...authHeaders },
1170
+ body: JSON.stringify({})
1171
+ }),
1172
+ catch: (error) => new DeploymentCreateError({
1173
+ message: `Failed to create deployment: ${error instanceof Error ? error.message : String(error)}`
1174
+ })
1175
+ });
1176
+ if (!response.ok) {
1177
+ const body = yield* import_effect13.Effect.tryPromise({
1178
+ try: () => response.text(),
1179
+ catch: () => new DeploymentCreateError({
1180
+ message: "Failed to read deployment creation error",
1181
+ statusCode: response.status
1182
+ })
1183
+ });
1184
+ return yield* import_effect13.Effect.fail(
1185
+ new DeploymentCreateError({
1186
+ message: `Failed to create deployment: ${body}`,
1187
+ statusCode: response.status
1188
+ })
1189
+ );
424
1190
  }
425
- })();
426
- }, 1e3);
427
- const logsInterval = setInterval(() => {
428
- void (async () => {
429
- try {
430
- debug(`Fetching logs for ${deploymentId}, current log count: ${logs.length}`);
431
- const newLogs = await getLogs(deploymentId, {
432
- offset: logs.length,
433
- limit: 100,
434
- "createdAt[gt]": logs[logs.length - 1]?.createdAt
1191
+ const json = yield* import_effect13.Effect.tryPromise({
1192
+ try: () => response.json(),
1193
+ catch: () => new DeploymentCreateError({ message: "Failed to parse deployment response" })
1194
+ });
1195
+ const decoded = yield* import_effect13.Schema.decodeUnknown(CreateDeploymentResponse)(json).pipe(
1196
+ import_effect13.Effect.mapError(() => new DeploymentCreateError({ message: "Invalid deployment response schema" }))
1197
+ );
1198
+ yield* import_effect13.Effect.log("Deployment created", { deploymentId: decoded.id });
1199
+ return decoded.id;
1200
+ });
1201
+ const startMultipartUpload = import_effect13.Effect.fn("DeploymentApiService.startMultipartUpload")(function* (deploymentId) {
1202
+ yield* import_effect13.Effect.annotateCurrentSpan("deploymentId", deploymentId);
1203
+ const authHeaders = yield* getAuthHeaders;
1204
+ const response = yield* import_effect13.Effect.tryPromise({
1205
+ try: () => fetch(`${baseUrl}/deployments/${deploymentId}/pre-signed-url/start`, {
1206
+ method: "GET",
1207
+ headers: { "Content-Type": "application/json", ...authHeaders }
1208
+ }),
1209
+ catch: (error) => new UploadStartError({
1210
+ message: `Failed to start multipart upload: ${error instanceof Error ? error.message : String(error)}`
1211
+ })
1212
+ });
1213
+ if (!response.ok) {
1214
+ const body = yield* import_effect13.Effect.tryPromise({
1215
+ try: () => response.text(),
1216
+ catch: () => new UploadStartError({ message: "Failed to read upload start error" })
435
1217
  });
436
- debug(`Fetched ${newLogs.items.length} new logs`);
437
- for (const logEntry of newLogs.items) {
438
- logs.push(logEntry);
439
- if (logEntry.type === "ERROR") {
440
- error(logEntry.message);
441
- } else if (logEntry.type === "WARN") {
442
- warn(logEntry.message);
443
- } else {
444
- log(logEntry.message);
1218
+ return yield* import_effect13.Effect.fail(
1219
+ new UploadStartError({ message: `Failed to start multipart upload: ${body}`, statusCode: response.status })
1220
+ );
1221
+ }
1222
+ const json = yield* import_effect13.Effect.tryPromise({
1223
+ try: () => response.json(),
1224
+ catch: () => new UploadStartError({ message: "Failed to parse upload start response" })
1225
+ });
1226
+ const decoded = yield* import_effect13.Schema.decodeUnknown(StartUploadResponse)(json).pipe(
1227
+ import_effect13.Effect.mapError(() => new UploadStartError({ message: "Invalid upload start response schema" }))
1228
+ );
1229
+ return decoded.uploadId;
1230
+ });
1231
+ const getPresignedUrl = import_effect13.Effect.fn("DeploymentApiService.getPresignedUrl")(function* (deploymentId, uploadId, partNumber) {
1232
+ const authHeaders = yield* getAuthHeaders;
1233
+ const response = yield* import_effect13.Effect.tryPromise({
1234
+ try: () => fetch(
1235
+ `${baseUrl}/deployments/${deploymentId}/pre-signed-url/part?uploadId=${uploadId}&partNumber=${partNumber}`,
1236
+ {
1237
+ method: "GET",
1238
+ headers: { "Content-Type": "application/json", ...authHeaders }
445
1239
  }
446
- }
447
- } catch (err) {
448
- debug(`Error fetching logs: ${err.message}`);
1240
+ ),
1241
+ catch: (error) => new PresignedUrlError({
1242
+ message: `Failed to get presigned URL: ${error instanceof Error ? error.message : String(error)}`,
1243
+ partNumber
1244
+ })
1245
+ });
1246
+ if (!response.ok) {
1247
+ const body = yield* import_effect13.Effect.tryPromise({
1248
+ try: () => response.text(),
1249
+ catch: () => new PresignedUrlError({ message: "Failed to read presigned URL error", partNumber })
1250
+ });
1251
+ return yield* import_effect13.Effect.fail(
1252
+ new PresignedUrlError({ message: `Failed to get presigned URL: ${body}`, partNumber })
1253
+ );
449
1254
  }
450
- })();
451
- }, 1e3);
452
- spinner(`Status: ${status.toLowerCase().replace(/^\w/, (c) => c.toUpperCase())}`);
453
- };
454
- var addLog = (message, type, logs) => {
455
- debug(`Adding log: [${type}] ${message}`);
456
- logs.push({
457
- id: String(logs.length),
458
- message,
459
- type,
460
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
461
- });
462
- if (type === "ERROR") {
463
- error(message);
464
- } else if (type === "WARN") {
465
- warn(message);
466
- } else {
467
- log(message);
468
- }
469
- };
470
- var uploadArchive = async (deploymentId, logs, config2) => {
471
- debug("Starting uploadArchive");
472
- addLog("Creating archive...", "INFO", logs);
473
- const archivePath = import_path4.default.join(process.env.TEMP || import_os.default.tmpdir(), `project-${Date.now()}.zip`);
474
- debug(`Archive path: ${archivePath}`);
475
- if (import_fs4.default.existsSync(archivePath)) {
476
- debug(`Archive already exists, removing: ${archivePath}`);
477
- import_fs4.default.unlinkSync(archivePath);
478
- }
479
- debug("Creating zip archive");
480
- await createZipArchive(config2.userArchive?.rootOverwrite || process.cwd(), archivePath, {
481
- whitelist: config2.userArchive?.fileWhitelist,
482
- useIgnoreFiles: false,
483
- useManagedIgnore: false
484
- });
485
- const fileStats = import_fs4.default.statSync(archivePath);
486
- const fileSize = fileStats.size;
487
- debug(`Archive created, size: ${fileSize} bytes (${(0, import_commons.formatFileSize)(fileSize)})`);
488
- addLog(`Archive created (${(0, import_commons.formatFileSize)(fileSize)})`, "INFO", logs);
489
- addLog("Uploading archive...", "INFO", logs);
490
- debug(`Starting multipart upload for deployment: ${deploymentId}`);
491
- const uploadId = await startMultipartUpload(deploymentId);
492
- debug(`Upload ID: ${uploadId}`);
493
- debug("Uploading archive...");
494
- const fileChunkSize = 10 * 1024 * 1024;
495
- const numChunks = Math.ceil(fileSize / fileChunkSize);
496
- debug(`File size: ${fileSize}, chunk size: ${fileChunkSize}, number of chunks: ${numChunks}`);
497
- const promises = [];
498
- let start, end;
499
- for (let index = 1; index < numChunks + 1; index++) {
500
- start = (index - 1) * fileChunkSize;
501
- end = index * fileChunkSize;
502
- debug(`Processing chunk ${index}/${numChunks}, start: ${start}, end: ${end}`);
503
- const presignedUrl = await getPresignedUrl(deploymentId, uploadId, index);
504
- debug("Presigned URL for part", index, presignedUrl);
505
- const promise = new Promise((resolve, reject) => {
506
- void (async () => {
507
- try {
508
- debug(`Reading chunk ${index} data`);
509
- const readStream = import_fs4.default.createReadStream(archivePath, {
510
- start,
511
- end: index < numChunks ? end - 1 : void 0
512
- });
513
- const chunks = [];
514
- readStream.on("data", (chunk) => {
515
- chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
516
- });
517
- await new Promise((resolveRead) => {
518
- readStream.on("end", () => resolveRead());
519
- readStream.on("error", reject);
520
- });
521
- const buffer = Buffer.concat(chunks);
522
- debug(`Uploading chunk ${index} to presigned URL (${buffer.length} bytes)`);
523
- const response = await fetch(presignedUrl, {
524
- method: "PUT",
525
- headers: {
526
- "Content-Type": "application/zip",
527
- "Content-Length": String(buffer.length)
528
- },
529
- body: buffer
530
- });
531
- resolve(response);
532
- } catch (err) {
533
- reject(err instanceof Error ? err : new Error(String(err)));
534
- }
535
- })();
1255
+ const json = yield* import_effect13.Effect.tryPromise({
1256
+ try: () => response.json(),
1257
+ catch: () => new PresignedUrlError({ message: "Failed to parse presigned URL response", partNumber })
1258
+ });
1259
+ const decoded = yield* import_effect13.Schema.decodeUnknown(PresignedUrlResponse)(json).pipe(
1260
+ import_effect13.Effect.mapError(() => new PresignedUrlError({ message: "Invalid presigned URL response schema", partNumber }))
1261
+ );
1262
+ return decoded.url;
536
1263
  });
537
- promises.push(promise);
538
- }
539
- const finishedParts = [];
540
- try {
541
- debug(`Waiting for all ${promises.length} upload promises to complete`);
542
- const responses = await Promise.all(promises);
543
- debug("All upload promises completed");
544
- for (let index = 0; index < responses.length; index++) {
545
- const res = responses[index];
546
- debug(`Checking response for part ${index + 1}, status: ${res.status}`);
547
- if (!res.ok) {
548
- debug(`Failed to upload part ${index + 1}: ${res.statusText} ${await res.text()}`);
549
- throw new Error(`Failed to upload part ${index + 1}: ${res.statusText}`);
1264
+ const uploadPart = import_effect13.Effect.fn("DeploymentApiService.uploadPart")(function* (presignedUrl, data, partNumber) {
1265
+ yield* import_effect13.Effect.annotateCurrentSpan("partNumber", partNumber);
1266
+ yield* import_effect13.Effect.annotateCurrentSpan("size", data.length);
1267
+ const response = yield* import_effect13.Effect.tryPromise({
1268
+ try: () => fetch(presignedUrl, {
1269
+ method: "PUT",
1270
+ headers: {
1271
+ "Content-Type": "application/zip",
1272
+ "Content-Length": String(data.length)
1273
+ },
1274
+ body: data
1275
+ }),
1276
+ catch: (error) => new UploadPartError({
1277
+ message: `Failed to upload part ${partNumber}: ${error instanceof Error ? error.message : String(error)}`,
1278
+ partNumber
1279
+ })
1280
+ });
1281
+ if (!response.ok) {
1282
+ return yield* import_effect13.Effect.fail(
1283
+ new UploadPartError({
1284
+ message: `Failed to upload part ${partNumber}: ${response.statusText}`,
1285
+ partNumber
1286
+ })
1287
+ );
550
1288
  }
551
- const etag = res.headers.get("ETag");
1289
+ const etag = response.headers.get("ETag");
552
1290
  if (!etag) {
553
- debug(`Failed to get ETag for part ${index + 1}`);
554
- throw new Error(`Failed to get ETag for part ${index + 1}`);
1291
+ return yield* import_effect13.Effect.fail(
1292
+ new UploadPartError({ message: `No ETag received for part ${partNumber}`, partNumber })
1293
+ );
555
1294
  }
556
- debug(`Part ${index + 1} uploaded successfully, ETag: ${etag}`);
557
- finishedParts.push({
558
- partNumber: index + 1,
559
- etag
1295
+ return { partNumber, etag };
1296
+ });
1297
+ const completeUpload = import_effect13.Effect.fn("DeploymentApiService.completeUpload")(function* (deploymentId, uploadId, parts) {
1298
+ yield* import_effect13.Effect.annotateCurrentSpan("deploymentId", deploymentId);
1299
+ yield* import_effect13.Effect.annotateCurrentSpan("partsCount", parts.length);
1300
+ const authHeaders = yield* getAuthHeaders;
1301
+ const response = yield* import_effect13.Effect.tryPromise({
1302
+ try: () => fetch(`${baseUrl}/deployments/${deploymentId}/pre-signed-url/complete`, {
1303
+ method: "POST",
1304
+ headers: { "Content-Type": "application/json", ...authHeaders },
1305
+ body: JSON.stringify({ uploadId, parts })
1306
+ }),
1307
+ catch: (error) => new UploadCompleteError({
1308
+ message: `Failed to complete upload: ${error instanceof Error ? error.message : String(error)}`
1309
+ })
560
1310
  });
561
- }
562
- debug(`Completing upload with ${finishedParts.length} parts`);
563
- await completeUpload(deploymentId, uploadId, finishedParts);
564
- debug("Upload completed successfully");
565
- } catch (err) {
566
- const error2 = err;
567
- debug(`Upload error: ${error2.message}`);
568
- addLog(`Upload error: ${error2.message}`, "ERROR", logs);
569
- throw error2;
570
- }
571
- };
572
- var createDeployment = async ({ applicationId }) => {
573
- debug(`Creating deployment for application: ${applicationId}`);
574
- const res = await fetch(`http://localhost:3000/${applicationId}/deployments`, {
575
- method: "POST",
576
- headers: {
577
- "Content-Type": "application/json"
578
- },
579
- body: JSON.stringify({}),
580
- duplex: "half"
581
- });
582
- if (!res.ok) {
583
- const errorText = await res.text();
584
- debug(`Failed to create deployment: ${errorText}`);
585
- throw new Error("Failed to create deployment: " + errorText);
586
- }
587
- const deployment = await res.json();
588
- debug(`Deployment created with ID: ${deployment.id}`);
589
- return deployment.id;
590
- };
591
- var startMultipartUpload = async (deploymentId) => {
592
- debug(`Starting multipart upload for deployment: ${deploymentId}`);
593
- const res = await fetch(`http://localhost:3000/deployments/${deploymentId}/pre-signed-url/start`, {
594
- method: "GET",
595
- headers: {
596
- "Content-Type": "application/json"
597
- }
598
- });
599
- if (!res.ok) {
600
- const errorText = await res.text();
601
- debug(`Failed to start multipart upload: ${errorText}`);
602
- throw new Error("Failed to start multipart upload: " + errorText);
603
- }
604
- const upload = await res.json();
605
- debug(`Multipart upload started with ID: ${upload.uploadId}`);
606
- return upload.uploadId;
607
- };
608
- var getPresignedUrl = async (deploymentId, uploadId, partNumber) => {
609
- debug(`Getting presigned URL for deployment: ${deploymentId}, upload: ${uploadId}, part: ${partNumber}`);
610
- const res = await fetch(
611
- `http://localhost:3000/deployments/${deploymentId}/pre-signed-url/part?uploadId=${uploadId}&partNumber=${partNumber}`,
612
- {
613
- method: "GET",
614
- headers: {
615
- "Content-Type": "application/json"
1311
+ if (!response.ok) {
1312
+ const body = yield* import_effect13.Effect.tryPromise({
1313
+ try: () => response.text(),
1314
+ catch: () => new UploadCompleteError({ message: "Failed to read upload complete error" })
1315
+ });
1316
+ return yield* import_effect13.Effect.fail(new UploadCompleteError({ message: `Failed to complete upload: ${body}` }));
616
1317
  }
617
- }
1318
+ yield* import_effect13.Effect.log("Upload completed successfully");
1319
+ });
1320
+ const getDeploymentStatus = import_effect13.Effect.fn("DeploymentApiService.getDeploymentStatus")(function* (deploymentId) {
1321
+ const authHeaders = yield* getAuthHeaders;
1322
+ const response = yield* import_effect13.Effect.tryPromise({
1323
+ try: () => fetch(`${baseUrl}/deployments/${deploymentId}`, {
1324
+ method: "GET",
1325
+ headers: { "Content-Type": "application/json", ...authHeaders }
1326
+ }),
1327
+ catch: (error) => new DeploymentStatusError({
1328
+ message: `Failed to get deployment status: ${error instanceof Error ? error.message : String(error)}`
1329
+ })
1330
+ });
1331
+ if (!response.ok) {
1332
+ const body = yield* import_effect13.Effect.tryPromise({
1333
+ try: () => response.text(),
1334
+ catch: () => new DeploymentStatusError({ message: "Failed to read deployment status error" })
1335
+ });
1336
+ return yield* import_effect13.Effect.fail(new DeploymentStatusError({ message: `Failed to get deployment status: ${body}` }));
1337
+ }
1338
+ const json = yield* import_effect13.Effect.tryPromise({
1339
+ try: () => response.json(),
1340
+ catch: () => new DeploymentStatusError({ message: "Failed to parse deployment status response" })
1341
+ });
1342
+ const decoded = yield* import_effect13.Schema.decodeUnknown(DeploymentStatusResponse)(json).pipe(
1343
+ import_effect13.Effect.mapError(() => new DeploymentStatusError({ message: "Invalid deployment status response schema" }))
1344
+ );
1345
+ return decoded.status;
1346
+ });
1347
+ const getLogs = import_effect13.Effect.fn("DeploymentApiService.getLogs")(function* (deploymentId, options) {
1348
+ const authHeaders = yield* getAuthHeaders;
1349
+ const response = yield* import_effect13.Effect.tryPromise({
1350
+ try: () => fetch(`${baseUrl}/deployments/${deploymentId}/logs${(0, import_commons.objectToQueryString)(options)}`, {
1351
+ method: "GET",
1352
+ headers: { "Content-Type": "application/json", ...authHeaders }
1353
+ }),
1354
+ catch: (error) => new DeploymentLogsFetchError({
1355
+ message: `Failed to get logs: ${error instanceof Error ? error.message : String(error)}`
1356
+ })
1357
+ });
1358
+ if (!response.ok) {
1359
+ const body = yield* import_effect13.Effect.tryPromise({
1360
+ try: () => response.text(),
1361
+ catch: () => new DeploymentLogsFetchError({ message: "Failed to read logs error" })
1362
+ });
1363
+ return yield* import_effect13.Effect.fail(new DeploymentLogsFetchError({ message: `Failed to get logs: ${body}` }));
1364
+ }
1365
+ const json = yield* import_effect13.Effect.tryPromise({
1366
+ try: () => response.json(),
1367
+ catch: () => new DeploymentLogsFetchError({ message: "Failed to parse logs response" })
1368
+ });
1369
+ const decoded = yield* import_effect13.Schema.decodeUnknown(DeploymentLogPageResponse)(json).pipe(
1370
+ import_effect13.Effect.mapError(() => new DeploymentLogsFetchError({ message: "Invalid logs response schema" }))
1371
+ );
1372
+ return decoded;
1373
+ });
1374
+ return {
1375
+ createDeployment,
1376
+ startMultipartUpload,
1377
+ getPresignedUrl,
1378
+ uploadPart,
1379
+ completeUpload,
1380
+ getDeploymentStatus,
1381
+ getLogs
1382
+ };
1383
+ })
1384
+ }) {
1385
+ };
1386
+
1387
+ // src/commands/platform/deploy/index.ts
1388
+ var deployCommand = import_cli5.Command.make(
1389
+ "deploy",
1390
+ {},
1391
+ () => import_effect14.Effect.gen(function* () {
1392
+ const projectConfig = yield* ProjectConfigService;
1393
+ const deploymentApi = yield* DeploymentApiService;
1394
+ const archiveService = yield* ArchiveService;
1395
+ const pathService = yield* import_platform5.Path.Path;
1396
+ const cwd = process.cwd();
1397
+ yield* import_effect14.Effect.log("Starting deploy command");
1398
+ const { config } = yield* projectConfig.resolve(cwd);
1399
+ yield* import_effect14.Console.log("Creating deployment...");
1400
+ const deploymentId = yield* deploymentApi.createDeployment("test");
1401
+ yield* import_effect14.Console.log(`Deployment ID: ${deploymentId}`);
1402
+ yield* import_effect14.Console.log("Creating archive...");
1403
+ const archivePath = pathService.join(os2.tmpdir(), `project-${Date.now()}.zip`);
1404
+ const archive = yield* archiveService.createZipArchive(config.userArchive?.rootOverwrite || cwd, archivePath, {
1405
+ whitelist: config.userArchive?.fileWhitelist,
1406
+ excludeFiles: config.excludeFiles,
1407
+ useIgnoreFiles: false,
1408
+ useManagedIgnore: false
1409
+ });
1410
+ yield* import_effect14.Console.log(`Archive created (${(0, import_commons2.formatFileSize)(archive.size)})`);
1411
+ yield* import_effect14.Console.log("Uploading archive...");
1412
+ yield* uploadArchive(deploymentId, archivePath, archive.size);
1413
+ yield* import_effect14.Console.log("Upload complete.");
1414
+ yield* import_effect14.Console.log("The deployment pipeline is now being provisioned. This may take a few seconds.");
1415
+ yield* pollDeployment(deploymentId);
1416
+ }).pipe(
1417
+ import_effect14.Effect.catchTags({
1418
+ ConfigNotFoundError: (err) => import_effect14.Console.error(err.message).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1419
+ ConfigParseError: (err) => import_effect14.Console.error(`Config parse error: ${err.message}`).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1420
+ ConfigValidationError: (err) => import_effect14.Effect.gen(function* () {
1421
+ for (const e of err.errors) {
1422
+ yield* import_effect14.Console.error(e);
1423
+ }
1424
+ yield* import_effect14.Console.error(err.message);
1425
+ }).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1426
+ DeploymentCreateError: (err) => import_effect14.Console.error(`Deployment creation failed: ${err.message}`).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1427
+ ArchiveCreateError: (err) => import_effect14.Console.error(`Archive creation failed: ${err.message}`).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1428
+ UploadStartError: (err) => import_effect14.Console.error(`Upload failed: ${err.message}`).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1429
+ UploadPartError: (err) => import_effect14.Console.error(`Upload failed: ${err.message}`).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1430
+ UploadCompleteError: (err) => import_effect14.Console.error(`Upload failed: ${err.message}`).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1431
+ PresignedUrlError: (err) => import_effect14.Console.error(`Upload failed: ${err.message}`).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1432
+ DeploymentAuthError: (err) => import_effect14.Console.error(`Authentication failed: ${err.message}`).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err))),
1433
+ DeploymentFailedError: (err) => import_effect14.Console.error(err.message).pipe(import_effect14.Effect.andThen(import_effect14.Effect.fail(err)))
1434
+ })
1435
+ )
1436
+ );
1437
+ var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect14.Effect.gen(function* () {
1438
+ const deploymentApi = yield* DeploymentApiService;
1439
+ const fs = yield* import_platform5.FileSystem.FileSystem;
1440
+ const uploadId = yield* deploymentApi.startMultipartUpload(deploymentId);
1441
+ const FILE_CHUNK_SIZE = 10 * 1024 * 1024;
1442
+ const numChunks = Math.ceil(fileSize / FILE_CHUNK_SIZE);
1443
+ yield* import_effect14.Effect.log("Starting multipart upload", { uploadId, numChunks, fileSize });
1444
+ const parts = yield* import_effect14.Effect.all(
1445
+ Array.from({ length: numChunks }, (_, i) => {
1446
+ const partNumber = i + 1;
1447
+ const start = i * FILE_CHUNK_SIZE;
1448
+ const bytesToRead = partNumber < numChunks ? FILE_CHUNK_SIZE : fileSize - start;
1449
+ return import_effect14.Effect.gen(function* () {
1450
+ const presignedUrl = yield* deploymentApi.getPresignedUrl(deploymentId, uploadId, partNumber);
1451
+ const chunkData = yield* import_effect14.Stream.runCollect(fs.stream(archivePath, { offset: start, bytesToRead })).pipe(
1452
+ import_effect14.Effect.map((chunks) => {
1453
+ const arrays = Array.from(chunks);
1454
+ return Buffer.concat(arrays);
1455
+ }),
1456
+ import_effect14.Effect.mapError(
1457
+ (error) => new UploadPartError({
1458
+ message: `Failed to read chunk ${partNumber}: ${String(error)}`,
1459
+ partNumber
1460
+ })
1461
+ )
1462
+ );
1463
+ yield* import_effect14.Effect.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
1464
+ return yield* deploymentApi.uploadPart(presignedUrl, chunkData, partNumber);
1465
+ });
1466
+ }),
1467
+ { concurrency: 3 }
618
1468
  );
619
- if (!res.ok) {
620
- const errorText = await res.text();
621
- debug(`Failed to get pre-signed URL: ${errorText}`);
622
- throw new Error("Failed to get pre-signed URL: " + errorText);
623
- }
624
- const upload = await res.json();
625
- debug(`Got presigned URL for part ${partNumber}`);
626
- return upload.url;
627
- };
628
- var completeUpload = async (deploymentId, uploadId, parts) => {
629
- debug(`Completing upload for deployment: ${deploymentId}, upload: ${uploadId}, parts: ${parts.length}`);
630
- const res = await fetch(`http://localhost:3000/deployments/${deploymentId}/pre-signed-url/complete`, {
631
- method: "POST",
632
- headers: {
633
- "Content-Type": "application/json"
634
- },
635
- body: JSON.stringify({
636
- uploadId,
637
- parts
1469
+ yield* deploymentApi.completeUpload(deploymentId, uploadId, parts);
1470
+ });
1471
+ var pollDeployment = (deploymentId) => import_effect14.Effect.gen(function* () {
1472
+ const deploymentApi = yield* DeploymentApiService;
1473
+ let lastStatus = "PENDING";
1474
+ let logOffset = 0;
1475
+ yield* import_effect14.Console.log("Status: Pending");
1476
+ yield* import_effect14.Effect.repeat(
1477
+ import_effect14.Effect.gen(function* () {
1478
+ const newStatus = yield* deploymentApi.getDeploymentStatus(deploymentId).pipe(import_effect14.Effect.catchTag("DeploymentStatusError", () => import_effect14.Effect.succeed(lastStatus)));
1479
+ if (newStatus !== lastStatus) {
1480
+ lastStatus = newStatus;
1481
+ const label = newStatus.charAt(0) + newStatus.slice(1).toLowerCase();
1482
+ yield* import_effect14.Console.log(`Status: ${label}`);
1483
+ }
1484
+ const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(import_effect14.Effect.catchTag("DeploymentLogsFetchError", () => import_effect14.Effect.succeed(null)));
1485
+ if (logsPage) {
1486
+ for (const logEntry of logsPage.items) {
1487
+ logOffset++;
1488
+ yield* printLog(logEntry);
1489
+ }
1490
+ }
1491
+ if (newStatus === "ACTIVE") {
1492
+ yield* import_effect14.Console.log(`Deployed to https://${deploymentId}.gigadrivedev.com`);
1493
+ return yield* import_effect14.Effect.fail("done");
1494
+ }
1495
+ if (newStatus === "FAILED") {
1496
+ return yield* import_effect14.Effect.fail(
1497
+ new DeploymentFailedError({
1498
+ message: "The deployment failed. Please check the logs for more information."
1499
+ })
1500
+ );
1501
+ }
638
1502
  }),
639
- duplex: "half"
640
- });
641
- if (!res.ok) {
642
- const errorText = await res.text();
643
- debug(`Failed to complete upload: ${errorText}`);
644
- throw new Error("Failed to complete upload: " + errorText);
645
- }
646
- debug("Upload completed successfully");
647
- };
648
- var getDeploymentStatus = async (deploymentId) => {
649
- debug(`Getting deployment status for: ${deploymentId}`);
650
- const res = await fetch(`http://localhost:3000/deployments/${deploymentId}`, {
651
- method: "GET",
652
- headers: {
653
- "Content-Type": "application/json"
654
- }
655
- });
656
- if (!res.ok) {
657
- const errorText = await res.text();
658
- debug(`Failed to get deployment status: ${errorText}`);
659
- throw new Error("Failed to get deployment status: " + errorText);
660
- }
661
- const deployment = await res.json();
662
- debug(`Deployment status: ${deployment.status}`);
663
- return deployment.status;
664
- };
665
- var getLogs = async (deploymentId, options) => {
666
- debug(`Getting logs for deployment: ${deploymentId}, options:`, options ?? "No options");
667
- const res = await fetch(`http://localhost:3000/deployments/${deploymentId}/logs${(0, import_commons.objectToQueryString)(options)}`, {
668
- method: "GET",
669
- headers: {
670
- "Content-Type": "application/json"
671
- }
672
- });
673
- if (!res.ok) {
674
- const errorText = await res.text();
675
- debug(`Failed to get logs: ${errorText}`);
676
- throw new Error("Failed to get logs: " + errorText);
1503
+ import_effect14.Schedule.addDelay(import_effect14.Schedule.forever, () => import_effect14.Duration.seconds(1))
1504
+ ).pipe(
1505
+ import_effect14.Effect.catchAll((err) => {
1506
+ if (err === "done") return import_effect14.Effect.void;
1507
+ return import_effect14.Effect.fail(err);
1508
+ })
1509
+ );
1510
+ });
1511
+ var printLog = (logEntry) => {
1512
+ switch (logEntry.type) {
1513
+ case "ERROR":
1514
+ return import_effect14.Console.error(logEntry.message);
1515
+ case "WARN":
1516
+ return import_effect14.Console.warn(logEntry.message);
1517
+ default:
1518
+ return import_effect14.Console.log(logEntry.message);
677
1519
  }
678
- const logs = await res.json();
679
- debug(`Got ${logs.items.length} logs, total: ${logs.totalItems}`);
680
- return logs;
681
1520
  };
682
1521
 
683
1522
  // src/commands/platform/index.ts
684
- var platform = (program) => {
685
- const platform2 = program.command("platform").description("Manage the platform");
686
- deploy(platform2);
687
- };
1523
+ var platformBase = import_cli6.Command.make("platform", {}, () => import_effect15.Effect.void);
1524
+ var platformCommand = platformBase.pipe(import_cli6.Command.withSubcommands([deployCommand]));
1525
+
1526
+ // src/commands/whoami/index.ts
1527
+ var import_cli7 = require("@effect/cli");
1528
+ var import_effect16 = require("effect");
1529
+ var whoamiCommand = import_cli7.Command.make(
1530
+ "whoami",
1531
+ {},
1532
+ () => import_effect16.Effect.gen(function* () {
1533
+ const auth = yield* AuthService;
1534
+ const info = yield* auth.getUserInfo;
1535
+ yield* import_effect16.Console.log(JSON.stringify(info, null, 2));
1536
+ }).pipe(
1537
+ import_effect16.Effect.catchTags({
1538
+ NotAuthenticatedError: () => import_effect16.Console.log('You are not logged in. Run "gigadrive login" to authenticate.'),
1539
+ UserInfoFetchError: (err) => import_effect16.Console.error(`Failed to retrieve user info: ${err.message}`),
1540
+ OAuthDiscoveryError: (err) => import_effect16.Console.error(`Failed to retrieve user info: ${err.message}`)
1541
+ })
1542
+ )
1543
+ );
688
1544
 
689
1545
  // src/index.ts
690
- var { isTTY } = process.stdout;
691
- var main = async () => {
692
- if (process.env.FORCE_TTY === "1") {
693
- isTTY = true;
694
- process.stdout.isTTY = true;
695
- process.stdin.isTTY = true;
696
- }
697
- let exitCode = 0;
698
- const program = new import_commander.Command();
699
- program.option("-v, --verbose", "Enable verbose logging", false);
700
- program.hook("preSubcommand", (thisCommand) => {
701
- setVerbose(thisCommand.opts().verbose === true);
702
- if (thisCommand.opts().verbose) {
703
- log("Using verbose mode");
704
- }
705
- });
706
- program.hook("preAction", (thisCommand) => {
707
- setVerbose(thisCommand.opts().verbose === true);
708
- });
709
- platform(program);
710
- debug2(program);
711
- build(program);
712
- try {
713
- await program.parseAsync(process.argv);
714
- } catch (err) {
715
- error(err);
716
- exitCode = 1;
717
- }
718
- return exitCode;
719
- };
720
- var handleRejection = (err) => {
721
- if (err) {
722
- if (err instanceof Error) {
723
- handleUnexpected(err);
724
- } else {
725
- console.error(`An unexpected rejection occurred
726
- ${JSON.stringify(err)}`);
727
- }
728
- } else {
729
- console.error("An unexpected empty rejection occurred");
730
- }
731
- process.exit(1);
732
- };
733
- var handleUnexpected = (error2) => {
734
- const { message } = error2;
735
- console.error(`An unexpected error occurred
736
- ${message}`);
737
- process.exit(1);
738
- };
739
- process.on("unhandledRejection", handleRejection);
740
- process.on("uncaughtException", handleUnexpected);
741
- process.on("exit", () => {
742
- const terminal = process.stderr.isTTY ? process.stderr : process.stdout.isTTY ? process.stdout : void 0;
743
- terminal?.write("\x1B[?25h");
1546
+ var gigadrive = import_cli8.Command.make("gigadrive", {}, () => import_effect17.Effect.void).pipe(
1547
+ import_cli8.Command.withSubcommands([loginCommand, whoamiCommand, buildCommand, debugCommand, platformCommand])
1548
+ );
1549
+ var cli = import_cli8.Command.run(gigadrive, {
1550
+ name: "Gigadrive CLI",
1551
+ version: "2.0.0"
744
1552
  });
745
- main().then((exitCode) => {
746
- process.exitCode = exitCode;
747
- }).catch(handleUnexpected);
1553
+ var BaseServices = import_effect17.Layer.mergeAll(
1554
+ OAuthConfigService.Default,
1555
+ AuthStorageService.Default,
1556
+ PackageManagerService.Default,
1557
+ ArchiveService.Default,
1558
+ import_network_config2.NetworkConfigLive,
1559
+ ProjectConfigService.Default
1560
+ ).pipe(import_effect17.Layer.provideMerge(AuthService.Default));
1561
+ var DeploymentApiLive = import_effect17.Layer.provide(DeploymentApiService.Default, BaseServices);
1562
+ var ServicesLive = import_effect17.Layer.mergeAll(BaseServices, DeploymentApiLive);
1563
+ var AppLive = import_effect17.Layer.mergeAll(ServicesLive, import_effect17.Logger.minimumLogLevel(import_effect17.LogLevel.Info)).pipe(
1564
+ import_effect17.Layer.provideMerge(import_platform_node.NodeContext.layer)
1565
+ );
1566
+ import_platform_node.NodeRuntime.runMain(import_effect17.Effect.provide(cli(process.argv), AppLive));
748
1567
  //# sourceMappingURL=index.cjs.map