gigadrive 2.1.3 → 2.2.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,16 +24,68 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_cli8 = require("@effect/cli");
27
+ var import_cli14 = require("@effect/cli");
28
28
  var import_platform_node = require("@effect/platform-node");
29
29
  var import_network_config2 = require("@gigadrive/network-config");
30
- var import_effect17 = require("effect");
30
+ var import_effect25 = require("effect");
31
31
 
32
- // src/commands/build/index.ts
32
+ // package.json
33
+ var package_default = {
34
+ name: "gigadrive",
35
+ version: "2.2.0",
36
+ repository: {
37
+ type: "git",
38
+ url: "https://github.com/Gigadrive/sdk.git",
39
+ directory: "packages/cli"
40
+ },
41
+ license: "Apache-2.0",
42
+ author: "Gigadrive <business@gigadrivegroup.com> (https://gigadrive.de)",
43
+ type: "module",
44
+ module: "src/index.ts",
45
+ bin: {
46
+ gigadrive: "dist/index.mjs",
47
+ gn: "dist/index.mjs"
48
+ },
49
+ files: [
50
+ "dist"
51
+ ],
52
+ scripts: {
53
+ build: "tsup",
54
+ clean: "rm -rf dist",
55
+ dev: "pnpm build && node dist/index.mjs",
56
+ typecheck: "tsc --noEmit"
57
+ },
58
+ dependencies: {
59
+ "@effect/cli": "^0.75.0",
60
+ "@effect/platform": "^0.96.0",
61
+ "@effect/platform-node": "^0.106.0",
62
+ "@gigadrive/build-utils": "workspace:*",
63
+ "@gigadrive/commons": "workspace:*",
64
+ "@gigadrive/network-config": "workspace:*",
65
+ "@gigadrive/sdk": "workspace:*",
66
+ archiver: "^7.0.1",
67
+ effect: "^3.19.17",
68
+ ignore: "^7.0.5",
69
+ open: "^10.2.0"
70
+ },
71
+ devDependencies: {
72
+ "@effect/language-service": "^0.84.0",
73
+ "@types/archiver": "^6.0.3",
74
+ esbuild: "^0.28.0",
75
+ typescript: "catalog:"
76
+ },
77
+ engines: {
78
+ node: ">=20.0.0"
79
+ },
80
+ publishConfig: {
81
+ access: "public"
82
+ }
83
+ };
84
+
85
+ // src/commands/ai/index.ts
33
86
  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");
87
+ var import_platform3 = require("@effect/platform");
88
+ var import_effect8 = require("effect");
37
89
 
38
90
  // src/errors.ts
39
91
  var import_effect = require("effect");
@@ -75,11 +127,57 @@ var UserInfoFetchError = class extends import_effect.Schema.TaggedError()("UserI
75
127
  statusCode: import_effect.Schema.optional(import_effect.Schema.Number)
76
128
  }) {
77
129
  };
130
+ var ApiRequestError = class extends import_effect.Schema.TaggedError()("ApiRequestError", {
131
+ message: import_effect.Schema.String,
132
+ statusCode: import_effect.Schema.optional(import_effect.Schema.Number),
133
+ code: import_effect.Schema.optional(import_effect.Schema.String)
134
+ }) {
135
+ };
136
+ var InvalidEnvVarFormatError = class extends import_effect.Schema.TaggedError()(
137
+ "InvalidEnvVarFormatError",
138
+ {
139
+ message: import_effect.Schema.String
140
+ }
141
+ ) {
142
+ };
143
+ var EnvVarNotFoundError = class extends import_effect.Schema.TaggedError()("EnvVarNotFoundError", {
144
+ message: import_effect.Schema.String
145
+ }) {
146
+ };
147
+ var NoApplicationsFoundError = class extends import_effect.Schema.TaggedError()(
148
+ "NoApplicationsFoundError",
149
+ {
150
+ message: import_effect.Schema.String
151
+ }
152
+ ) {
153
+ };
154
+ var ApplicationNotFoundError = class extends import_effect.Schema.TaggedError()(
155
+ "ApplicationNotFoundError",
156
+ {
157
+ message: import_effect.Schema.String
158
+ }
159
+ ) {
160
+ };
78
161
  var ConfigNotFoundError = class extends import_effect.Schema.TaggedError()("ConfigNotFoundError", {
79
162
  message: import_effect.Schema.String,
80
163
  directory: import_effect.Schema.String
81
164
  }) {
82
165
  };
166
+ var ProjectNotLinkedError = class extends import_effect.Schema.TaggedError()("ProjectNotLinkedError", {
167
+ message: import_effect.Schema.String,
168
+ directory: import_effect.Schema.String
169
+ }) {
170
+ };
171
+ var ProjectLinkReadError = class extends import_effect.Schema.TaggedError()("ProjectLinkReadError", {
172
+ message: import_effect.Schema.String,
173
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
174
+ }) {
175
+ };
176
+ var ProjectLinkWriteError = class extends import_effect.Schema.TaggedError()("ProjectLinkWriteError", {
177
+ message: import_effect.Schema.String,
178
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
179
+ }) {
180
+ };
83
181
  var ConfigParseError = class extends import_effect.Schema.TaggedError()("ConfigParseError", {
84
182
  message: import_effect.Schema.String,
85
183
  cause: import_effect.Schema.optional(import_effect.Schema.String)
@@ -160,6 +258,13 @@ var DeploymentLogsFetchError = class extends import_effect.Schema.TaggedError()(
160
258
  }
161
259
  ) {
162
260
  };
261
+ var DeploymentHostnamesFetchError = class extends import_effect.Schema.TaggedError()(
262
+ "DeploymentHostnamesFetchError",
263
+ {
264
+ message: import_effect.Schema.String
265
+ }
266
+ ) {
267
+ };
163
268
  var ArchiveCreateError = class extends import_effect.Schema.TaggedError()("ArchiveCreateError", {
164
269
  message: import_effect.Schema.String,
165
270
  cause: import_effect.Schema.optional(import_effect.Schema.String)
@@ -174,431 +279,150 @@ var DeploymentFailedError = class extends import_effect.Schema.TaggedError()("De
174
279
  }) {
175
280
  };
176
281
 
177
- // src/services/package-manager.ts
282
+ // src/services/api-client.ts
283
+ var import_sdk = require("@gigadrive/sdk");
284
+ var import_effect6 = require("effect");
285
+
286
+ // src/services/auth.ts
287
+ var import_effect5 = require("effect");
288
+ var crypto = __toESM(require("crypto"), 1);
289
+ var http = __toESM(require("http"), 1);
290
+ var import_open = __toESM(require("open"), 1);
291
+
292
+ // src/services/auth-storage.ts
178
293
  var import_platform = require("@effect/platform");
179
- var import_build_utils = require("@gigadrive/build-utils");
294
+ var import_effect3 = require("effect");
295
+ var os = __toESM(require("os"), 1);
296
+
297
+ // src/domain.ts
180
298
  var import_effect2 = require("effect");
181
- var PackageManagerService = class extends import_effect2.Effect.Service()("PackageManagerService", {
299
+ var DeploymentId = import_effect2.Schema.String.pipe(import_effect2.Schema.brand("@Gigadrive/DeploymentId"));
300
+ var ApplicationId = import_effect2.Schema.String.pipe(import_effect2.Schema.brand("@Gigadrive/ApplicationId"));
301
+ var UploadId = import_effect2.Schema.String.pipe(import_effect2.Schema.brand("@Gigadrive/UploadId"));
302
+ var PackageManager = import_effect2.Schema.Literal("npm", "yarn", "pnpm", "bun");
303
+ var OAuthTokens = import_effect2.Schema.Struct({
304
+ access_token: import_effect2.Schema.String,
305
+ refresh_token: import_effect2.Schema.optional(import_effect2.Schema.String),
306
+ expires_in: import_effect2.Schema.optional(import_effect2.Schema.Number),
307
+ token_type: import_effect2.Schema.optional(import_effect2.Schema.String)
308
+ });
309
+ var OAuthClientConfig = import_effect2.Schema.Struct({
310
+ clientId: import_effect2.Schema.String,
311
+ issuer: import_effect2.Schema.String,
312
+ authorizeUrl: import_effect2.Schema.String,
313
+ tokenUrl: import_effect2.Schema.String,
314
+ scope: import_effect2.Schema.String,
315
+ userinfoUrl: import_effect2.Schema.String
316
+ });
317
+ var StoredAuthData = import_effect2.Schema.Struct({
318
+ refreshToken: import_effect2.Schema.String,
319
+ accessToken: import_effect2.Schema.optional(import_effect2.Schema.String),
320
+ tokenExpirationTime: import_effect2.Schema.optional(import_effect2.Schema.Number)
321
+ });
322
+ var ProjectLink = import_effect2.Schema.Struct({
323
+ applicationId: import_effect2.Schema.String,
324
+ organizationId: import_effect2.Schema.optional(import_effect2.Schema.String)
325
+ });
326
+
327
+ // src/services/auth-storage.ts
328
+ var AuthStorageService = class extends import_effect3.Effect.Service()("AuthStorageService", {
182
329
  accessors: true,
183
- effect: import_effect2.Effect.gen(function* () {
330
+ effect: import_effect3.Effect.gen(function* () {
184
331
  const fs = yield* import_platform.FileSystem.FileSystem;
185
332
  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 })
333
+ const homeDir = os.homedir();
334
+ const authDir = pathService.join(homeDir, ".gigadrive");
335
+ const authFile = pathService.join(authDir, "auth.json");
336
+ const ensureDirectory = import_effect3.Effect.gen(function* () {
337
+ yield* fs.makeDirectory(authDir, { recursive: true }).pipe(
338
+ import_effect3.Effect.mapError(
339
+ (error) => new AuthStorageWriteError({
340
+ message: "Failed to create auth directory",
341
+ cause: String(error)
342
+ })
192
343
  )
193
344
  );
194
- if (!exists) {
195
- return yield* import_effect2.Effect.fail(
196
- new PackageManagerNotFoundError({
197
- message: "No package.json found",
198
- directory: cwd
345
+ yield* fs.chmod(authDir, 448).pipe(
346
+ import_effect3.Effect.mapError(
347
+ (error) => new AuthStorageWriteError({
348
+ message: "Failed to set auth directory permissions",
349
+ cause: String(error)
199
350
  })
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))
351
+ )
221
352
  );
222
- const pm = bunAvailable ? "bun" : "npm";
223
- yield* import_effect2.Effect.log("Package manager detected via fallback", { packageManager: pm });
224
- return pm;
225
353
  });
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
- }) {
241
- };
242
-
243
- // src/commands/build/index.ts
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
- })
354
+ const load = import_effect3.Effect.gen(function* () {
355
+ const exists = yield* fs.exists(authFile).pipe(import_effect3.Effect.mapError(() => new AuthStorageReadError({ message: "Failed to check auth file existence" })));
356
+ if (!exists) return import_effect3.Option.none();
357
+ const data = yield* fs.readFileString(authFile, "utf8").pipe(import_effect3.Effect.mapError(() => new AuthStorageReadError({ message: "Failed to read auth file" })));
358
+ const json = yield* import_effect3.Effect.try({
359
+ try: () => JSON.parse(data),
360
+ catch: () => new AuthStorageReadError({ message: "Auth file contains invalid JSON" })
361
+ });
362
+ const authData = yield* import_effect3.Schema.decodeUnknown(StoredAuthData)(json).pipe(
363
+ import_effect3.Effect.mapError(() => new AuthStorageReadError({ message: "Auth file has invalid schema" }))
364
+ );
365
+ return import_effect3.Option.some(authData);
272
366
  });
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 = {
278
- ...process.env,
279
- NODE_ENV: "production",
280
- NEBULA: "1",
281
- VERCEL: "1",
282
- NOW_BUILDER: "1"
283
- };
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
- })
367
+ const save = import_effect3.Effect.fn("AuthStorageService.save")(function* (authData) {
368
+ yield* ensureDirectory;
369
+ yield* fs.writeFileString(authFile, JSON.stringify(authData, null, 2), { mode: 384 }).pipe(
370
+ import_effect3.Effect.mapError(
371
+ (error) => new AuthStorageWriteError({
372
+ message: "Failed to write auth file",
373
+ cause: String(error)
374
+ })
375
+ )
376
+ );
377
+ yield* import_effect3.Effect.log("Auth data saved to local storage");
300
378
  });
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
- })
379
+ const remove = import_effect3.Effect.gen(function* () {
380
+ yield* fs.remove(authFile).pipe(
381
+ import_effect3.Effect.mapError((error) => {
382
+ const cause = error instanceof Error ? error : error.error;
383
+ const code = cause && typeof cause === "object" && "code" in cause ? cause.code : "";
384
+ return { isNotFound: code === "ENOENT" || String(error).includes("ENOENT"), original: error };
385
+ }),
386
+ import_effect3.Effect.catchAll(
387
+ (mapped) => mapped.isNotFound ? import_effect3.Effect.void : import_effect3.Effect.fail(
388
+ new AuthStorageWriteError({
389
+ message: "Failed to remove auth file",
390
+ cause: String(mapped.original)
391
+ })
392
+ )
393
+ )
394
+ );
395
+ yield* import_effect3.Effect.log("Auth data removed from local storage");
317
396
  });
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");
397
+ return { load, save, remove };
398
+ })
399
+ }) {
400
+ };
337
401
 
338
- // src/services/project-config.ts
339
- var import_network_config = require("@gigadrive/network-config");
402
+ // src/services/oauth-config.ts
340
403
  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", {
404
+ var OAuthEnvConfig = import_effect4.Config.all({
405
+ issuerUrl: import_effect4.Config.string("GIGADRIVE_NETWORK_OAUTH_ISSUER_URL").pipe(import_effect4.Config.withDefault("https://idp.gigadrive.de")),
406
+ clientId: import_effect4.Config.string("GIGADRIVE_NETWORK_OAUTH_CLIENT_ID").pipe(import_effect4.Config.withDefault("todo_add_client_id"))
407
+ });
408
+ var OpenIdDiscoveryDocument = import_effect4.Schema.Struct({
409
+ issuer: import_effect4.Schema.optional(import_effect4.Schema.String),
410
+ authorization_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String),
411
+ token_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String),
412
+ userinfo_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String)
413
+ });
414
+ var DEFAULT_SCOPE = "offline_access openid profile email";
415
+ var OAuthConfigService = class extends import_effect4.Effect.Service()("OAuthConfigService", {
352
416
  accessors: true,
353
- dependencies: [import_network_config.NetworkConfigLive],
354
417
  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);
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 };
414
- });
415
- return { resolve };
416
- })
417
- }) {
418
- };
419
-
420
- // src/commands/debug/config/index.ts
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
- );
443
-
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
475
- });
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
- }) {
576
- };
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
418
  const envConfig = yield* OAuthEnvConfig;
595
419
  let cached = null;
596
- const getConfig = import_effect9.Effect.gen(function* () {
420
+ const getConfig = import_effect4.Effect.gen(function* () {
597
421
  if (cached) return cached;
598
422
  const issuerBase = envConfig.issuerUrl.replace(/\/$/, "");
599
423
  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({
424
+ yield* import_effect4.Effect.log("Fetching OIDC discovery document", { discoveryUrl });
425
+ const response = yield* import_effect4.Effect.tryPromise({
602
426
  try: (signal) => fetch(discoveryUrl, { signal }),
603
427
  catch: (error) => new OAuthDiscoveryError({
604
428
  message: "Failed to fetch OIDC discovery document",
@@ -606,27 +430,27 @@ var OAuthConfigService = class extends import_effect9.Effect.Service()("OAuthCon
606
430
  })
607
431
  });
608
432
  if (!response.ok) {
609
- return yield* import_effect9.Effect.fail(
433
+ return yield* import_effect4.Effect.fail(
610
434
  new OAuthDiscoveryError({
611
435
  message: `OIDC discovery returned ${response.status} ${response.statusText}`
612
436
  })
613
437
  );
614
438
  }
615
- const json = yield* import_effect9.Effect.tryPromise({
439
+ const json = yield* import_effect4.Effect.tryPromise({
616
440
  try: () => response.json(),
617
441
  catch: () => new OAuthDiscoveryError({
618
442
  message: "Failed to parse OIDC discovery JSON"
619
443
  })
620
444
  });
621
- const decoded = yield* import_effect9.Schema.decodeUnknown(OpenIdDiscoveryDocument)(json).pipe(
622
- import_effect9.Effect.mapError(
445
+ const decoded = yield* import_effect4.Schema.decodeUnknown(OpenIdDiscoveryDocument)(json).pipe(
446
+ import_effect4.Effect.mapError(
623
447
  () => new OAuthDiscoveryError({
624
448
  message: "Invalid OIDC discovery document schema"
625
449
  })
626
450
  )
627
451
  );
628
452
  if (!decoded.authorization_endpoint || !decoded.token_endpoint || !decoded.userinfo_endpoint) {
629
- return yield* import_effect9.Effect.fail(
453
+ return yield* import_effect4.Effect.fail(
630
454
  new OAuthDiscoveryError({
631
455
  message: "OIDC discovery returned incomplete endpoints"
632
456
  })
@@ -641,7 +465,7 @@ var OAuthConfigService = class extends import_effect9.Effect.Service()("OAuthCon
641
465
  userinfoUrl: decoded.userinfo_endpoint
642
466
  };
643
467
  cached = config;
644
- yield* import_effect9.Effect.log("OIDC discovery completed", { issuer: config.issuer });
468
+ yield* import_effect4.Effect.log("OIDC discovery completed", { issuer: config.issuer });
645
469
  return config;
646
470
  });
647
471
  return { getConfig };
@@ -658,22 +482,22 @@ var generatePKCE = () => {
658
482
  const codeChallenge = base64URLEncode(challengeBytes);
659
483
  return { codeVerifier, codeChallenge };
660
484
  };
661
- var AuthService = class extends import_effect10.Effect.Service()("AuthService", {
485
+ var AuthService = class extends import_effect5.Effect.Service()("AuthService", {
662
486
  accessors: true,
663
487
  dependencies: [OAuthConfigService.Default, AuthStorageService.Default],
664
- effect: import_effect10.Effect.gen(function* () {
488
+ effect: import_effect5.Effect.gen(function* () {
665
489
  const oauthConfig = yield* OAuthConfigService;
666
490
  const storage = yield* AuthStorageService;
667
491
  let accessToken = null;
668
492
  let tokenExpirationTime = 0;
669
- const logout = import_effect10.Effect.gen(function* () {
493
+ const logout = import_effect5.Effect.gen(function* () {
670
494
  accessToken = null;
671
495
  tokenExpirationTime = 0;
672
496
  yield* storage.remove;
673
- yield* import_effect10.Effect.log("Logged out");
497
+ yield* import_effect5.Effect.log("Logged out");
674
498
  });
675
- const exchangeCodeForTokens = (config, authCode, redirectUri, codeVerifier) => import_effect10.Effect.gen(function* () {
676
- const response = yield* import_effect10.Effect.tryPromise({
499
+ const exchangeCodeForTokens = (config, authCode, redirectUri, codeVerifier) => import_effect5.Effect.gen(function* () {
500
+ const response = yield* import_effect5.Effect.tryPromise({
677
501
  try: () => fetch(config.tokenUrl, {
678
502
  method: "POST",
679
503
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
@@ -690,31 +514,31 @@ var AuthService = class extends import_effect10.Effect.Service()("AuthService",
690
514
  })
691
515
  });
692
516
  if (!response.ok) {
693
- const body = yield* import_effect10.Effect.tryPromise({
517
+ const body = yield* import_effect5.Effect.tryPromise({
694
518
  try: () => response.text(),
695
519
  catch: () => new TokenExchangeError({ message: "Failed to read token exchange error response" })
696
520
  });
697
- return yield* import_effect10.Effect.fail(
521
+ return yield* import_effect5.Effect.fail(
698
522
  new TokenExchangeError({
699
523
  message: `Token exchange failed: ${response.status} ${response.statusText} - ${body}`,
700
524
  statusCode: response.status
701
525
  })
702
526
  );
703
527
  }
704
- const tokens = yield* import_effect10.Effect.tryPromise({
528
+ const tokens = yield* import_effect5.Effect.tryPromise({
705
529
  try: () => response.json(),
706
530
  catch: () => new TokenExchangeError({ message: "Failed to parse token exchange response" })
707
531
  });
708
532
  return tokens;
709
533
  });
710
- const startCallbackServer = (config, codeChallenge, state) => import_effect10.Effect.async((resume) => {
534
+ const startCallbackServer = (config, codeChallenge, state) => import_effect5.Effect.async((resume) => {
711
535
  const server = http.createServer();
712
536
  const redirectPath = "/callback";
713
537
  server.listen(0, "127.0.0.1", () => {
714
538
  const address = server.address();
715
539
  if (!address || typeof address !== "object") {
716
540
  server.close(() => {
717
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: "Failed to determine callback server address" })));
541
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: "Failed to determine callback server address" })));
718
542
  });
719
543
  return;
720
544
  }
@@ -728,10 +552,10 @@ var AuthService = class extends import_effect10.Effect.Service()("AuthService",
728
552
  authUrl.searchParams.append("code_challenge", codeChallenge);
729
553
  authUrl.searchParams.append("code_challenge_method", "S256");
730
554
  authUrl.searchParams.append("state", state);
731
- import_effect10.Console.log(`Please open this URL in your browser to log in:
555
+ import_effect5.Console.log(`Please open this URL in your browser to log in:
732
556
  ${authUrl.toString()}
733
- `).pipe(import_effect10.Effect.runSync);
734
- import_effect10.Console.log("Waiting for login callback...").pipe(import_effect10.Effect.runSync);
557
+ `).pipe(import_effect5.Effect.runSync);
558
+ import_effect5.Console.log("Waiting for login callback...").pipe(import_effect5.Effect.runSync);
735
559
  void (0, import_open.default)(authUrl.toString());
736
560
  server.on("request", (req, res) => {
737
561
  const reqUrl = new URL(req.url || "", redirectUri);
@@ -747,7 +571,7 @@ ${authUrl.toString()}
747
571
  const safeOauthError = escapeHtml(oauthError);
748
572
  res.end(`<h1>Login Failed!</h1><p>Error: ${safeOauthError}</p><p>You can close this tab.</p>`);
749
573
  server.close();
750
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: `OAuth error: ${oauthError}` })));
574
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: `OAuth error: ${oauthError}` })));
751
575
  return;
752
576
  }
753
577
  if (receivedState !== state) {
@@ -755,26 +579,26 @@ ${authUrl.toString()}
755
579
  "<h1>Login Failed!</h1><p>State mismatch. Possible CSRF attack.</p><p>You can close this tab.</p>"
756
580
  );
757
581
  server.close();
758
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: "State mismatch" })));
582
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: "State mismatch" })));
759
583
  return;
760
584
  }
761
585
  if (authCode) {
762
586
  res.end("<h1>Login Successful!</h1><p>You can close this tab.</p>");
763
587
  server.close();
764
- resume(import_effect10.Effect.succeed({ authCode, redirectUri }));
588
+ resume(import_effect5.Effect.succeed({ authCode, redirectUri }));
765
589
  } else {
766
590
  res.end("<h1>Login Failed!</h1><p>No authorization code received.</p><p>You can close this tab.</p>");
767
591
  server.close();
768
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: "No authorization code received" })));
592
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: "No authorization code received" })));
769
593
  }
770
594
  });
771
595
  server.on("error", (err) => {
772
596
  server.close();
773
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: `Local server error: ${err.message}` })));
597
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: `Local server error: ${err.message}` })));
774
598
  });
775
599
  });
776
600
  });
777
- const login = import_effect10.Effect.gen(function* () {
601
+ const login = import_effect5.Effect.gen(function* () {
778
602
  yield* logout;
779
603
  const { codeVerifier, codeChallenge } = generatePKCE();
780
604
  const state = crypto.randomBytes(16).toString("hex");
@@ -782,7 +606,7 @@ ${authUrl.toString()}
782
606
  const { authCode, redirectUri } = yield* startCallbackServer(config, codeChallenge, state);
783
607
  const tokens = yield* exchangeCodeForTokens(config, authCode, redirectUri, codeVerifier);
784
608
  if (!tokens.refresh_token) {
785
- return yield* import_effect10.Effect.fail(
609
+ return yield* import_effect5.Effect.fail(
786
610
  new TokenExchangeError({ message: "No refresh token received from identity provider" })
787
611
  );
788
612
  }
@@ -793,21 +617,21 @@ ${authUrl.toString()}
793
617
  accessToken,
794
618
  tokenExpirationTime
795
619
  });
796
- yield* import_effect10.Effect.log("Login successful");
620
+ yield* import_effect5.Effect.log("Login successful");
797
621
  return true;
798
622
  });
799
- const refreshAccessToken = import_effect10.Effect.gen(function* () {
623
+ const refreshAccessToken = import_effect5.Effect.gen(function* () {
800
624
  const stored = yield* storage.load.pipe(
801
- import_effect10.Effect.catchTag("AuthStorageReadError", (err) => import_effect10.Effect.fail(new TokenRefreshError({ message: err.message })))
625
+ import_effect5.Effect.catchTag("AuthStorageReadError", (err) => import_effect5.Effect.fail(new TokenRefreshError({ message: err.message })))
802
626
  );
803
627
  const config = yield* oauthConfig.getConfig.pipe(
804
- import_effect10.Effect.catchTag("OAuthDiscoveryError", (err) => import_effect10.Effect.fail(new TokenRefreshError({ message: err.message })))
628
+ import_effect5.Effect.catchTag("OAuthDiscoveryError", (err) => import_effect5.Effect.fail(new TokenRefreshError({ message: err.message })))
805
629
  );
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." }))
630
+ const authData = yield* import_effect5.Option.match(stored, {
631
+ onNone: () => import_effect5.Effect.fail(new TokenRefreshError({ message: "No refresh token available. Please log in." })),
632
+ onSome: (data) => data.refreshToken ? import_effect5.Effect.succeed(data) : import_effect5.Effect.fail(new TokenRefreshError({ message: "No refresh token available. Please log in." }))
809
633
  });
810
- const response = yield* import_effect10.Effect.tryPromise({
634
+ const response = yield* import_effect5.Effect.tryPromise({
811
635
  try: () => fetch(config.tokenUrl, {
812
636
  method: "POST",
813
637
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
@@ -823,26 +647,26 @@ ${authUrl.toString()}
823
647
  });
824
648
  if (!response.ok) {
825
649
  if (response.status === 400) {
826
- yield* logout.pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.void));
827
- return yield* import_effect10.Effect.fail(
650
+ yield* logout.pipe(import_effect5.Effect.catchAll(() => import_effect5.Effect.void));
651
+ return yield* import_effect5.Effect.fail(
828
652
  new TokenRefreshError({
829
653
  message: "Refresh token is invalid or expired. Please log in again.",
830
654
  statusCode: response.status
831
655
  })
832
656
  );
833
657
  }
834
- const body = yield* import_effect10.Effect.tryPromise({
658
+ const body = yield* import_effect5.Effect.tryPromise({
835
659
  try: () => response.text(),
836
660
  catch: () => new TokenRefreshError({ message: "Failed to read refresh error response" })
837
661
  });
838
- return yield* import_effect10.Effect.fail(
662
+ return yield* import_effect5.Effect.fail(
839
663
  new TokenRefreshError({
840
664
  message: `Token refresh failed: ${response.status} ${response.statusText} - ${body}`,
841
665
  statusCode: response.status
842
666
  })
843
667
  );
844
668
  }
845
- const tokens = yield* import_effect10.Effect.tryPromise({
669
+ const tokens = yield* import_effect5.Effect.tryPromise({
846
670
  try: () => response.json(),
847
671
  catch: () => new TokenRefreshError({ message: "Failed to parse token refresh response" })
848
672
  });
@@ -853,18 +677,18 @@ ${authUrl.toString()}
853
677
  refreshToken: newRefreshToken,
854
678
  accessToken,
855
679
  tokenExpirationTime
856
- }).pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.void));
857
- yield* import_effect10.Effect.log("Access token refreshed successfully");
680
+ }).pipe(import_effect5.Effect.catchAll(() => import_effect5.Effect.void));
681
+ yield* import_effect5.Effect.log("Access token refreshed successfully");
858
682
  return true;
859
683
  });
860
- const getAccessToken = import_effect10.Effect.gen(function* () {
684
+ const getAccessToken = import_effect5.Effect.gen(function* () {
861
685
  if (accessToken && Date.now() < tokenExpirationTime) {
862
686
  return accessToken;
863
687
  }
864
688
  const stored = yield* storage.load.pipe(
865
- import_effect10.Effect.catchAll(() => import_effect10.Effect.succeed(import_effect10.Option.none()))
689
+ import_effect5.Effect.catchAll(() => import_effect5.Effect.succeed(import_effect5.Option.none()))
866
690
  );
867
- import_effect10.Option.match(stored, {
691
+ import_effect5.Option.match(stored, {
868
692
  onNone: () => {
869
693
  },
870
694
  onSome: (data) => {
@@ -877,124 +701,1145 @@ ${authUrl.toString()}
877
701
  if (accessToken && Date.now() < tokenExpirationTime) {
878
702
  return accessToken;
879
703
  }
880
- yield* import_effect10.Effect.log("Access token expired or not available. Attempting to refresh...");
704
+ yield* import_effect5.Effect.log("Access token expired or not available. Attempting to refresh...");
881
705
  yield* refreshAccessToken.pipe(
882
- import_effect10.Effect.catchTag("TokenRefreshError", (err) => import_effect10.Effect.fail(new NotAuthenticatedError({ message: err.message })))
706
+ import_effect5.Effect.catchTag("TokenRefreshError", (err) => import_effect5.Effect.fail(new NotAuthenticatedError({ message: err.message })))
883
707
  );
884
708
  if (!accessToken) {
885
- return yield* import_effect10.Effect.fail(
709
+ return yield* import_effect5.Effect.fail(
886
710
  new NotAuthenticatedError({ message: "No valid access token available. Please log in first." })
887
711
  );
888
712
  }
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);
713
+ return accessToken;
714
+ });
715
+ const getUserInfo = import_effect5.Effect.gen(function* () {
716
+ const config = yield* oauthConfig.getConfig;
717
+ const token = yield* getAccessToken;
718
+ const fetchUserInfo = (t) => import_effect5.Effect.tryPromise({
719
+ try: () => fetch(config.userinfoUrl, {
720
+ headers: {
721
+ Authorization: `Bearer ${t}`,
722
+ Accept: "application/json"
723
+ }
724
+ }),
725
+ catch: (error) => new UserInfoFetchError({
726
+ message: `Failed to fetch user info: ${error instanceof Error ? error.message : String(error)}`
727
+ })
728
+ });
729
+ let response = yield* fetchUserInfo(token);
730
+ if (response.status === 401 || response.status === 403) {
731
+ yield* refreshAccessToken.pipe(
732
+ import_effect5.Effect.catchTag("TokenRefreshError", (err) => import_effect5.Effect.fail(new UserInfoFetchError({ message: err.message })))
733
+ );
734
+ if (!accessToken) {
735
+ return yield* import_effect5.Effect.fail(new UserInfoFetchError({ message: "Token refresh failed" }));
736
+ }
737
+ response = yield* fetchUserInfo(accessToken);
738
+ }
739
+ if (!response.ok) {
740
+ const body = yield* import_effect5.Effect.tryPromise({
741
+ try: () => response.text(),
742
+ catch: () => new UserInfoFetchError({ message: "Failed to read user info error response" })
743
+ });
744
+ return yield* import_effect5.Effect.fail(
745
+ new UserInfoFetchError({
746
+ message: `Failed to fetch user info: ${response.status} ${response.statusText}${body ? ` - ${body}` : ""}`,
747
+ statusCode: response.status
748
+ })
749
+ );
750
+ }
751
+ const data = yield* import_effect5.Effect.tryPromise({
752
+ try: () => response.json(),
753
+ catch: () => new UserInfoFetchError({ message: "Failed to parse user info response" })
754
+ });
755
+ return data;
756
+ });
757
+ const inferUserName = (info) => {
758
+ const isNonEmptyString = (value) => typeof value === "string" && value.trim() !== "";
759
+ const name = info.name;
760
+ const givenName = info.given_name;
761
+ const familyName = info.family_name;
762
+ const preferredUsername = info.preferred_username;
763
+ const email = info.email;
764
+ return isNonEmptyString(name) && name || (isNonEmptyString(givenName) && isNonEmptyString(familyName) ? `${givenName} ${familyName}` : void 0) || isNonEmptyString(preferredUsername) && preferredUsername || isNonEmptyString(email) && email || "your account";
765
+ };
766
+ return { login, logout, getAccessToken, getUserInfo, refreshAccessToken, inferUserName };
767
+ })
768
+ }) {
769
+ };
770
+
771
+ // src/services/api-client.ts
772
+ var ApiBaseUrl = import_effect6.Config.string("GIGADRIVE_API_BASE_URL").pipe(import_effect6.Config.withDefault("https://api.gigadrive.network"));
773
+ var toApiRequestError = (error) => {
774
+ if (error instanceof import_sdk.ApiError) {
775
+ return new ApiRequestError({ message: error.message, statusCode: error.status, code: error.code });
776
+ }
777
+ return new ApiRequestError({ message: error instanceof Error ? error.message : String(error) });
778
+ };
779
+ var ApiClientService = class extends import_effect6.Effect.Service()("ApiClientService", {
780
+ accessors: true,
781
+ effect: import_effect6.Effect.gen(function* () {
782
+ const baseUrl = yield* ApiBaseUrl;
783
+ const authService = yield* AuthService;
784
+ const getClient = import_effect6.Effect.gen(function* () {
785
+ const token = yield* authService.getAccessToken;
786
+ return new import_sdk.GigadriveClient({ bearerToken: token, baseUrl });
787
+ });
788
+ const request = (f) => import_effect6.Effect.gen(function* () {
789
+ const client = yield* getClient;
790
+ return yield* import_effect6.Effect.tryPromise({
791
+ try: () => f(client),
792
+ catch: toApiRequestError
793
+ });
794
+ });
795
+ return { getClient, request };
796
+ })
797
+ }) {
798
+ };
799
+
800
+ // src/services/project-link.ts
801
+ var import_platform2 = require("@effect/platform");
802
+ var import_effect7 = require("effect");
803
+ var LINK_DIR = ".gigadrive";
804
+ var LINK_FILE = "project.json";
805
+ var ProjectLinkService = class extends import_effect7.Effect.Service()("ProjectLinkService", {
806
+ accessors: true,
807
+ effect: import_effect7.Effect.gen(function* () {
808
+ const fs = yield* import_platform2.FileSystem.FileSystem;
809
+ const pathService = yield* import_platform2.Path.Path;
810
+ const linkDir = (projectFolder) => pathService.join(projectFolder, LINK_DIR);
811
+ const linkFile = (projectFolder) => pathService.join(projectFolder, LINK_DIR, LINK_FILE);
812
+ const load = import_effect7.Effect.fn("ProjectLinkService.load")(function* (projectFolder) {
813
+ const file = linkFile(projectFolder);
814
+ const exists = yield* fs.exists(file).pipe(import_effect7.Effect.mapError(() => new ProjectLinkReadError({ message: "Failed to check project link existence" })));
815
+ if (!exists) return import_effect7.Option.none();
816
+ const data = yield* fs.readFileString(file, "utf8").pipe(import_effect7.Effect.mapError(() => new ProjectLinkReadError({ message: "Failed to read project link file" })));
817
+ const json = yield* import_effect7.Effect.try({
818
+ try: () => JSON.parse(data),
819
+ catch: () => new ProjectLinkReadError({ message: `${LINK_DIR}/${LINK_FILE} contains invalid JSON` })
820
+ });
821
+ const link = yield* import_effect7.Schema.decodeUnknown(ProjectLink)(json).pipe(
822
+ import_effect7.Effect.mapError(() => new ProjectLinkReadError({ message: `${LINK_DIR}/${LINK_FILE} has an invalid schema` }))
823
+ );
824
+ return import_effect7.Option.some(link);
825
+ });
826
+ const resolve = import_effect7.Effect.fn("ProjectLinkService.resolve")(function* (projectFolder) {
827
+ const link = yield* load(projectFolder);
828
+ return yield* import_effect7.Option.match(link, {
829
+ onNone: () => import_effect7.Effect.fail(
830
+ new ProjectNotLinkedError({
831
+ message: 'This directory is not linked to an application. Run "gigadrive link" first.',
832
+ directory: projectFolder
833
+ })
834
+ ),
835
+ onSome: (value) => import_effect7.Effect.succeed(value)
836
+ });
837
+ });
838
+ const save = import_effect7.Effect.fn("ProjectLinkService.save")(function* (projectFolder, link) {
839
+ yield* fs.makeDirectory(linkDir(projectFolder), { recursive: true }).pipe(
840
+ import_effect7.Effect.mapError(
841
+ (error) => new ProjectLinkWriteError({ message: "Failed to create .gigadrive directory", cause: String(error) })
842
+ )
843
+ );
844
+ yield* fs.writeFileString(linkFile(projectFolder), `${JSON.stringify(link, null, 2)}
845
+ `).pipe(
846
+ import_effect7.Effect.mapError(
847
+ (error) => new ProjectLinkWriteError({ message: "Failed to write project link file", cause: String(error) })
848
+ )
849
+ );
850
+ yield* import_effect7.Effect.log("Project link saved", { applicationId: link.applicationId });
851
+ });
852
+ const remove = import_effect7.Effect.fn("ProjectLinkService.remove")(function* (projectFolder) {
853
+ yield* fs.remove(linkFile(projectFolder), { force: true }).pipe(
854
+ import_effect7.Effect.mapError(
855
+ (error) => new ProjectLinkWriteError({ message: "Failed to remove project link file", cause: String(error) })
856
+ )
857
+ );
858
+ });
859
+ return { load, resolve, save, remove };
860
+ })
861
+ }) {
862
+ };
863
+
864
+ // src/commands/ai/index.ts
865
+ var orgOption = import_cli.Options.text("org").pipe(
866
+ import_cli.Options.withAlias("o"),
867
+ import_cli.Options.withDescription("Organization ID (defaults to the linked organization)"),
868
+ import_cli.Options.optional
869
+ );
870
+ var appOption = import_cli.Options.text("app").pipe(
871
+ import_cli.Options.withAlias("a"),
872
+ import_cli.Options.withDescription("Restrict to this application ID"),
873
+ import_cli.Options.optional
874
+ );
875
+ var fromOption = import_cli.Options.text("from").pipe(
876
+ import_cli.Options.withDescription("Include usage at or after this ISO 8601 timestamp"),
877
+ import_cli.Options.optional
878
+ );
879
+ var toOption = import_cli.Options.text("to").pipe(
880
+ import_cli.Options.withDescription("Include usage before or at this ISO 8601 timestamp"),
881
+ import_cli.Options.optional
882
+ );
883
+ var fileOption = import_cli.Options.text("file").pipe(
884
+ import_cli.Options.withAlias("f"),
885
+ import_cli.Options.withDescription('Path to a JSON file, or "-" for stdin (default: stdin)'),
886
+ import_cli.Options.optional
887
+ );
888
+ var resolveOrgId = (org) => import_effect8.Effect.gen(function* () {
889
+ if (import_effect8.Option.isSome(org)) return org.value;
890
+ const projectLink = yield* ProjectLinkService;
891
+ const link = yield* projectLink.resolve(process.cwd());
892
+ if (link.organizationId === void 0) {
893
+ return yield* import_effect8.Effect.fail(
894
+ new ProjectNotLinkedError({
895
+ message: 'No organization is linked. Pass --org <id> or re-run "gigadrive link".',
896
+ directory: process.cwd()
897
+ })
898
+ );
899
+ }
900
+ return link.organizationId;
901
+ });
902
+ var readStdin = import_effect8.Effect.async((resume) => {
903
+ let data = "";
904
+ const stdin = process.stdin;
905
+ stdin.setEncoding("utf8");
906
+ const onData = (chunk) => {
907
+ data += chunk;
908
+ };
909
+ const cleanup = () => {
910
+ stdin.off("data", onData);
911
+ stdin.off("end", onEnd);
912
+ stdin.off("error", onError);
913
+ };
914
+ const onEnd = () => {
915
+ cleanup();
916
+ resume(import_effect8.Effect.succeed(data));
917
+ };
918
+ const onError = (err) => {
919
+ cleanup();
920
+ resume(import_effect8.Effect.fail(new ApiRequestError({ message: `Failed to read stdin: ${err.message}` })));
921
+ };
922
+ stdin.on("data", onData);
923
+ stdin.on("end", onEnd);
924
+ stdin.on("error", onError);
925
+ stdin.resume();
926
+ });
927
+ var readJsonInput = (file) => import_effect8.Effect.gen(function* () {
928
+ const path = import_effect8.Option.getOrUndefined(file);
929
+ let raw;
930
+ if (path === void 0 || path === "-") {
931
+ raw = yield* readStdin;
932
+ } else {
933
+ const fs = yield* import_platform3.FileSystem.FileSystem;
934
+ raw = yield* fs.readFileString(path, "utf8").pipe(import_effect8.Effect.mapError((e) => new ApiRequestError({ message: `Failed to read ${path}: ${String(e)}` })));
935
+ }
936
+ return yield* import_effect8.Effect.try({
937
+ try: () => JSON.parse(raw),
938
+ catch: () => new ApiRequestError({ message: "Input is not valid JSON." })
939
+ });
940
+ });
941
+ var governanceErrorHandlers = {
942
+ NotAuthenticatedError: (err) => import_effect8.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
943
+ import_effect8.Effect.andThen(import_effect8.Effect.fail(err))
944
+ ),
945
+ ApiRequestError: (err) => import_effect8.Console.error(err.message).pipe(import_effect8.Effect.andThen(import_effect8.Effect.fail(err))),
946
+ ProjectNotLinkedError: (err) => import_effect8.Console.error(err.message).pipe(import_effect8.Effect.andThen(import_effect8.Effect.fail(err))),
947
+ ProjectLinkReadError: (err) => import_effect8.Console.error(err.message).pipe(import_effect8.Effect.andThen(import_effect8.Effect.fail(err)))
948
+ };
949
+ var usageSummaryCommand = import_cli.Command.make(
950
+ "summary",
951
+ { org: orgOption, app: appOption, from: fromOption, to: toOption },
952
+ ({ org, app, from, to }) => import_effect8.Effect.gen(function* () {
953
+ const apiClient = yield* ApiClientService;
954
+ const orgId = yield* resolveOrgId(org);
955
+ const summary = yield* apiClient.request(
956
+ (client) => client.organizations.aiGateway.usage.summary(orgId, {
957
+ applicationId: import_effect8.Option.getOrUndefined(app),
958
+ from: import_effect8.Option.getOrUndefined(from),
959
+ to: import_effect8.Option.getOrUndefined(to)
960
+ })
961
+ );
962
+ yield* import_effect8.Console.log(JSON.stringify(summary, null, 2));
963
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
964
+ );
965
+ var usageRequestsCommand = import_cli.Command.make(
966
+ "requests",
967
+ { org: orgOption, app: appOption, from: fromOption, to: toOption },
968
+ ({ org, app, from, to }) => import_effect8.Effect.gen(function* () {
969
+ const apiClient = yield* ApiClientService;
970
+ const orgId = yield* resolveOrgId(org);
971
+ const { items, total } = yield* apiClient.request(
972
+ (client) => client.organizations.aiGateway.usage.requests(orgId, {
973
+ applicationId: import_effect8.Option.getOrUndefined(app),
974
+ from: import_effect8.Option.getOrUndefined(from),
975
+ to: import_effect8.Option.getOrUndefined(to)
976
+ })
977
+ );
978
+ if (items.length === 0) {
979
+ yield* import_effect8.Console.log("No AI Gateway requests found.");
980
+ return;
981
+ }
982
+ for (const e of items) {
983
+ yield* import_effect8.Console.log(
984
+ `${e.createdAt} ${e.modelRequested} ${e.status} ${e.totalTokens} tok ${e.billableCostMicros}\xB5$`
985
+ );
986
+ }
987
+ yield* import_effect8.Console.log(`
988
+ ${total} request(s).`);
989
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
990
+ );
991
+ var outOption = import_cli.Options.text("out").pipe(
992
+ import_cli.Options.withDescription("Write the CSV to this file instead of stdout"),
993
+ import_cli.Options.optional
994
+ );
995
+ var usageExportCommand = import_cli.Command.make(
996
+ "export",
997
+ { org: orgOption, app: appOption, from: fromOption, to: toOption, out: outOption },
998
+ ({ org, app, from, to, out }) => import_effect8.Effect.gen(function* () {
999
+ const apiClient = yield* ApiClientService;
1000
+ const orgId = yield* resolveOrgId(org);
1001
+ const csv = yield* apiClient.request(
1002
+ (client) => client.organizations.aiGateway.usage.export(orgId, {
1003
+ applicationId: import_effect8.Option.getOrUndefined(app),
1004
+ from: import_effect8.Option.getOrUndefined(from),
1005
+ to: import_effect8.Option.getOrUndefined(to)
1006
+ })
1007
+ );
1008
+ const outPath = import_effect8.Option.getOrUndefined(out);
1009
+ if (outPath === void 0) {
1010
+ yield* import_effect8.Console.log(csv);
1011
+ } else {
1012
+ const fs = yield* import_platform3.FileSystem.FileSystem;
1013
+ yield* fs.writeFileString(outPath, csv).pipe(import_effect8.Effect.mapError((e) => new ApiRequestError({ message: `Failed to write ${outPath}: ${String(e)}` })));
1014
+ yield* import_effect8.Console.log(`Wrote ${outPath}.`);
1015
+ }
1016
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1017
+ );
1018
+ var usageCommand = import_cli.Command.make("usage", {}, () => import_effect8.Effect.void).pipe(
1019
+ import_cli.Command.withSubcommands([usageSummaryCommand, usageRequestsCommand, usageExportCommand])
1020
+ );
1021
+ var budgetsGetCommand = import_cli.Command.make(
1022
+ "get",
1023
+ { org: orgOption },
1024
+ ({ org }) => import_effect8.Effect.gen(function* () {
1025
+ const apiClient = yield* ApiClientService;
1026
+ const orgId = yield* resolveOrgId(org);
1027
+ const { items } = yield* apiClient.request((client) => client.organizations.aiGateway.budgets.list(orgId));
1028
+ yield* import_effect8.Console.log(JSON.stringify(items, null, 2));
1029
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1030
+ );
1031
+ var budgetsSetCommand = import_cli.Command.make(
1032
+ "set",
1033
+ { org: orgOption, file: fileOption },
1034
+ ({ org, file }) => import_effect8.Effect.gen(function* () {
1035
+ const apiClient = yield* ApiClientService;
1036
+ const orgId = yield* resolveOrgId(org);
1037
+ const parsed = yield* readJsonInput(file);
1038
+ const budgets = Array.isArray(parsed) ? parsed : parsed.budgets;
1039
+ if (!Array.isArray(budgets)) {
1040
+ return yield* import_effect8.Effect.fail(
1041
+ new ApiRequestError({
1042
+ message: 'Expected a JSON array of budgets, or an object of the form { "budgets": [...] }.'
1043
+ })
1044
+ );
1045
+ }
1046
+ const { items } = yield* apiClient.request(
1047
+ (client) => client.organizations.aiGateway.budgets.replace(orgId, budgets)
1048
+ );
1049
+ yield* import_effect8.Console.log(`Saved ${items.length} budget(s).`);
1050
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1051
+ );
1052
+ var budgetsCommand = import_cli.Command.make("budgets", {}, () => import_effect8.Effect.void).pipe(
1053
+ import_cli.Command.withSubcommands([budgetsGetCommand, budgetsSetCommand])
1054
+ );
1055
+ var policiesGetCommand = import_cli.Command.make(
1056
+ "get",
1057
+ { org: orgOption, app: appOption },
1058
+ ({ org, app }) => import_effect8.Effect.gen(function* () {
1059
+ const apiClient = yield* ApiClientService;
1060
+ const orgId = yield* resolveOrgId(org);
1061
+ const policy = yield* apiClient.request(
1062
+ (client) => client.organizations.aiGateway.policies.get(orgId, { applicationId: import_effect8.Option.getOrUndefined(app) })
1063
+ );
1064
+ yield* import_effect8.Console.log(policy === null ? "No policy set." : JSON.stringify(policy, null, 2));
1065
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1066
+ );
1067
+ var policiesSetCommand = import_cli.Command.make(
1068
+ "set",
1069
+ { org: orgOption, file: fileOption },
1070
+ ({ org, file }) => import_effect8.Effect.gen(function* () {
1071
+ const apiClient = yield* ApiClientService;
1072
+ const orgId = yield* resolveOrgId(org);
1073
+ const parsed = yield* readJsonInput(file);
1074
+ const policy = yield* apiClient.request(
1075
+ (client) => client.organizations.aiGateway.policies.put(orgId, parsed)
1076
+ );
1077
+ yield* import_effect8.Console.log(`Policy saved${policy.applicationId ? ` for application ${policy.applicationId}` : ""}.`);
1078
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1079
+ );
1080
+ var policiesCommand = import_cli.Command.make("policies", {}, () => import_effect8.Effect.void).pipe(
1081
+ import_cli.Command.withSubcommands([policiesGetCommand, policiesSetCommand])
1082
+ );
1083
+ var inferenceErrorHandlers = {
1084
+ NotAuthenticatedError: (err) => import_effect8.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1085
+ import_effect8.Effect.andThen(import_effect8.Effect.fail(err))
1086
+ ),
1087
+ ApiRequestError: (err) => import_effect8.Console.error(err.message).pipe(import_effect8.Effect.andThen(import_effect8.Effect.fail(err)))
1088
+ };
1089
+ var modelsListCommand = import_cli.Command.make(
1090
+ "list",
1091
+ {},
1092
+ () => import_effect8.Effect.gen(function* () {
1093
+ const apiClient = yield* ApiClientService;
1094
+ const { items } = yield* apiClient.request((client) => client.aiGateway.listModels());
1095
+ for (const m of items) {
1096
+ yield* import_effect8.Console.log(`${m.id} ${m.name} (ctx ${m.contextWindow})`);
1097
+ }
1098
+ }).pipe(import_effect8.Effect.catchTags(inferenceErrorHandlers))
1099
+ );
1100
+ var modelIdArg = import_cli.Args.text({ name: "model-id" }).pipe(import_cli.Args.withDescription("The model ID, e.g. openai/gpt-4o"));
1101
+ var modelsGetCommand = import_cli.Command.make(
1102
+ "get",
1103
+ { modelId: modelIdArg },
1104
+ ({ modelId }) => import_effect8.Effect.gen(function* () {
1105
+ const apiClient = yield* ApiClientService;
1106
+ const model = yield* apiClient.request((client) => client.aiGateway.getModel(modelId));
1107
+ yield* import_effect8.Console.log(JSON.stringify(model, null, 2));
1108
+ }).pipe(import_effect8.Effect.catchTags(inferenceErrorHandlers))
1109
+ );
1110
+ var modelsCommand = import_cli.Command.make("models", {}, () => import_effect8.Effect.void).pipe(
1111
+ import_cli.Command.withSubcommands([modelsListCommand, modelsGetCommand])
1112
+ );
1113
+ var modelOption = import_cli.Options.text("model").pipe(
1114
+ import_cli.Options.withAlias("m"),
1115
+ import_cli.Options.withDescription("The model ID to use, e.g. openai/gpt-4o")
1116
+ );
1117
+ var streamOption = import_cli.Options.boolean("stream").pipe(import_cli.Options.withDescription("Stream the response token by token"));
1118
+ var promptArg = import_cli.Args.text({ name: "prompt" }).pipe(
1119
+ import_cli.Args.withDescription('The prompt text, or "-" to read from stdin')
1120
+ );
1121
+ var chatCommand = import_cli.Command.make(
1122
+ "chat",
1123
+ { prompt: promptArg, model: modelOption, stream: streamOption },
1124
+ ({ prompt, model, stream }) => import_effect8.Effect.gen(function* () {
1125
+ const apiClient = yield* ApiClientService;
1126
+ const text = prompt === "-" ? yield* readStdin : prompt;
1127
+ const messages = [{ role: "user", content: text }];
1128
+ if (stream) {
1129
+ const client = yield* apiClient.getClient;
1130
+ yield* import_effect8.Effect.tryPromise({
1131
+ try: async () => {
1132
+ for await (const chunk of client.aiGateway.chatCompletionsStream({ model, messages })) {
1133
+ process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
1134
+ }
1135
+ process.stdout.write("\n");
1136
+ },
1137
+ catch: (error) => new ApiRequestError({ message: error instanceof Error ? error.message : String(error) })
1138
+ });
1139
+ return;
1140
+ }
1141
+ const response = yield* apiClient.request((client) => client.aiGateway.chatCompletions({ model, messages }));
1142
+ const content = response.choices[0]?.message.content;
1143
+ yield* import_effect8.Console.log(typeof content === "string" ? content : JSON.stringify(content));
1144
+ }).pipe(import_effect8.Effect.catchTags(inferenceErrorHandlers))
1145
+ );
1146
+ var aiBase = import_cli.Command.make("ai", {}, () => import_effect8.Effect.void);
1147
+ var aiCommand = aiBase.pipe(
1148
+ import_cli.Command.withSubcommands([usageCommand, budgetsCommand, policiesCommand, modelsCommand, chatCommand])
1149
+ );
1150
+
1151
+ // src/commands/apps/index.ts
1152
+ var import_cli2 = require("@effect/cli");
1153
+ var import_effect9 = require("effect");
1154
+ var orgOption2 = import_cli2.Options.text("org").pipe(
1155
+ import_cli2.Options.withAlias("o"),
1156
+ import_cli2.Options.withDescription("Only list applications belonging to this organization ID"),
1157
+ import_cli2.Options.optional
1158
+ );
1159
+ var appsListCommand = import_cli2.Command.make(
1160
+ "list",
1161
+ { org: orgOption2 },
1162
+ ({ org }) => import_effect9.Effect.gen(function* () {
1163
+ const apiClient = yield* ApiClientService;
1164
+ const organizationId = import_effect9.Option.getOrUndefined(org);
1165
+ const { items, total } = yield* apiClient.request((client) => client.applications.list({ organizationId }));
1166
+ if (items.length === 0) {
1167
+ yield* import_effect9.Console.log("No applications found.");
1168
+ return;
1169
+ }
1170
+ for (const app of items) {
1171
+ yield* import_effect9.Console.log(`${app.id} ${app.name} (${app.organization.name})`);
1172
+ }
1173
+ yield* import_effect9.Console.log(`
1174
+ ${total} application(s).`);
1175
+ }).pipe(
1176
+ import_effect9.Effect.catchTags({
1177
+ NotAuthenticatedError: (err) => import_effect9.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1178
+ import_effect9.Effect.andThen(import_effect9.Effect.fail(err))
1179
+ ),
1180
+ ApiRequestError: (err) => import_effect9.Console.error(`Failed to list applications: ${err.message}`).pipe(import_effect9.Effect.andThen(import_effect9.Effect.fail(err)))
1181
+ })
1182
+ )
1183
+ );
1184
+ var appsBase = import_cli2.Command.make("apps", {}, () => import_effect9.Effect.void);
1185
+ var appsCommand = appsBase.pipe(import_cli2.Command.withSubcommands([appsListCommand]));
1186
+
1187
+ // src/commands/build/index.ts
1188
+ var import_cli3 = require("@effect/cli");
1189
+ var import_platform5 = require("@effect/platform");
1190
+ var import_build_utils2 = require("@gigadrive/build-utils");
1191
+ var import_effect11 = require("effect");
1192
+
1193
+ // src/services/package-manager.ts
1194
+ var import_platform4 = require("@effect/platform");
1195
+ var import_build_utils = require("@gigadrive/build-utils");
1196
+ var import_effect10 = require("effect");
1197
+ var PackageManagerService = class extends import_effect10.Effect.Service()("PackageManagerService", {
1198
+ accessors: true,
1199
+ effect: import_effect10.Effect.gen(function* () {
1200
+ const fs = yield* import_platform4.FileSystem.FileSystem;
1201
+ const pathService = yield* import_platform4.Path.Path;
1202
+ const detect = import_effect10.Effect.fn("PackageManagerService.detect")(function* (cwd) {
1203
+ yield* import_effect10.Effect.annotateCurrentSpan("cwd", cwd);
1204
+ const packageJsonPath = pathService.join(cwd, "package.json");
1205
+ const exists = yield* fs.exists(packageJsonPath).pipe(
1206
+ import_effect10.Effect.mapError(
1207
+ () => new PackageManagerNotFoundError({ message: "Failed to check for package.json", directory: cwd })
1208
+ )
1209
+ );
1210
+ if (!exists) {
1211
+ return yield* import_effect10.Effect.fail(
1212
+ new PackageManagerNotFoundError({
1213
+ message: "No package.json found",
1214
+ directory: cwd
1215
+ })
1216
+ );
1217
+ }
1218
+ const lockChecks = [
1219
+ { file: "yarn.lock", pm: "yarn" },
1220
+ { file: "pnpm-lock.yaml", pm: "pnpm" },
1221
+ { file: "bun.lockb", pm: "bun" },
1222
+ { file: "package-lock.json", pm: "npm" }
1223
+ ];
1224
+ for (const { file, pm: pm2 } of lockChecks) {
1225
+ const lockExists = yield* fs.exists(pathService.join(cwd, file)).pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.succeed(false)));
1226
+ if (lockExists) {
1227
+ yield* import_effect10.Effect.log("Package manager detected from lock file", { packageManager: pm2, lockFile: file });
1228
+ return pm2;
1229
+ }
1230
+ }
1231
+ const bunAvailable = yield* import_effect10.Effect.tryPromise({
1232
+ try: () => (0, import_build_utils.exec)({ command: "bun --version", cwd }),
1233
+ catch: () => new ExecError({ message: "bun not found", command: "bun --version" })
1234
+ }).pipe(
1235
+ import_effect10.Effect.as(true),
1236
+ import_effect10.Effect.catchTag("ExecError", () => import_effect10.Effect.succeed(false))
1237
+ );
1238
+ const pm = bunAvailable ? "bun" : "npm";
1239
+ yield* import_effect10.Effect.log("Package manager detected via fallback", { packageManager: pm });
1240
+ return pm;
1241
+ });
1242
+ const installCommand = (pm) => ({
1243
+ bun: "bun install",
1244
+ npm: "npm install",
1245
+ pnpm: "pnpm install",
1246
+ yarn: "yarn install"
1247
+ })[pm];
1248
+ const buildCommand2 = (pm) => ({
1249
+ bun: "bun run build",
1250
+ npm: "npm run build",
1251
+ pnpm: "pnpm build",
1252
+ yarn: "yarn build"
1253
+ })[pm];
1254
+ return { detect, installCommand, buildCommand: buildCommand2 };
1255
+ })
1256
+ }) {
1257
+ };
1258
+
1259
+ // src/commands/build/index.ts
1260
+ var buildCommand = import_cli3.Command.make(
1261
+ "build",
1262
+ {},
1263
+ () => import_effect11.Effect.gen(function* () {
1264
+ const pmService = yield* PackageManagerService;
1265
+ const fs = yield* import_platform5.FileSystem.FileSystem;
1266
+ const pathService = yield* import_platform5.Path.Path;
1267
+ const cwd = process.cwd();
1268
+ yield* import_effect11.Effect.log("Starting build", { cwd });
1269
+ const packageJsonPath = pathService.join(cwd, "package.json");
1270
+ const packageJsonExists = yield* fs.exists(packageJsonPath).pipe(
1271
+ import_effect11.Effect.mapError(
1272
+ () => new PackageJsonNotFoundError({ message: "Failed to check for package.json", directory: cwd })
1273
+ )
1274
+ );
1275
+ if (!packageJsonExists) {
1276
+ return yield* import_effect11.Effect.fail(new PackageJsonNotFoundError({ message: "No package.json found", directory: cwd }));
1277
+ }
1278
+ const packageJsonContent = yield* fs.readFileString(packageJsonPath, "utf-8").pipe(
1279
+ import_effect11.Effect.mapError(() => new PackageJsonNotFoundError({ message: "Failed to read package.json", directory: cwd }))
1280
+ );
1281
+ const packageJson = yield* import_effect11.Effect.try({
1282
+ try: () => JSON.parse(packageJsonContent),
1283
+ catch: (error) => new PackageJsonParseError({
1284
+ message: "Failed to parse package.json",
1285
+ directory: cwd,
1286
+ cause: error instanceof Error ? error.message : String(error)
1287
+ })
1288
+ });
1289
+ if (!packageJson.scripts?.build) {
1290
+ return yield* import_effect11.Effect.fail(new BuildScriptNotFoundError({ message: "No build script found in package.json" }));
1291
+ }
1292
+ const pm = yield* pmService.detect(cwd);
1293
+ const buildEnv = {
1294
+ ...process.env,
1295
+ NODE_ENV: "production",
1296
+ NEBULA: "1",
1297
+ VERCEL: "1",
1298
+ NOW_BUILDER: "1"
1299
+ };
1300
+ const installCmd = pmService.installCommand(pm);
1301
+ yield* import_effect11.Console.log(`Running: ${installCmd}`);
1302
+ yield* import_effect11.Effect.tryPromise({
1303
+ try: () => (0, import_build_utils2.exec)({
1304
+ command: installCmd,
1305
+ cwd,
1306
+ env: buildEnv,
1307
+ onOutput: (chunk) => {
1308
+ import_effect11.Console.log(chunk).pipe(import_effect11.Effect.runSync);
1309
+ }
1310
+ }),
1311
+ catch: (error) => new ExecError({
1312
+ message: "Install failed",
1313
+ command: installCmd,
1314
+ cause: error instanceof Error ? error.message : String(error)
1315
+ })
1316
+ });
1317
+ const buildCmd = pmService.buildCommand(pm);
1318
+ yield* import_effect11.Console.log(`Running: ${buildCmd}`);
1319
+ yield* import_effect11.Effect.tryPromise({
1320
+ try: () => (0, import_build_utils2.exec)({
1321
+ command: buildCmd,
1322
+ cwd,
1323
+ env: buildEnv,
1324
+ onOutput: (chunk) => {
1325
+ import_effect11.Console.log(chunk).pipe(import_effect11.Effect.runSync);
1326
+ }
1327
+ }),
1328
+ catch: (error) => new ExecError({
1329
+ message: "Build failed",
1330
+ command: buildCmd,
1331
+ cause: error instanceof Error ? error.message : String(error)
1332
+ })
1333
+ });
1334
+ yield* import_effect11.Console.log("Build completed successfully.");
1335
+ }).pipe(
1336
+ import_effect11.Effect.catchTags({
1337
+ PackageJsonNotFoundError: (err) => import_effect11.Console.error(err.message).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err))),
1338
+ PackageJsonParseError: (err) => import_effect11.Console.error(err.message).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err))),
1339
+ BuildScriptNotFoundError: (err) => import_effect11.Console.error(err.message).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err))),
1340
+ PackageManagerNotFoundError: (err) => import_effect11.Console.error(err.message).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err))),
1341
+ ExecError: (err) => import_effect11.Console.error(`${err.message}: ${err.cause ?? err.command}`).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err)))
1342
+ })
1343
+ )
1344
+ );
1345
+
1346
+ // src/commands/debug/index.ts
1347
+ var import_cli5 = require("@effect/cli");
1348
+ var import_effect14 = require("effect");
1349
+
1350
+ // src/commands/debug/config/index.ts
1351
+ var import_cli4 = require("@effect/cli");
1352
+ var import_effect13 = require("effect");
1353
+
1354
+ // src/services/project-config.ts
1355
+ var import_network_config = require("@gigadrive/network-config");
1356
+ var import_effect12 = require("effect");
1357
+ var wrapParseErrors = (effect) => effect.pipe(
1358
+ import_effect12.Effect.catchTags({
1359
+ ConfigFileNotFoundError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.filePath })),
1360
+ ConfigFileEmptyError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.filePath })),
1361
+ ConfigFileParseError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.cause })),
1362
+ ConfigVersionError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.filePath })),
1363
+ ConfigSchemaValidationError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.validationErrors.join(", ") })),
1364
+ FunctionConfigError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.functionPath }))
1365
+ })
1366
+ );
1367
+ var ProjectConfigService = class extends import_effect12.Effect.Service()("ProjectConfigService", {
1368
+ accessors: true,
1369
+ dependencies: [import_network_config.NetworkConfigLive],
1370
+ effect: import_effect12.Effect.gen(function* () {
1371
+ const rawReader = yield* import_network_config.RawConfigReader;
1372
+ const resolve = import_effect12.Effect.fn("ProjectConfigService.resolve")(function* (cwd) {
1373
+ yield* import_effect12.Effect.annotateCurrentSpan("cwd", cwd);
1374
+ yield* import_effect12.Effect.log("Resolving project config", { cwd });
1375
+ const configPath = yield* rawReader.findConfig(cwd);
1376
+ const detection = yield* (0, import_network_config.detectFramework)(cwd).pipe(
1377
+ import_effect12.Effect.catchTag("FrameworkNotDetectedError", () => import_effect12.Effect.succeed(null)),
1378
+ import_effect12.Effect.catchTag(
1379
+ "ManifestReadError",
1380
+ (e) => import_effect12.Effect.fail(new ConfigParseError({ message: `Framework detection failed: ${e.message}`, cause: e.filePath }))
1381
+ ),
1382
+ import_effect12.Effect.catchTag(
1383
+ "ManifestParseError",
1384
+ (e) => import_effect12.Effect.fail(new ConfigParseError({ message: `Framework detection failed: ${e.message}`, cause: e.filePath }))
1385
+ )
1386
+ );
1387
+ if (detection) {
1388
+ yield* import_effect12.Effect.log("Framework auto-detected", {
1389
+ framework: detection.framework.name,
1390
+ slug: detection.framework.slug
1391
+ });
1392
+ }
1393
+ let config;
1394
+ let resolvedConfigPath = null;
1395
+ let framework;
1396
+ if (configPath && detection) {
1397
+ yield* import_effect12.Effect.log("Config file found, merging with framework defaults", { configPath });
1398
+ resolvedConfigPath = configPath;
1399
+ const userConfig = yield* wrapParseErrors((0, import_network_config.parseConfigRaw)(configPath, cwd));
1400
+ const merged = yield* (0, import_network_config.mergeWithFrameworkDefaults)(userConfig, detection.config);
1401
+ config = yield* wrapParseErrors((0, import_network_config.postProcessConfig)(merged, cwd));
1402
+ framework = { name: detection.framework.name, slug: detection.framework.slug };
1403
+ } else if (configPath) {
1404
+ yield* import_effect12.Effect.log("Config file found", { configPath });
1405
+ resolvedConfigPath = configPath;
1406
+ config = yield* wrapParseErrors((0, import_network_config.parseConfig)(configPath, cwd));
1407
+ } else if (detection) {
1408
+ config = yield* wrapParseErrors((0, import_network_config.postProcessConfig)(detection.config, cwd));
1409
+ framework = { name: detection.framework.name, slug: detection.framework.slug };
1410
+ } else {
1411
+ return yield* import_effect12.Effect.fail(
1412
+ new ConfigNotFoundError({
1413
+ message: "No config file found and no framework detected.",
1414
+ directory: cwd
1415
+ })
1416
+ );
1417
+ }
1418
+ for (const warning of config.warnings) {
1419
+ yield* import_effect12.Effect.logWarning(warning);
914
1420
  }
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
1421
+ if (config.errors.length > 0) {
1422
+ return yield* import_effect12.Effect.fail(
1423
+ new ConfigValidationError({
1424
+ message: "Config file has validation errors",
1425
+ errors: config.errors
924
1426
  })
925
1427
  );
926
1428
  }
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;
1429
+ return { config, configPath: resolvedConfigPath, framework };
932
1430
  });
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 };
1431
+ return { resolve };
943
1432
  })
944
1433
  }) {
945
1434
  };
946
1435
 
1436
+ // src/commands/debug/config/index.ts
1437
+ var debugConfigCommand = import_cli4.Command.make(
1438
+ "config",
1439
+ {},
1440
+ () => import_effect13.Effect.gen(function* () {
1441
+ const projectConfig = yield* ProjectConfigService;
1442
+ const cwd = process.cwd();
1443
+ const { config, configPath } = yield* projectConfig.resolve(cwd);
1444
+ yield* import_effect13.Console.log(`Config file found at: ${configPath}`);
1445
+ yield* import_effect13.Console.log(JSON.stringify(config, null, 2));
1446
+ }).pipe(
1447
+ import_effect13.Effect.catchTags({
1448
+ ConfigNotFoundError: (err) => import_effect13.Console.error(err.message),
1449
+ ConfigParseError: (err) => import_effect13.Console.error(`Config parse error: ${err.message}`),
1450
+ ConfigValidationError: (err) => import_effect13.Effect.gen(function* () {
1451
+ for (const e of err.errors) {
1452
+ yield* import_effect13.Console.error(e);
1453
+ }
1454
+ yield* import_effect13.Console.error(err.message);
1455
+ })
1456
+ })
1457
+ )
1458
+ );
1459
+
1460
+ // src/commands/debug/index.ts
1461
+ var debugBase = import_cli5.Command.make("debug", {}, () => import_effect14.Effect.void);
1462
+ var debugCommand = debugBase.pipe(import_cli5.Command.withSubcommands([debugConfigCommand]));
1463
+
1464
+ // src/commands/deployments/index.ts
1465
+ var import_cli6 = require("@effect/cli");
1466
+ var import_effect15 = require("effect");
1467
+ var DEPLOYMENT_STATUSES = ["PENDING", "QUEUED", "STARTING", "BUILDING", "PROVISIONING", "ACTIVE", "FAILED"];
1468
+ var appOption2 = import_cli6.Options.text("app").pipe(
1469
+ import_cli6.Options.withAlias("a"),
1470
+ import_cli6.Options.withDescription("Filter by application ID (defaults to the linked application)"),
1471
+ import_cli6.Options.optional
1472
+ );
1473
+ var orgOption3 = import_cli6.Options.text("org").pipe(
1474
+ import_cli6.Options.withAlias("o"),
1475
+ import_cli6.Options.withDescription("Filter by organization ID"),
1476
+ import_cli6.Options.optional
1477
+ );
1478
+ var statusOption = import_cli6.Options.choice("status", DEPLOYMENT_STATUSES).pipe(
1479
+ import_cli6.Options.withAlias("s"),
1480
+ import_cli6.Options.withDescription("Filter by deployment status"),
1481
+ import_cli6.Options.optional
1482
+ );
1483
+ var resolveApplicationFilter = (app) => import_effect15.Option.match(app, {
1484
+ onSome: (id) => import_effect15.Effect.succeed(id),
1485
+ onNone: () => import_effect15.Effect.gen(function* () {
1486
+ const projectLink = yield* ProjectLinkService;
1487
+ const link = yield* projectLink.load(process.cwd());
1488
+ return import_effect15.Option.match(link, { onNone: () => void 0, onSome: (l) => l.applicationId });
1489
+ })
1490
+ });
1491
+ var deploymentsListCommand = import_cli6.Command.make(
1492
+ "list",
1493
+ { app: appOption2, org: orgOption3, status: statusOption },
1494
+ ({ app, org, status }) => import_effect15.Effect.gen(function* () {
1495
+ const apiClient = yield* ApiClientService;
1496
+ const applicationId = yield* resolveApplicationFilter(app);
1497
+ const { items, total } = yield* apiClient.request(
1498
+ (client) => client.deployments.list({
1499
+ applicationId,
1500
+ organizationId: import_effect15.Option.getOrUndefined(org),
1501
+ status: import_effect15.Option.getOrUndefined(status)
1502
+ })
1503
+ );
1504
+ if (items.length === 0) {
1505
+ yield* import_effect15.Console.log("No deployments found.");
1506
+ return;
1507
+ }
1508
+ for (const d of items) {
1509
+ yield* import_effect15.Console.log(`${d.id} ${d.status.padEnd(12)} ${d.createdAt}`);
1510
+ }
1511
+ yield* import_effect15.Console.log(`
1512
+ ${total} deployment(s).`);
1513
+ }).pipe(
1514
+ import_effect15.Effect.catchTags({
1515
+ NotAuthenticatedError: (err) => import_effect15.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1516
+ import_effect15.Effect.andThen(import_effect15.Effect.fail(err))
1517
+ ),
1518
+ ApiRequestError: (err) => import_effect15.Console.error(`Failed to list deployments: ${err.message}`).pipe(import_effect15.Effect.andThen(import_effect15.Effect.fail(err))),
1519
+ ProjectLinkReadError: (err) => import_effect15.Console.error(err.message).pipe(import_effect15.Effect.andThen(import_effect15.Effect.fail(err)))
1520
+ })
1521
+ )
1522
+ );
1523
+ var deploymentIdArg = import_cli6.Args.text({ name: "deployment-id" }).pipe(import_cli6.Args.withDescription("The deployment ID to inspect"));
1524
+ var deploymentsInspectCommand = import_cli6.Command.make(
1525
+ "inspect",
1526
+ { deploymentId: deploymentIdArg },
1527
+ ({ deploymentId }) => import_effect15.Effect.gen(function* () {
1528
+ const apiClient = yield* ApiClientService;
1529
+ const deployment = yield* apiClient.request((client) => client.deployments.get(deploymentId));
1530
+ yield* import_effect15.Console.log(`ID: ${deployment.id}`);
1531
+ yield* import_effect15.Console.log(`Application: ${deployment.applicationId}`);
1532
+ yield* import_effect15.Console.log(`Status: ${deployment.status}`);
1533
+ yield* import_effect15.Console.log(`Created: ${deployment.createdAt}`);
1534
+ yield* import_effect15.Console.log(`Updated: ${deployment.updatedAt}`);
1535
+ const hostnames = yield* apiClient.request((client) => client.deployments.getHostnames(deploymentId)).pipe(import_effect15.Effect.catchAll(() => import_effect15.Effect.succeed({ items: [], total: 0 })));
1536
+ if (hostnames.items.length > 0) {
1537
+ yield* import_effect15.Console.log("Hostnames:");
1538
+ for (const h of hostnames.items) {
1539
+ yield* import_effect15.Console.log(` https://${h.hostname}${h.active ? "" : " (inactive)"}`);
1540
+ }
1541
+ }
1542
+ }).pipe(
1543
+ import_effect15.Effect.catchTags({
1544
+ NotAuthenticatedError: (err) => import_effect15.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1545
+ import_effect15.Effect.andThen(import_effect15.Effect.fail(err))
1546
+ ),
1547
+ ApiRequestError: (err) => import_effect15.Console.error(`Failed to inspect deployment: ${err.message}`).pipe(import_effect15.Effect.andThen(import_effect15.Effect.fail(err)))
1548
+ })
1549
+ )
1550
+ );
1551
+ var deploymentsBase = import_cli6.Command.make("deployments", {}, () => import_effect15.Effect.void);
1552
+ var deploymentsCommand = deploymentsBase.pipe(
1553
+ import_cli6.Command.withSubcommands([deploymentsListCommand, deploymentsInspectCommand])
1554
+ );
1555
+
1556
+ // src/commands/env/index.ts
1557
+ var import_cli7 = require("@effect/cli");
1558
+ var import_effect16 = require("effect");
1559
+ var appOption3 = import_cli7.Options.text("app").pipe(
1560
+ import_cli7.Options.withAlias("a"),
1561
+ import_cli7.Options.withDescription("Operate on this application (overrides the linked application)"),
1562
+ import_cli7.Options.optional
1563
+ );
1564
+ var orgOption4 = import_cli7.Options.text("org").pipe(
1565
+ import_cli7.Options.withAlias("o"),
1566
+ import_cli7.Options.withDescription("Operate on this organization instead of an application"),
1567
+ import_cli7.Options.optional
1568
+ );
1569
+ var sensitiveOption = import_cli7.Options.boolean("sensitive").pipe(
1570
+ import_cli7.Options.withDescription("Store the value as sensitive (hidden in API responses)")
1571
+ );
1572
+ var envIdOption = import_cli7.Options.text("env").pipe(
1573
+ import_cli7.Options.withAlias("e"),
1574
+ import_cli7.Options.withDescription("Environment ID for an environment-scoped variable (application scope only)"),
1575
+ import_cli7.Options.optional
1576
+ );
1577
+ var keyValueArg = import_cli7.Args.text({ name: "key=value" }).pipe(
1578
+ import_cli7.Args.withDescription("Variable assignment, e.g. DATABASE_URL=postgres://user:pass@host/db")
1579
+ );
1580
+ var keyOrIdArg = import_cli7.Args.text({ name: "key-or-id" }).pipe(import_cli7.Args.withDescription("Variable key or ID to remove"));
1581
+ var resolveScope = (app, org) => import_effect16.Effect.gen(function* () {
1582
+ if (import_effect16.Option.isSome(org)) {
1583
+ return { kind: "organization", id: org.value };
1584
+ }
1585
+ const projectLink = yield* ProjectLinkService;
1586
+ const id = yield* import_effect16.Option.match(app, {
1587
+ onSome: (value) => import_effect16.Effect.succeed(value),
1588
+ onNone: () => projectLink.resolve(process.cwd()).pipe(import_effect16.Effect.map((link) => link.applicationId))
1589
+ });
1590
+ return { kind: "application", id };
1591
+ });
1592
+ var envListCommand = import_cli7.Command.make(
1593
+ "list",
1594
+ { app: appOption3, org: orgOption4 },
1595
+ ({ app, org }) => import_effect16.Effect.gen(function* () {
1596
+ const apiClient = yield* ApiClientService;
1597
+ const scope = yield* resolveScope(app, org);
1598
+ const { items } = yield* apiClient.request(
1599
+ (client) => scope.kind === "organization" ? client.organizations.envVars.list(scope.id) : client.applications.envVars.list(scope.id)
1600
+ );
1601
+ if (items.length === 0) {
1602
+ yield* import_effect16.Console.log("No environment variables set.");
1603
+ return;
1604
+ }
1605
+ for (const v of items) {
1606
+ yield* import_effect16.Console.log(`${v.key}=${v.sensitive ? "***" : v.value ?? ""}`);
1607
+ }
1608
+ }).pipe(
1609
+ import_effect16.Effect.catchTags({
1610
+ NotAuthenticatedError: (err) => import_effect16.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1611
+ import_effect16.Effect.andThen(import_effect16.Effect.fail(err))
1612
+ ),
1613
+ ApiRequestError: (err) => import_effect16.Console.error(`Failed to list environment variables: ${err.message}`).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1614
+ ProjectNotLinkedError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1615
+ ProjectLinkReadError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err)))
1616
+ })
1617
+ )
1618
+ );
1619
+ var envSetCommand = import_cli7.Command.make(
1620
+ "set",
1621
+ { keyValue: keyValueArg, app: appOption3, org: orgOption4, sensitive: sensitiveOption, env: envIdOption },
1622
+ ({ keyValue, app, org, sensitive, env }) => import_effect16.Effect.gen(function* () {
1623
+ const separator = keyValue.indexOf("=");
1624
+ if (separator <= 0) {
1625
+ return yield* import_effect16.Effect.fail(
1626
+ new InvalidEnvVarFormatError({
1627
+ message: 'Invalid format. Use KEY=VALUE, e.g. "gigadrive env set DATABASE_URL=postgres://...".'
1628
+ })
1629
+ );
1630
+ }
1631
+ const key = keyValue.slice(0, separator);
1632
+ const value = keyValue.slice(separator + 1);
1633
+ const apiClient = yield* ApiClientService;
1634
+ const scope = yield* resolveScope(app, org);
1635
+ const environmentId = import_effect16.Option.getOrUndefined(env);
1636
+ const created = yield* apiClient.request(
1637
+ (client) => scope.kind === "organization" ? client.organizations.envVars.create(scope.id, { key, value, sensitive }) : client.applications.envVars.create(scope.id, { key, value, sensitive, environmentId })
1638
+ );
1639
+ yield* import_effect16.Console.log(`Set ${created.key}.`);
1640
+ }).pipe(
1641
+ import_effect16.Effect.catchTags({
1642
+ InvalidEnvVarFormatError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1643
+ NotAuthenticatedError: (err) => import_effect16.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1644
+ import_effect16.Effect.andThen(import_effect16.Effect.fail(err))
1645
+ ),
1646
+ ApiRequestError: (err) => import_effect16.Console.error(`Failed to set environment variable: ${err.message}`).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1647
+ ProjectNotLinkedError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1648
+ ProjectLinkReadError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err)))
1649
+ })
1650
+ )
1651
+ );
1652
+ var envRmCommand = import_cli7.Command.make(
1653
+ "rm",
1654
+ { keyOrId: keyOrIdArg, app: appOption3, org: orgOption4 },
1655
+ ({ keyOrId, app, org }) => import_effect16.Effect.gen(function* () {
1656
+ const apiClient = yield* ApiClientService;
1657
+ const scope = yield* resolveScope(app, org);
1658
+ const { items } = yield* apiClient.request(
1659
+ (client) => scope.kind === "organization" ? client.organizations.envVars.list(scope.id) : client.applications.envVars.list(scope.id)
1660
+ );
1661
+ const target = items.find((v) => v.id === keyOrId || v.key === keyOrId);
1662
+ if (target === void 0) {
1663
+ return yield* import_effect16.Effect.fail(
1664
+ new EnvVarNotFoundError({ message: `No environment variable matching "${keyOrId}".` })
1665
+ );
1666
+ }
1667
+ yield* apiClient.request(
1668
+ (client) => scope.kind === "organization" ? client.organizations.envVars.delete(scope.id, target.id) : client.applications.envVars.delete(scope.id, target.id)
1669
+ );
1670
+ yield* import_effect16.Console.log(`Removed ${target.key}.`);
1671
+ }).pipe(
1672
+ import_effect16.Effect.catchTags({
1673
+ EnvVarNotFoundError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1674
+ NotAuthenticatedError: (err) => import_effect16.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1675
+ import_effect16.Effect.andThen(import_effect16.Effect.fail(err))
1676
+ ),
1677
+ ApiRequestError: (err) => import_effect16.Console.error(`Failed to remove environment variable: ${err.message}`).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1678
+ ProjectNotLinkedError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1679
+ ProjectLinkReadError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err)))
1680
+ })
1681
+ )
1682
+ );
1683
+ var envBase = import_cli7.Command.make("env", {}, () => import_effect16.Effect.void);
1684
+ var envCommand = envBase.pipe(import_cli7.Command.withSubcommands([envListCommand, envSetCommand, envRmCommand]));
1685
+
1686
+ // src/commands/link/index.ts
1687
+ var import_cli8 = require("@effect/cli");
1688
+ var import_effect17 = require("effect");
1689
+ var appOption4 = import_cli8.Options.text("app").pipe(
1690
+ import_cli8.Options.withAlias("a"),
1691
+ import_cli8.Options.withDescription("Application ID to link (skips the interactive picker)"),
1692
+ import_cli8.Options.optional
1693
+ );
1694
+ var orgOption5 = import_cli8.Options.text("org").pipe(
1695
+ import_cli8.Options.withAlias("o"),
1696
+ import_cli8.Options.withDescription("Only list applications belonging to this organization ID"),
1697
+ import_cli8.Options.optional
1698
+ );
1699
+ var linkCommand = import_cli8.Command.make(
1700
+ "link",
1701
+ { app: appOption4, org: orgOption5 },
1702
+ ({ app, org }) => import_effect17.Effect.gen(function* () {
1703
+ const apiClient = yield* ApiClientService;
1704
+ const projectLink = yield* ProjectLinkService;
1705
+ const cwd = process.cwd();
1706
+ const organizationId = import_effect17.Option.getOrUndefined(org);
1707
+ const { items: apps } = yield* apiClient.request((client) => client.applications.list({ organizationId }));
1708
+ if (apps.length === 0) {
1709
+ return yield* import_effect17.Effect.fail(
1710
+ new NoApplicationsFoundError({
1711
+ message: 'No applications found. Create one in the Gigadrive console, then run "gigadrive link" again.'
1712
+ })
1713
+ );
1714
+ }
1715
+ const explicitAppId = import_effect17.Option.getOrUndefined(app);
1716
+ let applicationId;
1717
+ let linkedOrganizationId;
1718
+ if (explicitAppId !== void 0) {
1719
+ const match = apps.find((a) => a.id === explicitAppId);
1720
+ if (match === void 0) {
1721
+ return yield* import_effect17.Effect.fail(
1722
+ new ApplicationNotFoundError({
1723
+ message: `Application "${explicitAppId}" was not found among your applications.`
1724
+ })
1725
+ );
1726
+ }
1727
+ applicationId = match.id;
1728
+ linkedOrganizationId = match.organization.id;
1729
+ } else if (apps.length === 1) {
1730
+ applicationId = apps[0].id;
1731
+ linkedOrganizationId = apps[0].organization.id;
1732
+ } else {
1733
+ const chosen = yield* import_cli8.Prompt.run(
1734
+ import_cli8.Prompt.select({
1735
+ message: "Select an application to link",
1736
+ choices: apps.map((a) => ({ title: `${a.name} (${a.organization.name})`, value: a }))
1737
+ })
1738
+ );
1739
+ applicationId = chosen.id;
1740
+ linkedOrganizationId = chosen.organization.id;
1741
+ }
1742
+ yield* projectLink.save(cwd, { applicationId, organizationId: linkedOrganizationId });
1743
+ yield* import_effect17.Console.log(`Linked this directory to application ${applicationId}.`);
1744
+ yield* import_effect17.Console.log('Tip: add ".gigadrive/" to your .gitignore.');
1745
+ }).pipe(
1746
+ import_effect17.Effect.catchTags({
1747
+ // Ctrl+C during the picker is a deliberate cancel — exit 0.
1748
+ QuitException: () => import_effect17.Console.log("Cancelled."),
1749
+ NotAuthenticatedError: (err) => import_effect17.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1750
+ import_effect17.Effect.andThen(import_effect17.Effect.fail(err))
1751
+ ),
1752
+ ApiRequestError: (err) => import_effect17.Console.error(`Failed to list applications: ${err.message}`).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err))),
1753
+ NoApplicationsFoundError: (err) => import_effect17.Console.error(err.message).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err))),
1754
+ ApplicationNotFoundError: (err) => import_effect17.Console.error(err.message).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err))),
1755
+ ProjectLinkWriteError: (err) => import_effect17.Console.error(err.message).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err)))
1756
+ })
1757
+ )
1758
+ );
1759
+ var unlinkCommand = import_cli8.Command.make(
1760
+ "unlink",
1761
+ {},
1762
+ () => import_effect17.Effect.gen(function* () {
1763
+ const projectLink = yield* ProjectLinkService;
1764
+ yield* projectLink.remove(process.cwd());
1765
+ yield* import_effect17.Console.log("Unlinked this directory.");
1766
+ }).pipe(
1767
+ import_effect17.Effect.catchTags({
1768
+ ProjectLinkWriteError: (err) => import_effect17.Console.error(err.message).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err)))
1769
+ })
1770
+ )
1771
+ );
1772
+
947
1773
  // src/commands/login/index.ts
948
- var loginCommand = import_cli4.Command.make(
1774
+ var import_cli9 = require("@effect/cli");
1775
+ var import_effect18 = require("effect");
1776
+ var loginCommand = import_cli9.Command.make(
949
1777
  "login",
950
1778
  {},
951
- () => import_effect11.Effect.gen(function* () {
952
- yield* import_effect11.Console.log("Initiating login...");
1779
+ () => import_effect18.Effect.gen(function* () {
1780
+ yield* import_effect18.Console.log("Initiating login...");
953
1781
  const auth = yield* AuthService;
954
1782
  yield* auth.login;
955
- const info = yield* auth.getUserInfo.pipe(import_effect11.Effect.catchAll(() => import_effect11.Effect.succeed({})));
1783
+ const info = yield* auth.getUserInfo.pipe(import_effect18.Effect.catchAll(() => import_effect18.Effect.succeed({})));
956
1784
  const name = auth.inferUserName(info);
957
- yield* import_effect11.Console.log(`You are now logged in as ${name}.`);
1785
+ yield* import_effect18.Console.log(`You are now logged in as ${name}.`);
958
1786
  }).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}`)
1787
+ import_effect18.Effect.catchTags({
1788
+ LoginFlowError: (err) => import_effect18.Console.error(`Login failed: ${err.message}`),
1789
+ TokenExchangeError: (err) => import_effect18.Console.error(`Login failed: ${err.message}`),
1790
+ OAuthDiscoveryError: (err) => import_effect18.Console.error(`Login failed: ${err.message}`),
1791
+ AuthStorageWriteError: (err) => import_effect18.Console.error(`Failed to save credentials: ${err.message}`)
1792
+ })
1793
+ )
1794
+ );
1795
+
1796
+ // src/commands/logout/index.ts
1797
+ var import_cli10 = require("@effect/cli");
1798
+ var import_effect19 = require("effect");
1799
+ var logoutCommand = import_cli10.Command.make(
1800
+ "logout",
1801
+ {},
1802
+ () => import_effect19.Effect.gen(function* () {
1803
+ const auth = yield* AuthService;
1804
+ yield* auth.logout;
1805
+ yield* import_effect19.Console.log("Logged out.");
1806
+ }).pipe(
1807
+ import_effect19.Effect.catchTags({
1808
+ AuthStorageWriteError: (err) => import_effect19.Console.error(`Failed to log out: ${err.message}`).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err)))
964
1809
  })
965
1810
  )
966
1811
  );
967
1812
 
968
1813
  // src/commands/platform/index.ts
969
- var import_cli6 = require("@effect/cli");
970
- var import_effect15 = require("effect");
1814
+ var import_cli12 = require("@effect/cli");
1815
+ var import_effect23 = require("effect");
971
1816
 
972
1817
  // src/commands/platform/deploy/index.ts
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");
1818
+ var import_cli11 = require("@effect/cli");
1819
+ var import_platform7 = require("@effect/platform");
1820
+ var import_commons = require("@gigadrive/commons");
1821
+ var import_effect22 = require("effect");
977
1822
  var os2 = __toESM(require("os"), 1);
978
1823
 
979
1824
  // src/services/archive.ts
980
- var import_platform4 = require("@effect/platform");
1825
+ var import_platform6 = require("@effect/platform");
981
1826
  var import_build_utils3 = require("@gigadrive/build-utils");
982
1827
  var import_archiver = __toESM(require("archiver"), 1);
983
- var import_effect12 = require("effect");
1828
+ var import_effect20 = require("effect");
984
1829
  var import_ignore = __toESM(require("ignore"), 1);
985
1830
  var nodeFs = __toESM(require("fs"), 1);
986
1831
  var nodeFsPromises = __toESM(require("fs/promises"), 1);
987
1832
  var IGNORE_FILE_NAMES = [".gigadriveignore", ".vercelignore", ".dockerignore", ".nowignore"];
988
1833
  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([]))
1834
+ import_effect20.Effect.map((content) => content.split("\n").filter((line) => line.trim() !== "")),
1835
+ import_effect20.Effect.catchAll(() => import_effect20.Effect.succeed([]))
991
1836
  );
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}` })));
1837
+ var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) => import_effect20.Effect.gen(function* () {
1838
+ const items = yield* fs.readDirectory(dir).pipe(import_effect20.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${dir}` })));
994
1839
  for (const item of items) {
995
1840
  const fullPath = pathService.join(dir, item);
996
1841
  const relativePath = pathService.relative(baseDir, fullPath);
997
- const stat = yield* import_effect12.Effect.tryPromise({
1842
+ const stat = yield* import_effect20.Effect.tryPromise({
998
1843
  try: () => nodeFsPromises.lstat(fullPath),
999
1844
  catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
1000
1845
  });
@@ -1014,8 +1859,8 @@ var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) => impo
1014
1859
  yield* collectIgnorePatterns(fs, pathService, fullPath, baseDir, ignoreRules);
1015
1860
  }
1016
1861
  }
1017
- }).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.void));
1018
- var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect12.Effect.gen(function* () {
1862
+ }).pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.void));
1863
+ var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect20.Effect.gen(function* () {
1019
1864
  const ignoreRules = (0, import_ignore.default)();
1020
1865
  ignoreRules.add([".git/", "**/.DS_Store", "**/Thumbs.db"]);
1021
1866
  for (const ignoreFileName of IGNORE_FILE_NAMES) {
@@ -1030,14 +1875,14 @@ var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect12.Effect
1030
1875
  yield* collectIgnorePatterns(fs, pathService, baseDir, baseDir, ignoreRules);
1031
1876
  return ignoreRules;
1032
1877
  });
1033
- var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect12.Effect.gen(function* () {
1878
+ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect20.Effect.gen(function* () {
1034
1879
  const filesToInclude = [];
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}` })));
1880
+ const walkDir = (currentDir) => import_effect20.Effect.gen(function* () {
1881
+ const items = yield* fs.readDirectory(currentDir).pipe(import_effect20.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${currentDir}` })));
1037
1882
  for (const item of items) {
1038
1883
  const fullPath = pathService.join(currentDir, item);
1039
1884
  const relativePath = pathService.relative(dir, fullPath);
1040
- const stat = yield* import_effect12.Effect.tryPromise({
1885
+ const stat = yield* import_effect20.Effect.tryPromise({
1041
1886
  try: () => nodeFsPromises.lstat(fullPath),
1042
1887
  catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
1043
1888
  });
@@ -1051,21 +1896,21 @@ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect12.E
1051
1896
  }
1052
1897
  }
1053
1898
  }
1054
- }).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.void));
1899
+ }).pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.void));
1055
1900
  yield* walkDir(dir);
1056
1901
  return filesToInclude;
1057
1902
  });
1058
- var ArchiveService = class extends import_effect12.Effect.Service()("ArchiveService", {
1903
+ var ArchiveService = class extends import_effect20.Effect.Service()("ArchiveService", {
1059
1904
  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 });
1905
+ effect: import_effect20.Effect.gen(function* () {
1906
+ const fs = yield* import_platform6.FileSystem.FileSystem;
1907
+ const pathService = yield* import_platform6.Path.Path;
1908
+ const createZipArchive = import_effect20.Effect.fn("ArchiveService.createZipArchive")(function* (inputDir, outputFile, options = {}) {
1909
+ yield* import_effect20.Effect.annotateCurrentSpan("inputDir", inputDir);
1910
+ yield* import_effect20.Effect.annotateCurrentSpan("outputFile", outputFile);
1911
+ yield* import_effect20.Effect.log("Creating zip archive", { inputDir, outputFile });
1067
1912
  const ignoreRules = options.useIgnoreFiles !== false ? yield* initializeIgnoreRules(fs, pathService, inputDir) : (0, import_ignore.default)();
1068
- const filesToInclude = options.whitelist ? yield* import_effect12.Effect.tryPromise({
1913
+ const filesToInclude = options.whitelist ? yield* import_effect20.Effect.tryPromise({
1069
1914
  try: () => Promise.all(
1070
1915
  options.whitelist.map((file) => (0, import_build_utils3.getFilesForPattern)(file, inputDir, options.excludeFiles ?? []))
1071
1916
  ),
@@ -1073,25 +1918,25 @@ var ArchiveService = class extends import_effect12.Effect.Service()("ArchiveServ
1073
1918
  message: "Failed to resolve whitelist patterns",
1074
1919
  cause: error instanceof Error ? error.message : String(error)
1075
1920
  })
1076
- }) : yield* import_effect12.Effect.gen(function* () {
1921
+ }) : yield* import_effect20.Effect.gen(function* () {
1077
1922
  if (options.excludeFiles && options.excludeFiles.length > 0) {
1078
1923
  ignoreRules.add(options.excludeFiles);
1079
1924
  }
1080
1925
  return yield* getFilesToInclude(fs, pathService, inputDir, ignoreRules).pipe(
1081
- import_effect12.Effect.map((files) => [files])
1926
+ import_effect20.Effect.map((files) => [files])
1082
1927
  );
1083
1928
  });
1084
- const archiveExists = yield* fs.exists(outputFile).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.succeed(false)));
1929
+ const archiveExists = yield* fs.exists(outputFile).pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.succeed(false)));
1085
1930
  if (archiveExists) {
1086
1931
  yield* fs.remove(outputFile).pipe(
1087
- import_effect12.Effect.mapError(
1932
+ import_effect20.Effect.mapError(
1088
1933
  (error) => new ArchiveCreateError({
1089
1934
  message: `Failed to remove existing archive: ${String(error)}`
1090
1935
  })
1091
1936
  )
1092
1937
  );
1093
1938
  }
1094
- yield* import_effect12.Effect.tryPromise({
1939
+ yield* import_effect20.Effect.tryPromise({
1095
1940
  try: () => new Promise((resolve, reject) => {
1096
1941
  const output = nodeFs.createWriteStream(outputFile);
1097
1942
  const archive = (0, import_archiver.default)("zip", { zlib: { level: 9 } });
@@ -1110,14 +1955,14 @@ var ArchiveService = class extends import_effect12.Effect.Service()("ArchiveServ
1110
1955
  })
1111
1956
  });
1112
1957
  const fileInfo = yield* fs.stat(outputFile).pipe(
1113
- import_effect12.Effect.mapError(
1958
+ import_effect20.Effect.mapError(
1114
1959
  (error) => new ArchiveCreateError({
1115
1960
  message: `Failed to stat archive: ${String(error)}`
1116
1961
  })
1117
1962
  )
1118
1963
  );
1119
1964
  const size = Number(fileInfo.size);
1120
- yield* import_effect12.Effect.log("Archive created", { size });
1965
+ yield* import_effect20.Effect.log("Archive created", { size });
1121
1966
  return { path: outputFile, size };
1122
1967
  });
1123
1968
  return { createZipArchive };
@@ -1126,250 +1971,104 @@ var ArchiveService = class extends import_effect12.Effect.Service()("ArchiveServ
1126
1971
  };
1127
1972
 
1128
1973
  // 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", {
1974
+ var import_effect21 = require("effect");
1975
+ var DeploymentApiService = class extends import_effect21.Effect.Service()("DeploymentApiService", {
1152
1976
  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
- }),
1977
+ dependencies: [ApiClientService.Default],
1978
+ effect: import_effect21.Effect.gen(function* () {
1979
+ const apiClient = yield* ApiClientService;
1980
+ const getClient = apiClient.getClient.pipe(
1981
+ import_effect21.Effect.mapError((err) => new DeploymentAuthError({ message: `Authentication failed: ${err.message}` }))
1982
+ );
1983
+ const createDeployment = import_effect21.Effect.fn("DeploymentApiService.createDeployment")(function* (applicationId) {
1984
+ yield* import_effect21.Effect.annotateCurrentSpan("applicationId", applicationId);
1985
+ yield* import_effect21.Effect.log("Creating deployment", { applicationId });
1986
+ const client = yield* getClient;
1987
+ const deployment = yield* import_effect21.Effect.tryPromise({
1988
+ try: () => client.deployments.create({ applicationId }),
1172
1989
  catch: (error) => new DeploymentCreateError({
1173
1990
  message: `Failed to create deployment: ${error instanceof Error ? error.message : String(error)}`
1174
1991
  })
1175
1992
  });
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
- );
1190
- }
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;
1993
+ yield* import_effect21.Effect.log("Deployment created", { deploymentId: deployment.id });
1994
+ return deployment.id;
1200
1995
  });
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
- }),
1996
+ const startMultipartUpload = import_effect21.Effect.fn("DeploymentApiService.startMultipartUpload")(function* (deploymentId) {
1997
+ yield* import_effect21.Effect.annotateCurrentSpan("deploymentId", deploymentId);
1998
+ const client = yield* getClient;
1999
+ const result = yield* import_effect21.Effect.tryPromise({
2000
+ try: () => client.deployments.startUpload(deploymentId),
1209
2001
  catch: (error) => new UploadStartError({
1210
2002
  message: `Failed to start multipart upload: ${error instanceof Error ? error.message : String(error)}`
1211
2003
  })
1212
2004
  });
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" })
1217
- });
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;
2005
+ return result.uploadId;
1230
2006
  });
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 }
1239
- }
1240
- ),
2007
+ const getPresignedUrl = import_effect21.Effect.fn("DeploymentApiService.getPresignedUrl")(function* (deploymentId, uploadId, partNumber) {
2008
+ const client = yield* getClient;
2009
+ const result = yield* import_effect21.Effect.tryPromise({
2010
+ try: () => client.deployments.getPresignedUrl(deploymentId, uploadId, partNumber),
1241
2011
  catch: (error) => new PresignedUrlError({
1242
2012
  message: `Failed to get presigned URL: ${error instanceof Error ? error.message : String(error)}`,
1243
2013
  partNumber
1244
2014
  })
1245
2015
  });
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
- );
1254
- }
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;
2016
+ return result.url;
1263
2017
  });
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
- }),
2018
+ const uploadPart = import_effect21.Effect.fn("DeploymentApiService.uploadPart")(function* (presignedUrl, data, partNumber) {
2019
+ yield* import_effect21.Effect.annotateCurrentSpan("partNumber", partNumber);
2020
+ yield* import_effect21.Effect.annotateCurrentSpan("size", data.length);
2021
+ const client = yield* getClient;
2022
+ const result = yield* import_effect21.Effect.tryPromise({
2023
+ try: () => client.deployments.uploadPart(presignedUrl, data, partNumber),
1276
2024
  catch: (error) => new UploadPartError({
1277
2025
  message: `Failed to upload part ${partNumber}: ${error instanceof Error ? error.message : String(error)}`,
1278
2026
  partNumber
1279
2027
  })
1280
2028
  });
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
- );
1288
- }
1289
- const etag = response.headers.get("ETag");
1290
- if (!etag) {
1291
- return yield* import_effect13.Effect.fail(
1292
- new UploadPartError({ message: `No ETag received for part ${partNumber}`, partNumber })
1293
- );
1294
- }
1295
- return { partNumber, etag };
2029
+ return result;
1296
2030
  });
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
- }),
2031
+ const completeUpload = import_effect21.Effect.fn("DeploymentApiService.completeUpload")(function* (deploymentId, uploadId, parts) {
2032
+ yield* import_effect21.Effect.annotateCurrentSpan("deploymentId", deploymentId);
2033
+ yield* import_effect21.Effect.annotateCurrentSpan("partsCount", parts.length);
2034
+ const client = yield* getClient;
2035
+ yield* import_effect21.Effect.tryPromise({
2036
+ try: () => client.deployments.completeUpload(deploymentId, uploadId, parts),
1307
2037
  catch: (error) => new UploadCompleteError({
1308
2038
  message: `Failed to complete upload: ${error instanceof Error ? error.message : String(error)}`
1309
2039
  })
1310
2040
  });
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}` }));
1317
- }
1318
- yield* import_effect13.Effect.log("Upload completed successfully");
2041
+ yield* import_effect21.Effect.log("Upload completed successfully");
1319
2042
  });
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
- }),
2043
+ const getDeploymentStatus = import_effect21.Effect.fn("DeploymentApiService.getDeploymentStatus")(function* (deploymentId) {
2044
+ const client = yield* getClient;
2045
+ const deployment = yield* import_effect21.Effect.tryPromise({
2046
+ try: () => client.deployments.get(deploymentId),
1327
2047
  catch: (error) => new DeploymentStatusError({
1328
2048
  message: `Failed to get deployment status: ${error instanceof Error ? error.message : String(error)}`
1329
2049
  })
1330
2050
  });
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;
2051
+ return deployment.status;
1346
2052
  });
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
- }),
2053
+ const getLogs = import_effect21.Effect.fn("DeploymentApiService.getLogs")(function* (deploymentId, options) {
2054
+ const client = yield* getClient;
2055
+ const result = yield* import_effect21.Effect.tryPromise({
2056
+ try: () => client.deployments.getLogs(deploymentId, options),
1354
2057
  catch: (error) => new DeploymentLogsFetchError({
1355
2058
  message: `Failed to get logs: ${error instanceof Error ? error.message : String(error)}`
1356
2059
  })
1357
2060
  });
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" })
2061
+ return result;
2062
+ });
2063
+ const getHostnames = import_effect21.Effect.fn("DeploymentApiService.getHostnames")(function* (deploymentId) {
2064
+ const client = yield* getClient;
2065
+ const result = yield* import_effect21.Effect.tryPromise({
2066
+ try: () => client.deployments.getHostnames(deploymentId),
2067
+ catch: (error) => new DeploymentHostnamesFetchError({
2068
+ message: `Failed to get hostnames: ${error instanceof Error ? error.message : String(error)}`
2069
+ })
1368
2070
  });
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;
2071
+ return result.items;
1373
2072
  });
1374
2073
  return {
1375
2074
  createDeployment,
@@ -1378,28 +2077,39 @@ var DeploymentApiService = class extends import_effect13.Effect.Service()("Deplo
1378
2077
  uploadPart,
1379
2078
  completeUpload,
1380
2079
  getDeploymentStatus,
1381
- getLogs
2080
+ getLogs,
2081
+ getHostnames
1382
2082
  };
1383
2083
  })
1384
2084
  }) {
1385
2085
  };
1386
2086
 
1387
2087
  // src/commands/platform/deploy/index.ts
1388
- var deployCommand = import_cli5.Command.make(
2088
+ var appOption5 = import_cli11.Options.text("app").pipe(
2089
+ import_cli11.Options.withAlias("a"),
2090
+ import_cli11.Options.withDescription("Application ID to deploy to (overrides the linked application)"),
2091
+ import_cli11.Options.optional
2092
+ );
2093
+ var deployCommand = import_cli11.Command.make(
1389
2094
  "deploy",
1390
- {},
1391
- () => import_effect14.Effect.gen(function* () {
2095
+ { app: appOption5 },
2096
+ ({ app }) => import_effect22.Effect.gen(function* () {
1392
2097
  const projectConfig = yield* ProjectConfigService;
1393
2098
  const deploymentApi = yield* DeploymentApiService;
1394
2099
  const archiveService = yield* ArchiveService;
1395
- const pathService = yield* import_platform5.Path.Path;
2100
+ const projectLink = yield* ProjectLinkService;
2101
+ const pathService = yield* import_platform7.Path.Path;
1396
2102
  const cwd = process.cwd();
1397
- yield* import_effect14.Effect.log("Starting deploy command");
2103
+ const applicationId = yield* import_effect22.Option.match(app, {
2104
+ onSome: (id) => import_effect22.Effect.succeed(id),
2105
+ onNone: () => projectLink.resolve(cwd).pipe(import_effect22.Effect.map((link) => link.applicationId))
2106
+ });
2107
+ yield* import_effect22.Effect.log("Starting deploy command");
1398
2108
  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...");
2109
+ yield* import_effect22.Console.log("Creating deployment...");
2110
+ const deploymentId = yield* deploymentApi.createDeployment(applicationId);
2111
+ yield* import_effect22.Console.log(`Deployment ID: ${deploymentId}`);
2112
+ yield* import_effect22.Console.log("Creating archive...");
1403
2113
  const archivePath = pathService.join(os2.tmpdir(), `project-${Date.now()}.zip`);
1404
2114
  const archive = yield* archiveService.createZipArchive(config.userArchive?.rootOverwrite || cwd, archivePath, {
1405
2115
  whitelist: config.userArchive?.fileWhitelist,
@@ -1407,60 +2117,62 @@ var deployCommand = import_cli5.Command.make(
1407
2117
  useIgnoreFiles: false,
1408
2118
  useManagedIgnore: false
1409
2119
  });
1410
- yield* import_effect14.Console.log(`Archive created (${(0, import_commons2.formatFileSize)(archive.size)})`);
1411
- yield* import_effect14.Console.log("Uploading archive...");
2120
+ yield* import_effect22.Console.log(`Archive created (${(0, import_commons.formatFileSize)(archive.size)})`);
2121
+ yield* import_effect22.Console.log("Uploading archive...");
1412
2122
  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.");
2123
+ yield* import_effect22.Console.log("Upload complete.");
2124
+ yield* import_effect22.Console.log("The deployment pipeline is now being provisioned. This may take a few seconds.");
1415
2125
  yield* pollDeployment(deploymentId);
1416
2126
  }).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* () {
2127
+ import_effect22.Effect.catchTags({
2128
+ ProjectNotLinkedError: (err) => import_effect22.Console.error(err.message).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2129
+ ProjectLinkReadError: (err) => import_effect22.Console.error(err.message).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2130
+ ConfigNotFoundError: (err) => import_effect22.Console.error(err.message).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2131
+ ConfigParseError: (err) => import_effect22.Console.error(`Config parse error: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2132
+ ConfigValidationError: (err) => import_effect22.Effect.gen(function* () {
1421
2133
  for (const e of err.errors) {
1422
- yield* import_effect14.Console.error(e);
2134
+ yield* import_effect22.Console.error(e);
1423
2135
  }
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)))
2136
+ yield* import_effect22.Console.error(err.message);
2137
+ }).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2138
+ DeploymentCreateError: (err) => import_effect22.Console.error(`Deployment creation failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2139
+ ArchiveCreateError: (err) => import_effect22.Console.error(`Archive creation failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2140
+ UploadStartError: (err) => import_effect22.Console.error(`Upload failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2141
+ UploadPartError: (err) => import_effect22.Console.error(`Upload failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2142
+ UploadCompleteError: (err) => import_effect22.Console.error(`Upload failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2143
+ PresignedUrlError: (err) => import_effect22.Console.error(`Upload failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2144
+ DeploymentAuthError: (err) => import_effect22.Console.error(`Authentication failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2145
+ DeploymentFailedError: (err) => import_effect22.Console.error(err.message).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err)))
1434
2146
  })
1435
2147
  )
1436
2148
  );
1437
- var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect14.Effect.gen(function* () {
2149
+ var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect22.Effect.gen(function* () {
1438
2150
  const deploymentApi = yield* DeploymentApiService;
1439
- const fs = yield* import_platform5.FileSystem.FileSystem;
2151
+ const fs = yield* import_platform7.FileSystem.FileSystem;
1440
2152
  const uploadId = yield* deploymentApi.startMultipartUpload(deploymentId);
1441
2153
  const FILE_CHUNK_SIZE = 10 * 1024 * 1024;
1442
2154
  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(
2155
+ yield* import_effect22.Effect.log("Starting multipart upload", { uploadId, numChunks, fileSize });
2156
+ const parts = yield* import_effect22.Effect.all(
1445
2157
  Array.from({ length: numChunks }, (_, i) => {
1446
2158
  const partNumber = i + 1;
1447
2159
  const start = i * FILE_CHUNK_SIZE;
1448
2160
  const bytesToRead = partNumber < numChunks ? FILE_CHUNK_SIZE : fileSize - start;
1449
- return import_effect14.Effect.gen(function* () {
2161
+ return import_effect22.Effect.gen(function* () {
1450
2162
  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) => {
2163
+ const chunkData = yield* import_effect22.Stream.runCollect(fs.stream(archivePath, { offset: start, bytesToRead })).pipe(
2164
+ import_effect22.Effect.map((chunks) => {
1453
2165
  const arrays = Array.from(chunks);
1454
2166
  return Buffer.concat(arrays);
1455
2167
  }),
1456
- import_effect14.Effect.mapError(
2168
+ import_effect22.Effect.mapError(
1457
2169
  (error) => new UploadPartError({
1458
2170
  message: `Failed to read chunk ${partNumber}: ${String(error)}`,
1459
2171
  partNumber
1460
2172
  })
1461
2173
  )
1462
2174
  );
1463
- yield* import_effect14.Effect.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
2175
+ yield* import_effect22.Effect.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
1464
2176
  return yield* deploymentApi.uploadPart(presignedUrl, chunkData, partNumber);
1465
2177
  });
1466
2178
  }),
@@ -1468,20 +2180,20 @@ var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect14.Eff
1468
2180
  );
1469
2181
  yield* deploymentApi.completeUpload(deploymentId, uploadId, parts);
1470
2182
  });
1471
- var pollDeployment = (deploymentId) => import_effect14.Effect.gen(function* () {
2183
+ var pollDeployment = (deploymentId) => import_effect22.Effect.gen(function* () {
1472
2184
  const deploymentApi = yield* DeploymentApiService;
1473
2185
  let lastStatus = "PENDING";
1474
2186
  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)));
2187
+ yield* import_effect22.Console.log("Status: Pending");
2188
+ yield* import_effect22.Effect.repeat(
2189
+ import_effect22.Effect.gen(function* () {
2190
+ const newStatus = yield* deploymentApi.getDeploymentStatus(deploymentId).pipe(import_effect22.Effect.catchTag("DeploymentStatusError", () => import_effect22.Effect.succeed(lastStatus)));
1479
2191
  if (newStatus !== lastStatus) {
1480
2192
  lastStatus = newStatus;
1481
2193
  const label = newStatus.charAt(0) + newStatus.slice(1).toLowerCase();
1482
- yield* import_effect14.Console.log(`Status: ${label}`);
2194
+ yield* import_effect22.Console.log(`Status: ${label}`);
1483
2195
  }
1484
- const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(import_effect14.Effect.catchTag("DeploymentLogsFetchError", () => import_effect14.Effect.succeed(null)));
2196
+ const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(import_effect22.Effect.catchTag("DeploymentLogsFetchError", () => import_effect22.Effect.succeed(null)));
1485
2197
  if (logsPage) {
1486
2198
  for (const logEntry of logsPage.items) {
1487
2199
  logOffset++;
@@ -1489,79 +2201,100 @@ var pollDeployment = (deploymentId) => import_effect14.Effect.gen(function* () {
1489
2201
  }
1490
2202
  }
1491
2203
  if (newStatus === "ACTIVE") {
1492
- yield* import_effect14.Console.log(`Deployed to https://${deploymentId}.gigadrivedev.com`);
1493
- return yield* import_effect14.Effect.fail("done");
2204
+ const hostnames = yield* deploymentApi.getHostnames(deploymentId).pipe(import_effect22.Effect.catchTag("DeploymentHostnamesFetchError", () => import_effect22.Effect.succeed([])));
2205
+ const active = hostnames.find((h) => h.active) ?? hostnames.at(0);
2206
+ if (active) {
2207
+ yield* import_effect22.Console.log(`Deployed to https://${active.hostname}`);
2208
+ } else {
2209
+ yield* import_effect22.Console.log("Deployment is live.");
2210
+ }
2211
+ return yield* import_effect22.Effect.fail("done");
1494
2212
  }
1495
2213
  if (newStatus === "FAILED") {
1496
- return yield* import_effect14.Effect.fail(
2214
+ return yield* import_effect22.Effect.fail(
1497
2215
  new DeploymentFailedError({
1498
2216
  message: "The deployment failed. Please check the logs for more information."
1499
2217
  })
1500
2218
  );
1501
2219
  }
1502
2220
  }),
1503
- import_effect14.Schedule.addDelay(import_effect14.Schedule.forever, () => import_effect14.Duration.seconds(1))
2221
+ import_effect22.Schedule.addDelay(import_effect22.Schedule.forever, () => import_effect22.Duration.seconds(1))
1504
2222
  ).pipe(
1505
- import_effect14.Effect.catchAll((err) => {
1506
- if (err === "done") return import_effect14.Effect.void;
1507
- return import_effect14.Effect.fail(err);
2223
+ import_effect22.Effect.catchAll((err) => {
2224
+ if (err === "done") return import_effect22.Effect.void;
2225
+ return import_effect22.Effect.fail(err);
1508
2226
  })
1509
2227
  );
1510
2228
  });
1511
2229
  var printLog = (logEntry) => {
1512
2230
  switch (logEntry.type) {
1513
2231
  case "ERROR":
1514
- return import_effect14.Console.error(logEntry.message);
2232
+ return import_effect22.Console.error(logEntry.message);
1515
2233
  case "WARN":
1516
- return import_effect14.Console.warn(logEntry.message);
2234
+ return import_effect22.Console.warn(logEntry.message);
1517
2235
  default:
1518
- return import_effect14.Console.log(logEntry.message);
2236
+ return import_effect22.Console.log(logEntry.message);
1519
2237
  }
1520
2238
  };
1521
2239
 
1522
2240
  // src/commands/platform/index.ts
1523
- var platformBase = import_cli6.Command.make("platform", {}, () => import_effect15.Effect.void);
1524
- var platformCommand = platformBase.pipe(import_cli6.Command.withSubcommands([deployCommand]));
2241
+ var platformBase = import_cli12.Command.make("platform", {}, () => import_effect23.Effect.void);
2242
+ var platformCommand = platformBase.pipe(import_cli12.Command.withSubcommands([deployCommand]));
1525
2243
 
1526
2244
  // 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(
2245
+ var import_cli13 = require("@effect/cli");
2246
+ var import_effect24 = require("effect");
2247
+ var whoamiCommand = import_cli13.Command.make(
1530
2248
  "whoami",
1531
2249
  {},
1532
- () => import_effect16.Effect.gen(function* () {
2250
+ () => import_effect24.Effect.gen(function* () {
1533
2251
  const auth = yield* AuthService;
1534
2252
  const info = yield* auth.getUserInfo;
1535
- yield* import_effect16.Console.log(JSON.stringify(info, null, 2));
2253
+ yield* import_effect24.Console.log(JSON.stringify(info, null, 2));
1536
2254
  }).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}`)
2255
+ import_effect24.Effect.catchTags({
2256
+ NotAuthenticatedError: () => import_effect24.Console.log('You are not logged in. Run "gigadrive login" to authenticate.'),
2257
+ UserInfoFetchError: (err) => import_effect24.Console.error(`Failed to retrieve user info: ${err.message}`),
2258
+ OAuthDiscoveryError: (err) => import_effect24.Console.error(`Failed to retrieve user info: ${err.message}`)
1541
2259
  })
1542
2260
  )
1543
2261
  );
1544
2262
 
1545
2263
  // src/index.ts
1546
- var gigadrive = import_cli8.Command.make("gigadrive", {}, () => import_effect17.Effect.void).pipe(
1547
- import_cli8.Command.withSubcommands([loginCommand, whoamiCommand, buildCommand, debugCommand, platformCommand])
2264
+ var gigadrive = import_cli14.Command.make("gigadrive", {}, () => import_effect25.Effect.void).pipe(
2265
+ import_cli14.Command.withSubcommands([
2266
+ loginCommand,
2267
+ logoutCommand,
2268
+ whoamiCommand,
2269
+ linkCommand,
2270
+ unlinkCommand,
2271
+ appsCommand,
2272
+ envCommand,
2273
+ deploymentsCommand,
2274
+ aiCommand,
2275
+ buildCommand,
2276
+ debugCommand,
2277
+ platformCommand
2278
+ ])
1548
2279
  );
1549
- var cli = import_cli8.Command.run(gigadrive, {
2280
+ var cli = import_cli14.Command.run(gigadrive, {
1550
2281
  name: "Gigadrive CLI",
1551
- version: "2.0.0"
2282
+ version: package_default.version
1552
2283
  });
1553
- var BaseServices = import_effect17.Layer.mergeAll(
2284
+ var BaseServices = import_effect25.Layer.mergeAll(
1554
2285
  OAuthConfigService.Default,
1555
2286
  AuthStorageService.Default,
1556
2287
  PackageManagerService.Default,
1557
2288
  ArchiveService.Default,
1558
2289
  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)
2290
+ ProjectConfigService.Default,
2291
+ ProjectLinkService.Default
2292
+ ).pipe(import_effect25.Layer.provideMerge(AuthService.Default));
2293
+ var ApiClientLive = import_effect25.Layer.provide(ApiClientService.Default, BaseServices);
2294
+ var DeploymentApiLive = import_effect25.Layer.provide(DeploymentApiService.Default, BaseServices);
2295
+ var ServicesLive = import_effect25.Layer.mergeAll(BaseServices, ApiClientLive, DeploymentApiLive);
2296
+ var AppLive = import_effect25.Layer.mergeAll(ServicesLive, import_effect25.Logger.minimumLogLevel(import_effect25.LogLevel.Info)).pipe(
2297
+ import_effect25.Layer.provideMerge(import_platform_node.NodeContext.layer)
1565
2298
  );
1566
- import_platform_node.NodeRuntime.runMain(import_effect17.Effect.provide(cli(process.argv), AppLive));
2299
+ import_platform_node.NodeRuntime.runMain(import_effect25.Effect.provide(cli(process.argv), AppLive));
1567
2300
  //# sourceMappingURL=index.cjs.map