gigadrive 2.1.3 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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_cli15 = 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_effect29 = require("effect");
31
31
 
32
- // src/commands/build/index.ts
32
+ // package.json
33
+ var package_default = {
34
+ name: "gigadrive",
35
+ version: "2.3.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,82 @@ 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
+ };
161
+ var EnvFileReadError = class extends import_effect.Schema.TaggedError()("EnvFileReadError", {
162
+ message: import_effect.Schema.String,
163
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
164
+ }) {
165
+ };
166
+ var EnvFileWriteError = class extends import_effect.Schema.TaggedError()("EnvFileWriteError", {
167
+ message: import_effect.Schema.String,
168
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
169
+ }) {
170
+ };
171
+ var DevCredentialsStoreReadError = class extends import_effect.Schema.TaggedError()(
172
+ "DevCredentialsStoreReadError",
173
+ {
174
+ message: import_effect.Schema.String
175
+ }
176
+ ) {
177
+ };
178
+ var DevCredentialsStoreWriteError = class extends import_effect.Schema.TaggedError()(
179
+ "DevCredentialsStoreWriteError",
180
+ {
181
+ message: import_effect.Schema.String,
182
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
183
+ }
184
+ ) {
185
+ };
78
186
  var ConfigNotFoundError = class extends import_effect.Schema.TaggedError()("ConfigNotFoundError", {
79
187
  message: import_effect.Schema.String,
80
188
  directory: import_effect.Schema.String
81
189
  }) {
82
190
  };
191
+ var ProjectNotLinkedError = class extends import_effect.Schema.TaggedError()("ProjectNotLinkedError", {
192
+ message: import_effect.Schema.String,
193
+ directory: import_effect.Schema.String
194
+ }) {
195
+ };
196
+ var ProjectLinkReadError = class extends import_effect.Schema.TaggedError()("ProjectLinkReadError", {
197
+ message: import_effect.Schema.String,
198
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
199
+ }) {
200
+ };
201
+ var ProjectLinkWriteError = class extends import_effect.Schema.TaggedError()("ProjectLinkWriteError", {
202
+ message: import_effect.Schema.String,
203
+ cause: import_effect.Schema.optional(import_effect.Schema.String)
204
+ }) {
205
+ };
83
206
  var ConfigParseError = class extends import_effect.Schema.TaggedError()("ConfigParseError", {
84
207
  message: import_effect.Schema.String,
85
208
  cause: import_effect.Schema.optional(import_effect.Schema.String)
@@ -160,6 +283,13 @@ var DeploymentLogsFetchError = class extends import_effect.Schema.TaggedError()(
160
283
  }
161
284
  ) {
162
285
  };
286
+ var DeploymentHostnamesFetchError = class extends import_effect.Schema.TaggedError()(
287
+ "DeploymentHostnamesFetchError",
288
+ {
289
+ message: import_effect.Schema.String
290
+ }
291
+ ) {
292
+ };
163
293
  var ArchiveCreateError = class extends import_effect.Schema.TaggedError()("ArchiveCreateError", {
164
294
  message: import_effect.Schema.String,
165
295
  cause: import_effect.Schema.optional(import_effect.Schema.String)
@@ -174,431 +304,184 @@ var DeploymentFailedError = class extends import_effect.Schema.TaggedError()("De
174
304
  }) {
175
305
  };
176
306
 
177
- // src/services/package-manager.ts
307
+ // src/services/api-client.ts
308
+ var import_sdk = require("@gigadrive/sdk");
309
+ var import_effect6 = require("effect");
310
+
311
+ // src/services/auth.ts
312
+ var import_effect5 = require("effect");
313
+ var crypto = __toESM(require("crypto"), 1);
314
+ var http = __toESM(require("http"), 1);
315
+ var import_open = __toESM(require("open"), 1);
316
+
317
+ // src/services/auth-storage.ts
178
318
  var import_platform = require("@effect/platform");
179
- var import_build_utils = require("@gigadrive/build-utils");
319
+ var import_effect3 = require("effect");
320
+ var os = __toESM(require("os"), 1);
321
+
322
+ // src/domain.ts
180
323
  var import_effect2 = require("effect");
181
- var PackageManagerService = class extends import_effect2.Effect.Service()("PackageManagerService", {
324
+ var DeploymentId = import_effect2.Schema.String.pipe(import_effect2.Schema.brand("@Gigadrive/DeploymentId"));
325
+ var ApplicationId = import_effect2.Schema.String.pipe(import_effect2.Schema.brand("@Gigadrive/ApplicationId"));
326
+ var UploadId = import_effect2.Schema.String.pipe(import_effect2.Schema.brand("@Gigadrive/UploadId"));
327
+ var PackageManager = import_effect2.Schema.Literal("npm", "yarn", "pnpm", "bun");
328
+ var OAuthTokens = import_effect2.Schema.Struct({
329
+ access_token: import_effect2.Schema.String,
330
+ refresh_token: import_effect2.Schema.optional(import_effect2.Schema.String),
331
+ expires_in: import_effect2.Schema.optional(import_effect2.Schema.Number),
332
+ token_type: import_effect2.Schema.optional(import_effect2.Schema.String)
333
+ });
334
+ var OAuthClientConfig = import_effect2.Schema.Struct({
335
+ clientId: import_effect2.Schema.String,
336
+ issuer: import_effect2.Schema.String,
337
+ authorizeUrl: import_effect2.Schema.String,
338
+ tokenUrl: import_effect2.Schema.String,
339
+ scope: import_effect2.Schema.String,
340
+ userinfoUrl: import_effect2.Schema.String
341
+ });
342
+ var StoredAuthData = import_effect2.Schema.Struct({
343
+ refreshToken: import_effect2.Schema.String,
344
+ accessToken: import_effect2.Schema.optional(import_effect2.Schema.String),
345
+ tokenExpirationTime: import_effect2.Schema.optional(import_effect2.Schema.Number)
346
+ });
347
+ var ProjectLink = import_effect2.Schema.Struct({
348
+ applicationId: import_effect2.Schema.String,
349
+ organizationId: import_effect2.Schema.optional(import_effect2.Schema.String)
350
+ });
351
+ var DevKeyEntry = import_effect2.Schema.Struct({
352
+ apiKeyId: import_effect2.Schema.String
353
+ });
354
+ var DevKeyStore = import_effect2.Schema.Record({ key: import_effect2.Schema.String, value: DevKeyEntry });
355
+
356
+ // src/services/auth-storage.ts
357
+ var AuthStorageService = class extends import_effect3.Effect.Service()("AuthStorageService", {
182
358
  accessors: true,
183
- effect: import_effect2.Effect.gen(function* () {
359
+ effect: import_effect3.Effect.gen(function* () {
184
360
  const fs = yield* import_platform.FileSystem.FileSystem;
185
361
  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 })
362
+ const homeDir = os.homedir();
363
+ const authDir = pathService.join(homeDir, ".gigadrive");
364
+ const authFile = pathService.join(authDir, "auth.json");
365
+ const ensureDirectory = import_effect3.Effect.gen(function* () {
366
+ yield* fs.makeDirectory(authDir, { recursive: true }).pipe(
367
+ import_effect3.Effect.mapError(
368
+ (error) => new AuthStorageWriteError({
369
+ message: "Failed to create auth directory",
370
+ cause: String(error)
371
+ })
192
372
  )
193
373
  );
194
- if (!exists) {
195
- return yield* import_effect2.Effect.fail(
196
- new PackageManagerNotFoundError({
197
- message: "No package.json found",
198
- directory: cwd
374
+ yield* fs.chmod(authDir, 448).pipe(
375
+ import_effect3.Effect.mapError(
376
+ (error) => new AuthStorageWriteError({
377
+ message: "Failed to set auth directory permissions",
378
+ cause: String(error)
199
379
  })
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))
380
+ )
221
381
  );
222
- const pm = bunAvailable ? "bun" : "npm";
223
- yield* import_effect2.Effect.log("Package manager detected via fallback", { packageManager: pm });
224
- return pm;
225
382
  });
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
- })
383
+ const load = import_effect3.Effect.gen(function* () {
384
+ const exists = yield* fs.exists(authFile).pipe(import_effect3.Effect.mapError(() => new AuthStorageReadError({ message: "Failed to check auth file existence" })));
385
+ if (!exists) return import_effect3.Option.none();
386
+ const data = yield* fs.readFileString(authFile, "utf8").pipe(import_effect3.Effect.mapError(() => new AuthStorageReadError({ message: "Failed to read auth file" })));
387
+ const json = yield* import_effect3.Effect.try({
388
+ try: () => JSON.parse(data),
389
+ catch: () => new AuthStorageReadError({ message: "Auth file contains invalid JSON" })
390
+ });
391
+ const authData = yield* import_effect3.Schema.decodeUnknown(StoredAuthData)(json).pipe(
392
+ import_effect3.Effect.mapError(() => new AuthStorageReadError({ message: "Auth file has invalid schema" }))
393
+ );
394
+ return import_effect3.Option.some(authData);
272
395
  });
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
- })
396
+ const save = import_effect3.Effect.fn("AuthStorageService.save")(function* (authData) {
397
+ yield* ensureDirectory;
398
+ yield* fs.writeFileString(authFile, JSON.stringify(authData, null, 2), { mode: 384 }).pipe(
399
+ import_effect3.Effect.mapError(
400
+ (error) => new AuthStorageWriteError({
401
+ message: "Failed to write auth file",
402
+ cause: String(error)
403
+ })
404
+ )
405
+ );
406
+ yield* import_effect3.Effect.log("Auth data saved to local storage");
300
407
  });
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
- })
408
+ const remove = import_effect3.Effect.gen(function* () {
409
+ yield* fs.remove(authFile).pipe(
410
+ import_effect3.Effect.mapError((error) => {
411
+ const cause = error instanceof Error ? error : error.error;
412
+ const code = cause && typeof cause === "object" && "code" in cause ? cause.code : "";
413
+ return { isNotFound: code === "ENOENT" || String(error).includes("ENOENT"), original: error };
414
+ }),
415
+ import_effect3.Effect.catchAll(
416
+ (mapped) => mapped.isNotFound ? import_effect3.Effect.void : import_effect3.Effect.fail(
417
+ new AuthStorageWriteError({
418
+ message: "Failed to remove auth file",
419
+ cause: String(mapped.original)
420
+ })
421
+ )
422
+ )
423
+ );
424
+ yield* import_effect3.Effect.log("Auth data removed from local storage");
317
425
  });
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");
426
+ return { load, save, remove };
427
+ })
428
+ }) {
429
+ };
337
430
 
338
- // src/services/project-config.ts
339
- var import_network_config = require("@gigadrive/network-config");
431
+ // src/services/oauth-config.ts
340
432
  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", {
433
+ var OAuthEnvConfig = import_effect4.Config.all({
434
+ issuerUrl: import_effect4.Config.string("GIGADRIVE_NETWORK_OAUTH_ISSUER_URL").pipe(import_effect4.Config.withDefault("https://idp.gigadrive.de")),
435
+ // Default is the first-party CLI's public OAuth client (seeded in the Network
436
+ // repo). Override with GIGADRIVE_NETWORK_OAUTH_CLIENT_ID per environment.
437
+ clientId: import_effect4.Config.string("GIGADRIVE_NETWORK_OAUTH_CLIENT_ID").pipe(
438
+ import_effect4.Config.withDefault("0195c11c-0000-7000-8000-000000000001")
439
+ )
440
+ });
441
+ var OpenIdDiscoveryDocument = import_effect4.Schema.Struct({
442
+ issuer: import_effect4.Schema.optional(import_effect4.Schema.String),
443
+ authorization_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String),
444
+ token_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String),
445
+ userinfo_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String)
446
+ });
447
+ var CLI_SCOPES = [
448
+ // Identity / OIDC
449
+ "offline_access",
450
+ "openid",
451
+ "profile",
452
+ "email",
453
+ // Network API capabilities
454
+ "network:applications:read",
455
+ "network:env_vars:read",
456
+ "network:env_vars:write",
457
+ "network:env_vars:delete",
458
+ "network:deployments:read",
459
+ "network:deployments:write",
460
+ "network:deployments:trigger",
461
+ "network:ai_gateway:chat",
462
+ "network:ai_gateway:models",
463
+ "network:ai_gateway:usage:read",
464
+ "network:ai_gateway:budgets:read",
465
+ "network:ai_gateway:budgets:write",
466
+ "network:ai_gateway:policies:read",
467
+ "network:ai_gateway:policies:write",
468
+ // Provisioning local-dev API credentials
469
+ "platform:api_keys:read",
470
+ "platform:api_keys:write",
471
+ "platform:api_keys:delete"
472
+ ];
473
+ var DEFAULT_SCOPE = CLI_SCOPES.join(" ");
474
+ var OAuthConfigService = class extends import_effect4.Effect.Service()("OAuthConfigService", {
352
475
  accessors: true,
353
- dependencies: [import_network_config.NetworkConfigLive],
354
476
  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
477
  const envConfig = yield* OAuthEnvConfig;
595
478
  let cached = null;
596
- const getConfig = import_effect9.Effect.gen(function* () {
479
+ const getConfig = import_effect4.Effect.gen(function* () {
597
480
  if (cached) return cached;
598
481
  const issuerBase = envConfig.issuerUrl.replace(/\/$/, "");
599
482
  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({
483
+ yield* import_effect4.Effect.log("Fetching OIDC discovery document", { discoveryUrl });
484
+ const response = yield* import_effect4.Effect.tryPromise({
602
485
  try: (signal) => fetch(discoveryUrl, { signal }),
603
486
  catch: (error) => new OAuthDiscoveryError({
604
487
  message: "Failed to fetch OIDC discovery document",
@@ -606,27 +489,27 @@ var OAuthConfigService = class extends import_effect9.Effect.Service()("OAuthCon
606
489
  })
607
490
  });
608
491
  if (!response.ok) {
609
- return yield* import_effect9.Effect.fail(
492
+ return yield* import_effect4.Effect.fail(
610
493
  new OAuthDiscoveryError({
611
494
  message: `OIDC discovery returned ${response.status} ${response.statusText}`
612
495
  })
613
496
  );
614
497
  }
615
- const json = yield* import_effect9.Effect.tryPromise({
498
+ const json = yield* import_effect4.Effect.tryPromise({
616
499
  try: () => response.json(),
617
500
  catch: () => new OAuthDiscoveryError({
618
501
  message: "Failed to parse OIDC discovery JSON"
619
502
  })
620
503
  });
621
- const decoded = yield* import_effect9.Schema.decodeUnknown(OpenIdDiscoveryDocument)(json).pipe(
622
- import_effect9.Effect.mapError(
504
+ const decoded = yield* import_effect4.Schema.decodeUnknown(OpenIdDiscoveryDocument)(json).pipe(
505
+ import_effect4.Effect.mapError(
623
506
  () => new OAuthDiscoveryError({
624
507
  message: "Invalid OIDC discovery document schema"
625
508
  })
626
509
  )
627
510
  );
628
511
  if (!decoded.authorization_endpoint || !decoded.token_endpoint || !decoded.userinfo_endpoint) {
629
- return yield* import_effect9.Effect.fail(
512
+ return yield* import_effect4.Effect.fail(
630
513
  new OAuthDiscoveryError({
631
514
  message: "OIDC discovery returned incomplete endpoints"
632
515
  })
@@ -641,7 +524,7 @@ var OAuthConfigService = class extends import_effect9.Effect.Service()("OAuthCon
641
524
  userinfoUrl: decoded.userinfo_endpoint
642
525
  };
643
526
  cached = config;
644
- yield* import_effect9.Effect.log("OIDC discovery completed", { issuer: config.issuer });
527
+ yield* import_effect4.Effect.log("OIDC discovery completed", { issuer: config.issuer });
645
528
  return config;
646
529
  });
647
530
  return { getConfig };
@@ -658,22 +541,22 @@ var generatePKCE = () => {
658
541
  const codeChallenge = base64URLEncode(challengeBytes);
659
542
  return { codeVerifier, codeChallenge };
660
543
  };
661
- var AuthService = class extends import_effect10.Effect.Service()("AuthService", {
544
+ var AuthService = class extends import_effect5.Effect.Service()("AuthService", {
662
545
  accessors: true,
663
546
  dependencies: [OAuthConfigService.Default, AuthStorageService.Default],
664
- effect: import_effect10.Effect.gen(function* () {
547
+ effect: import_effect5.Effect.gen(function* () {
665
548
  const oauthConfig = yield* OAuthConfigService;
666
549
  const storage = yield* AuthStorageService;
667
550
  let accessToken = null;
668
551
  let tokenExpirationTime = 0;
669
- const logout = import_effect10.Effect.gen(function* () {
552
+ const logout = import_effect5.Effect.gen(function* () {
670
553
  accessToken = null;
671
554
  tokenExpirationTime = 0;
672
555
  yield* storage.remove;
673
- yield* import_effect10.Effect.log("Logged out");
556
+ yield* import_effect5.Effect.log("Logged out");
674
557
  });
675
- const exchangeCodeForTokens = (config, authCode, redirectUri, codeVerifier) => import_effect10.Effect.gen(function* () {
676
- const response = yield* import_effect10.Effect.tryPromise({
558
+ const exchangeCodeForTokens = (config, authCode, redirectUri, codeVerifier) => import_effect5.Effect.gen(function* () {
559
+ const response = yield* import_effect5.Effect.tryPromise({
677
560
  try: () => fetch(config.tokenUrl, {
678
561
  method: "POST",
679
562
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
@@ -690,31 +573,31 @@ var AuthService = class extends import_effect10.Effect.Service()("AuthService",
690
573
  })
691
574
  });
692
575
  if (!response.ok) {
693
- const body = yield* import_effect10.Effect.tryPromise({
576
+ const body = yield* import_effect5.Effect.tryPromise({
694
577
  try: () => response.text(),
695
578
  catch: () => new TokenExchangeError({ message: "Failed to read token exchange error response" })
696
579
  });
697
- return yield* import_effect10.Effect.fail(
580
+ return yield* import_effect5.Effect.fail(
698
581
  new TokenExchangeError({
699
582
  message: `Token exchange failed: ${response.status} ${response.statusText} - ${body}`,
700
583
  statusCode: response.status
701
584
  })
702
585
  );
703
586
  }
704
- const tokens = yield* import_effect10.Effect.tryPromise({
587
+ const tokens = yield* import_effect5.Effect.tryPromise({
705
588
  try: () => response.json(),
706
589
  catch: () => new TokenExchangeError({ message: "Failed to parse token exchange response" })
707
590
  });
708
591
  return tokens;
709
592
  });
710
- const startCallbackServer = (config, codeChallenge, state) => import_effect10.Effect.async((resume) => {
593
+ const startCallbackServer = (config, codeChallenge, state) => import_effect5.Effect.async((resume) => {
711
594
  const server = http.createServer();
712
595
  const redirectPath = "/callback";
713
596
  server.listen(0, "127.0.0.1", () => {
714
597
  const address = server.address();
715
598
  if (!address || typeof address !== "object") {
716
599
  server.close(() => {
717
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: "Failed to determine callback server address" })));
600
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: "Failed to determine callback server address" })));
718
601
  });
719
602
  return;
720
603
  }
@@ -728,10 +611,10 @@ var AuthService = class extends import_effect10.Effect.Service()("AuthService",
728
611
  authUrl.searchParams.append("code_challenge", codeChallenge);
729
612
  authUrl.searchParams.append("code_challenge_method", "S256");
730
613
  authUrl.searchParams.append("state", state);
731
- import_effect10.Console.log(`Please open this URL in your browser to log in:
614
+ import_effect5.Console.log(`Please open this URL in your browser to log in:
732
615
  ${authUrl.toString()}
733
- `).pipe(import_effect10.Effect.runSync);
734
- import_effect10.Console.log("Waiting for login callback...").pipe(import_effect10.Effect.runSync);
616
+ `).pipe(import_effect5.Effect.runSync);
617
+ import_effect5.Console.log("Waiting for login callback...").pipe(import_effect5.Effect.runSync);
735
618
  void (0, import_open.default)(authUrl.toString());
736
619
  server.on("request", (req, res) => {
737
620
  const reqUrl = new URL(req.url || "", redirectUri);
@@ -747,7 +630,7 @@ ${authUrl.toString()}
747
630
  const safeOauthError = escapeHtml(oauthError);
748
631
  res.end(`<h1>Login Failed!</h1><p>Error: ${safeOauthError}</p><p>You can close this tab.</p>`);
749
632
  server.close();
750
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: `OAuth error: ${oauthError}` })));
633
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: `OAuth error: ${oauthError}` })));
751
634
  return;
752
635
  }
753
636
  if (receivedState !== state) {
@@ -755,26 +638,26 @@ ${authUrl.toString()}
755
638
  "<h1>Login Failed!</h1><p>State mismatch. Possible CSRF attack.</p><p>You can close this tab.</p>"
756
639
  );
757
640
  server.close();
758
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: "State mismatch" })));
641
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: "State mismatch" })));
759
642
  return;
760
643
  }
761
644
  if (authCode) {
762
645
  res.end("<h1>Login Successful!</h1><p>You can close this tab.</p>");
763
646
  server.close();
764
- resume(import_effect10.Effect.succeed({ authCode, redirectUri }));
647
+ resume(import_effect5.Effect.succeed({ authCode, redirectUri }));
765
648
  } else {
766
649
  res.end("<h1>Login Failed!</h1><p>No authorization code received.</p><p>You can close this tab.</p>");
767
650
  server.close();
768
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: "No authorization code received" })));
651
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: "No authorization code received" })));
769
652
  }
770
653
  });
771
654
  server.on("error", (err) => {
772
655
  server.close();
773
- resume(import_effect10.Effect.fail(new LoginFlowError({ message: `Local server error: ${err.message}` })));
656
+ resume(import_effect5.Effect.fail(new LoginFlowError({ message: `Local server error: ${err.message}` })));
774
657
  });
775
658
  });
776
659
  });
777
- const login = import_effect10.Effect.gen(function* () {
660
+ const login = import_effect5.Effect.gen(function* () {
778
661
  yield* logout;
779
662
  const { codeVerifier, codeChallenge } = generatePKCE();
780
663
  const state = crypto.randomBytes(16).toString("hex");
@@ -782,7 +665,7 @@ ${authUrl.toString()}
782
665
  const { authCode, redirectUri } = yield* startCallbackServer(config, codeChallenge, state);
783
666
  const tokens = yield* exchangeCodeForTokens(config, authCode, redirectUri, codeVerifier);
784
667
  if (!tokens.refresh_token) {
785
- return yield* import_effect10.Effect.fail(
668
+ return yield* import_effect5.Effect.fail(
786
669
  new TokenExchangeError({ message: "No refresh token received from identity provider" })
787
670
  );
788
671
  }
@@ -793,21 +676,21 @@ ${authUrl.toString()}
793
676
  accessToken,
794
677
  tokenExpirationTime
795
678
  });
796
- yield* import_effect10.Effect.log("Login successful");
679
+ yield* import_effect5.Effect.log("Login successful");
797
680
  return true;
798
681
  });
799
- const refreshAccessToken = import_effect10.Effect.gen(function* () {
682
+ const refreshAccessToken = import_effect5.Effect.gen(function* () {
800
683
  const stored = yield* storage.load.pipe(
801
- import_effect10.Effect.catchTag("AuthStorageReadError", (err) => import_effect10.Effect.fail(new TokenRefreshError({ message: err.message })))
684
+ import_effect5.Effect.catchTag("AuthStorageReadError", (err) => import_effect5.Effect.fail(new TokenRefreshError({ message: err.message })))
802
685
  );
803
686
  const config = yield* oauthConfig.getConfig.pipe(
804
- import_effect10.Effect.catchTag("OAuthDiscoveryError", (err) => import_effect10.Effect.fail(new TokenRefreshError({ message: err.message })))
687
+ import_effect5.Effect.catchTag("OAuthDiscoveryError", (err) => import_effect5.Effect.fail(new TokenRefreshError({ message: err.message })))
805
688
  );
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." }))
689
+ const authData = yield* import_effect5.Option.match(stored, {
690
+ onNone: () => import_effect5.Effect.fail(new TokenRefreshError({ message: "No refresh token available. Please log in." })),
691
+ onSome: (data) => data.refreshToken ? import_effect5.Effect.succeed(data) : import_effect5.Effect.fail(new TokenRefreshError({ message: "No refresh token available. Please log in." }))
809
692
  });
810
- const response = yield* import_effect10.Effect.tryPromise({
693
+ const response = yield* import_effect5.Effect.tryPromise({
811
694
  try: () => fetch(config.tokenUrl, {
812
695
  method: "POST",
813
696
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
@@ -823,26 +706,26 @@ ${authUrl.toString()}
823
706
  });
824
707
  if (!response.ok) {
825
708
  if (response.status === 400) {
826
- yield* logout.pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.void));
827
- return yield* import_effect10.Effect.fail(
709
+ yield* logout.pipe(import_effect5.Effect.catchAll(() => import_effect5.Effect.void));
710
+ return yield* import_effect5.Effect.fail(
828
711
  new TokenRefreshError({
829
712
  message: "Refresh token is invalid or expired. Please log in again.",
830
713
  statusCode: response.status
831
714
  })
832
715
  );
833
716
  }
834
- const body = yield* import_effect10.Effect.tryPromise({
717
+ const body = yield* import_effect5.Effect.tryPromise({
835
718
  try: () => response.text(),
836
719
  catch: () => new TokenRefreshError({ message: "Failed to read refresh error response" })
837
720
  });
838
- return yield* import_effect10.Effect.fail(
721
+ return yield* import_effect5.Effect.fail(
839
722
  new TokenRefreshError({
840
723
  message: `Token refresh failed: ${response.status} ${response.statusText} - ${body}`,
841
724
  statusCode: response.status
842
725
  })
843
726
  );
844
727
  }
845
- const tokens = yield* import_effect10.Effect.tryPromise({
728
+ const tokens = yield* import_effect5.Effect.tryPromise({
846
729
  try: () => response.json(),
847
730
  catch: () => new TokenRefreshError({ message: "Failed to parse token refresh response" })
848
731
  });
@@ -853,148 +736,1485 @@ ${authUrl.toString()}
853
736
  refreshToken: newRefreshToken,
854
737
  accessToken,
855
738
  tokenExpirationTime
856
- }).pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.void));
857
- yield* import_effect10.Effect.log("Access token refreshed successfully");
739
+ }).pipe(import_effect5.Effect.catchAll(() => import_effect5.Effect.void));
740
+ yield* import_effect5.Effect.log("Access token refreshed successfully");
858
741
  return true;
859
742
  });
860
- const getAccessToken = import_effect10.Effect.gen(function* () {
861
- if (accessToken && Date.now() < tokenExpirationTime) {
862
- return accessToken;
863
- }
864
- const stored = yield* storage.load.pipe(
865
- import_effect10.Effect.catchAll(() => import_effect10.Effect.succeed(import_effect10.Option.none()))
743
+ const getAccessToken = import_effect5.Effect.gen(function* () {
744
+ if (accessToken && Date.now() < tokenExpirationTime) {
745
+ return accessToken;
746
+ }
747
+ const stored = yield* storage.load.pipe(
748
+ import_effect5.Effect.catchAll(() => import_effect5.Effect.succeed(import_effect5.Option.none()))
749
+ );
750
+ import_effect5.Option.match(stored, {
751
+ onNone: () => {
752
+ },
753
+ onSome: (data) => {
754
+ if (data.accessToken && data.tokenExpirationTime && Date.now() < data.tokenExpirationTime) {
755
+ accessToken = data.accessToken;
756
+ tokenExpirationTime = data.tokenExpirationTime;
757
+ }
758
+ }
759
+ });
760
+ if (accessToken && Date.now() < tokenExpirationTime) {
761
+ return accessToken;
762
+ }
763
+ yield* import_effect5.Effect.log("Access token expired or not available. Attempting to refresh...");
764
+ yield* refreshAccessToken.pipe(
765
+ import_effect5.Effect.catchTag("TokenRefreshError", (err) => import_effect5.Effect.fail(new NotAuthenticatedError({ message: err.message })))
766
+ );
767
+ if (!accessToken) {
768
+ return yield* import_effect5.Effect.fail(
769
+ new NotAuthenticatedError({ message: "No valid access token available. Please log in first." })
770
+ );
771
+ }
772
+ return accessToken;
773
+ });
774
+ const getUserInfo = import_effect5.Effect.gen(function* () {
775
+ const config = yield* oauthConfig.getConfig;
776
+ const token = yield* getAccessToken;
777
+ const fetchUserInfo = (t) => import_effect5.Effect.tryPromise({
778
+ try: () => fetch(config.userinfoUrl, {
779
+ headers: {
780
+ Authorization: `Bearer ${t}`,
781
+ Accept: "application/json"
782
+ }
783
+ }),
784
+ catch: (error) => new UserInfoFetchError({
785
+ message: `Failed to fetch user info: ${error instanceof Error ? error.message : String(error)}`
786
+ })
787
+ });
788
+ let response = yield* fetchUserInfo(token);
789
+ if (response.status === 401 || response.status === 403) {
790
+ yield* refreshAccessToken.pipe(
791
+ import_effect5.Effect.catchTag("TokenRefreshError", (err) => import_effect5.Effect.fail(new UserInfoFetchError({ message: err.message })))
792
+ );
793
+ if (!accessToken) {
794
+ return yield* import_effect5.Effect.fail(new UserInfoFetchError({ message: "Token refresh failed" }));
795
+ }
796
+ response = yield* fetchUserInfo(accessToken);
797
+ }
798
+ if (!response.ok) {
799
+ const body = yield* import_effect5.Effect.tryPromise({
800
+ try: () => response.text(),
801
+ catch: () => new UserInfoFetchError({ message: "Failed to read user info error response" })
802
+ });
803
+ return yield* import_effect5.Effect.fail(
804
+ new UserInfoFetchError({
805
+ message: `Failed to fetch user info: ${response.status} ${response.statusText}${body ? ` - ${body}` : ""}`,
806
+ statusCode: response.status
807
+ })
808
+ );
809
+ }
810
+ const data = yield* import_effect5.Effect.tryPromise({
811
+ try: () => response.json(),
812
+ catch: () => new UserInfoFetchError({ message: "Failed to parse user info response" })
813
+ });
814
+ return data;
815
+ });
816
+ const inferUserName = (info) => {
817
+ const isNonEmptyString = (value) => typeof value === "string" && value.trim() !== "";
818
+ const name = info.name;
819
+ const givenName = info.given_name;
820
+ const familyName = info.family_name;
821
+ const preferredUsername = info.preferred_username;
822
+ const email = info.email;
823
+ return isNonEmptyString(name) && name || (isNonEmptyString(givenName) && isNonEmptyString(familyName) ? `${givenName} ${familyName}` : void 0) || isNonEmptyString(preferredUsername) && preferredUsername || isNonEmptyString(email) && email || "your account";
824
+ };
825
+ return { login, logout, getAccessToken, getUserInfo, refreshAccessToken, inferUserName };
826
+ })
827
+ }) {
828
+ };
829
+
830
+ // src/services/api-client.ts
831
+ var ApiBaseUrl = import_effect6.Config.string("GIGADRIVE_API_BASE_URL").pipe(import_effect6.Config.withDefault("https://api.gigadrive.network"));
832
+ var toApiRequestError = (error) => {
833
+ if (error instanceof import_sdk.ApiError) {
834
+ return new ApiRequestError({ message: error.message, statusCode: error.status, code: error.code });
835
+ }
836
+ return new ApiRequestError({ message: error instanceof Error ? error.message : String(error) });
837
+ };
838
+ var ApiClientService = class extends import_effect6.Effect.Service()("ApiClientService", {
839
+ accessors: true,
840
+ effect: import_effect6.Effect.gen(function* () {
841
+ const baseUrl = yield* ApiBaseUrl;
842
+ const authService = yield* AuthService;
843
+ const getClient = import_effect6.Effect.gen(function* () {
844
+ const token = yield* authService.getAccessToken;
845
+ return new import_sdk.GigadriveClient({ bearerToken: token, baseUrl });
846
+ });
847
+ const request = (f) => import_effect6.Effect.gen(function* () {
848
+ const client = yield* getClient;
849
+ return yield* import_effect6.Effect.tryPromise({
850
+ try: () => f(client),
851
+ catch: toApiRequestError
852
+ });
853
+ });
854
+ return { getClient, request };
855
+ })
856
+ }) {
857
+ };
858
+
859
+ // src/services/project-link.ts
860
+ var import_platform2 = require("@effect/platform");
861
+ var import_effect7 = require("effect");
862
+ var LINK_DIR = ".gigadrive";
863
+ var LINK_FILE = "project.json";
864
+ var ProjectLinkService = class extends import_effect7.Effect.Service()("ProjectLinkService", {
865
+ accessors: true,
866
+ effect: import_effect7.Effect.gen(function* () {
867
+ const fs = yield* import_platform2.FileSystem.FileSystem;
868
+ const pathService = yield* import_platform2.Path.Path;
869
+ const linkDir = (projectFolder) => pathService.join(projectFolder, LINK_DIR);
870
+ const linkFile = (projectFolder) => pathService.join(projectFolder, LINK_DIR, LINK_FILE);
871
+ const load = import_effect7.Effect.fn("ProjectLinkService.load")(function* (projectFolder) {
872
+ const file = linkFile(projectFolder);
873
+ const exists = yield* fs.exists(file).pipe(import_effect7.Effect.mapError(() => new ProjectLinkReadError({ message: "Failed to check project link existence" })));
874
+ if (!exists) return import_effect7.Option.none();
875
+ const data = yield* fs.readFileString(file, "utf8").pipe(import_effect7.Effect.mapError(() => new ProjectLinkReadError({ message: "Failed to read project link file" })));
876
+ const json = yield* import_effect7.Effect.try({
877
+ try: () => JSON.parse(data),
878
+ catch: () => new ProjectLinkReadError({ message: `${LINK_DIR}/${LINK_FILE} contains invalid JSON` })
879
+ });
880
+ const link = yield* import_effect7.Schema.decodeUnknown(ProjectLink)(json).pipe(
881
+ import_effect7.Effect.mapError(() => new ProjectLinkReadError({ message: `${LINK_DIR}/${LINK_FILE} has an invalid schema` }))
882
+ );
883
+ return import_effect7.Option.some(link);
884
+ });
885
+ const resolve = import_effect7.Effect.fn("ProjectLinkService.resolve")(function* (projectFolder) {
886
+ const link = yield* load(projectFolder);
887
+ return yield* import_effect7.Option.match(link, {
888
+ onNone: () => import_effect7.Effect.fail(
889
+ new ProjectNotLinkedError({
890
+ message: 'This directory is not linked to an application. Run "gigadrive link" first.',
891
+ directory: projectFolder
892
+ })
893
+ ),
894
+ onSome: (value) => import_effect7.Effect.succeed(value)
895
+ });
896
+ });
897
+ const save = import_effect7.Effect.fn("ProjectLinkService.save")(function* (projectFolder, link) {
898
+ yield* fs.makeDirectory(linkDir(projectFolder), { recursive: true }).pipe(
899
+ import_effect7.Effect.mapError(
900
+ (error) => new ProjectLinkWriteError({ message: "Failed to create .gigadrive directory", cause: String(error) })
901
+ )
902
+ );
903
+ yield* fs.writeFileString(linkFile(projectFolder), `${JSON.stringify(link, null, 2)}
904
+ `).pipe(
905
+ import_effect7.Effect.mapError(
906
+ (error) => new ProjectLinkWriteError({ message: "Failed to write project link file", cause: String(error) })
907
+ )
908
+ );
909
+ yield* import_effect7.Effect.log("Project link saved", { applicationId: link.applicationId });
910
+ });
911
+ const remove = import_effect7.Effect.fn("ProjectLinkService.remove")(function* (projectFolder) {
912
+ yield* fs.remove(linkFile(projectFolder), { force: true }).pipe(
913
+ import_effect7.Effect.mapError(
914
+ (error) => new ProjectLinkWriteError({ message: "Failed to remove project link file", cause: String(error) })
915
+ )
916
+ );
917
+ });
918
+ return { load, resolve, save, remove };
919
+ })
920
+ }) {
921
+ };
922
+
923
+ // src/commands/ai/index.ts
924
+ var orgOption = import_cli.Options.text("org").pipe(
925
+ import_cli.Options.withAlias("o"),
926
+ import_cli.Options.withDescription("Organization ID (defaults to the linked organization)"),
927
+ import_cli.Options.optional
928
+ );
929
+ var appOption = import_cli.Options.text("app").pipe(
930
+ import_cli.Options.withAlias("a"),
931
+ import_cli.Options.withDescription("Restrict to this application ID"),
932
+ import_cli.Options.optional
933
+ );
934
+ var fromOption = import_cli.Options.text("from").pipe(
935
+ import_cli.Options.withDescription("Include usage at or after this ISO 8601 timestamp"),
936
+ import_cli.Options.optional
937
+ );
938
+ var toOption = import_cli.Options.text("to").pipe(
939
+ import_cli.Options.withDescription("Include usage before or at this ISO 8601 timestamp"),
940
+ import_cli.Options.optional
941
+ );
942
+ var fileOption = import_cli.Options.text("file").pipe(
943
+ import_cli.Options.withAlias("f"),
944
+ import_cli.Options.withDescription('Path to a JSON file, or "-" for stdin (default: stdin)'),
945
+ import_cli.Options.optional
946
+ );
947
+ var resolveOrgId = (org) => import_effect8.Effect.gen(function* () {
948
+ if (import_effect8.Option.isSome(org)) return org.value;
949
+ const projectLink = yield* ProjectLinkService;
950
+ const link = yield* projectLink.resolve(process.cwd());
951
+ if (link.organizationId === void 0) {
952
+ return yield* import_effect8.Effect.fail(
953
+ new ProjectNotLinkedError({
954
+ message: 'No organization is linked. Pass --org <id> or re-run "gigadrive link".',
955
+ directory: process.cwd()
956
+ })
957
+ );
958
+ }
959
+ return link.organizationId;
960
+ });
961
+ var readStdin = import_effect8.Effect.async((resume) => {
962
+ let data = "";
963
+ const stdin = process.stdin;
964
+ stdin.setEncoding("utf8");
965
+ const onData = (chunk) => {
966
+ data += chunk;
967
+ };
968
+ const cleanup = () => {
969
+ stdin.off("data", onData);
970
+ stdin.off("end", onEnd);
971
+ stdin.off("error", onError);
972
+ };
973
+ const onEnd = () => {
974
+ cleanup();
975
+ resume(import_effect8.Effect.succeed(data));
976
+ };
977
+ const onError = (err) => {
978
+ cleanup();
979
+ resume(import_effect8.Effect.fail(new ApiRequestError({ message: `Failed to read stdin: ${err.message}` })));
980
+ };
981
+ stdin.on("data", onData);
982
+ stdin.on("end", onEnd);
983
+ stdin.on("error", onError);
984
+ stdin.resume();
985
+ });
986
+ var readJsonInput = (file) => import_effect8.Effect.gen(function* () {
987
+ const path = import_effect8.Option.getOrUndefined(file);
988
+ let raw;
989
+ if (path === void 0 || path === "-") {
990
+ raw = yield* readStdin;
991
+ } else {
992
+ const fs = yield* import_platform3.FileSystem.FileSystem;
993
+ raw = yield* fs.readFileString(path, "utf8").pipe(import_effect8.Effect.mapError((e) => new ApiRequestError({ message: `Failed to read ${path}: ${String(e)}` })));
994
+ }
995
+ return yield* import_effect8.Effect.try({
996
+ try: () => JSON.parse(raw),
997
+ catch: () => new ApiRequestError({ message: "Input is not valid JSON." })
998
+ });
999
+ });
1000
+ var governanceErrorHandlers = {
1001
+ NotAuthenticatedError: (err) => import_effect8.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1002
+ import_effect8.Effect.andThen(import_effect8.Effect.fail(err))
1003
+ ),
1004
+ ApiRequestError: (err) => import_effect8.Console.error(err.message).pipe(import_effect8.Effect.andThen(import_effect8.Effect.fail(err))),
1005
+ ProjectNotLinkedError: (err) => import_effect8.Console.error(err.message).pipe(import_effect8.Effect.andThen(import_effect8.Effect.fail(err))),
1006
+ ProjectLinkReadError: (err) => import_effect8.Console.error(err.message).pipe(import_effect8.Effect.andThen(import_effect8.Effect.fail(err)))
1007
+ };
1008
+ var usageSummaryCommand = import_cli.Command.make(
1009
+ "summary",
1010
+ { org: orgOption, app: appOption, from: fromOption, to: toOption },
1011
+ ({ org, app, from, to }) => import_effect8.Effect.gen(function* () {
1012
+ const apiClient = yield* ApiClientService;
1013
+ const orgId = yield* resolveOrgId(org);
1014
+ const summary = yield* apiClient.request(
1015
+ (client) => client.organizations.aiGateway.usage.summary(orgId, {
1016
+ applicationId: import_effect8.Option.getOrUndefined(app),
1017
+ from: import_effect8.Option.getOrUndefined(from),
1018
+ to: import_effect8.Option.getOrUndefined(to)
1019
+ })
1020
+ );
1021
+ yield* import_effect8.Console.log(JSON.stringify(summary, null, 2));
1022
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1023
+ );
1024
+ var usageRequestsCommand = import_cli.Command.make(
1025
+ "requests",
1026
+ { org: orgOption, app: appOption, from: fromOption, to: toOption },
1027
+ ({ org, app, from, to }) => import_effect8.Effect.gen(function* () {
1028
+ const apiClient = yield* ApiClientService;
1029
+ const orgId = yield* resolveOrgId(org);
1030
+ const { items, total } = yield* apiClient.request(
1031
+ (client) => client.organizations.aiGateway.usage.requests(orgId, {
1032
+ applicationId: import_effect8.Option.getOrUndefined(app),
1033
+ from: import_effect8.Option.getOrUndefined(from),
1034
+ to: import_effect8.Option.getOrUndefined(to)
1035
+ })
1036
+ );
1037
+ if (items.length === 0) {
1038
+ yield* import_effect8.Console.log("No AI Gateway requests found.");
1039
+ return;
1040
+ }
1041
+ for (const e of items) {
1042
+ yield* import_effect8.Console.log(
1043
+ `${e.createdAt} ${e.modelRequested} ${e.status} ${e.totalTokens} tok ${e.billableCostMicros}\xB5$`
1044
+ );
1045
+ }
1046
+ yield* import_effect8.Console.log(`
1047
+ ${total} request(s).`);
1048
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1049
+ );
1050
+ var outOption = import_cli.Options.text("out").pipe(
1051
+ import_cli.Options.withDescription("Write the CSV to this file instead of stdout"),
1052
+ import_cli.Options.optional
1053
+ );
1054
+ var usageExportCommand = import_cli.Command.make(
1055
+ "export",
1056
+ { org: orgOption, app: appOption, from: fromOption, to: toOption, out: outOption },
1057
+ ({ org, app, from, to, out }) => import_effect8.Effect.gen(function* () {
1058
+ const apiClient = yield* ApiClientService;
1059
+ const orgId = yield* resolveOrgId(org);
1060
+ const csv = yield* apiClient.request(
1061
+ (client) => client.organizations.aiGateway.usage.export(orgId, {
1062
+ applicationId: import_effect8.Option.getOrUndefined(app),
1063
+ from: import_effect8.Option.getOrUndefined(from),
1064
+ to: import_effect8.Option.getOrUndefined(to)
1065
+ })
1066
+ );
1067
+ const outPath = import_effect8.Option.getOrUndefined(out);
1068
+ if (outPath === void 0) {
1069
+ yield* import_effect8.Console.log(csv);
1070
+ } else {
1071
+ const fs = yield* import_platform3.FileSystem.FileSystem;
1072
+ yield* fs.writeFileString(outPath, csv).pipe(import_effect8.Effect.mapError((e) => new ApiRequestError({ message: `Failed to write ${outPath}: ${String(e)}` })));
1073
+ yield* import_effect8.Console.log(`Wrote ${outPath}.`);
1074
+ }
1075
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1076
+ );
1077
+ var usageCommand = import_cli.Command.make("usage", {}, () => import_effect8.Effect.void).pipe(
1078
+ import_cli.Command.withSubcommands([usageSummaryCommand, usageRequestsCommand, usageExportCommand])
1079
+ );
1080
+ var budgetsGetCommand = import_cli.Command.make(
1081
+ "get",
1082
+ { org: orgOption },
1083
+ ({ org }) => import_effect8.Effect.gen(function* () {
1084
+ const apiClient = yield* ApiClientService;
1085
+ const orgId = yield* resolveOrgId(org);
1086
+ const { items } = yield* apiClient.request((client) => client.organizations.aiGateway.budgets.list(orgId));
1087
+ yield* import_effect8.Console.log(JSON.stringify(items, null, 2));
1088
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1089
+ );
1090
+ var budgetsSetCommand = import_cli.Command.make(
1091
+ "set",
1092
+ { org: orgOption, file: fileOption },
1093
+ ({ org, file }) => import_effect8.Effect.gen(function* () {
1094
+ const apiClient = yield* ApiClientService;
1095
+ const orgId = yield* resolveOrgId(org);
1096
+ const parsed = yield* readJsonInput(file);
1097
+ const budgets = Array.isArray(parsed) ? parsed : parsed.budgets;
1098
+ if (!Array.isArray(budgets)) {
1099
+ return yield* import_effect8.Effect.fail(
1100
+ new ApiRequestError({
1101
+ message: 'Expected a JSON array of budgets, or an object of the form { "budgets": [...] }.'
1102
+ })
1103
+ );
1104
+ }
1105
+ const { items } = yield* apiClient.request(
1106
+ (client) => client.organizations.aiGateway.budgets.replace(orgId, budgets)
1107
+ );
1108
+ yield* import_effect8.Console.log(`Saved ${items.length} budget(s).`);
1109
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1110
+ );
1111
+ var budgetsCommand = import_cli.Command.make("budgets", {}, () => import_effect8.Effect.void).pipe(
1112
+ import_cli.Command.withSubcommands([budgetsGetCommand, budgetsSetCommand])
1113
+ );
1114
+ var policiesGetCommand = import_cli.Command.make(
1115
+ "get",
1116
+ { org: orgOption, app: appOption },
1117
+ ({ org, app }) => import_effect8.Effect.gen(function* () {
1118
+ const apiClient = yield* ApiClientService;
1119
+ const orgId = yield* resolveOrgId(org);
1120
+ const policy = yield* apiClient.request(
1121
+ (client) => client.organizations.aiGateway.policies.get(orgId, { applicationId: import_effect8.Option.getOrUndefined(app) })
1122
+ );
1123
+ yield* import_effect8.Console.log(policy === null ? "No policy set." : JSON.stringify(policy, null, 2));
1124
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1125
+ );
1126
+ var policiesSetCommand = import_cli.Command.make(
1127
+ "set",
1128
+ { org: orgOption, file: fileOption },
1129
+ ({ org, file }) => import_effect8.Effect.gen(function* () {
1130
+ const apiClient = yield* ApiClientService;
1131
+ const orgId = yield* resolveOrgId(org);
1132
+ const parsed = yield* readJsonInput(file);
1133
+ const policy = yield* apiClient.request(
1134
+ (client) => client.organizations.aiGateway.policies.put(orgId, parsed)
1135
+ );
1136
+ yield* import_effect8.Console.log(`Policy saved${policy.applicationId ? ` for application ${policy.applicationId}` : ""}.`);
1137
+ }).pipe(import_effect8.Effect.catchTags(governanceErrorHandlers))
1138
+ );
1139
+ var policiesCommand = import_cli.Command.make("policies", {}, () => import_effect8.Effect.void).pipe(
1140
+ import_cli.Command.withSubcommands([policiesGetCommand, policiesSetCommand])
1141
+ );
1142
+ var inferenceErrorHandlers = {
1143
+ NotAuthenticatedError: (err) => import_effect8.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1144
+ import_effect8.Effect.andThen(import_effect8.Effect.fail(err))
1145
+ ),
1146
+ ApiRequestError: (err) => import_effect8.Console.error(err.message).pipe(import_effect8.Effect.andThen(import_effect8.Effect.fail(err)))
1147
+ };
1148
+ var modelsListCommand = import_cli.Command.make(
1149
+ "list",
1150
+ {},
1151
+ () => import_effect8.Effect.gen(function* () {
1152
+ const apiClient = yield* ApiClientService;
1153
+ const { items } = yield* apiClient.request((client) => client.aiGateway.listModels());
1154
+ for (const m of items) {
1155
+ yield* import_effect8.Console.log(`${m.id} ${m.name} (ctx ${m.contextWindow})`);
1156
+ }
1157
+ }).pipe(import_effect8.Effect.catchTags(inferenceErrorHandlers))
1158
+ );
1159
+ var modelIdArg = import_cli.Args.text({ name: "model-id" }).pipe(import_cli.Args.withDescription("The model ID, e.g. openai/gpt-4o"));
1160
+ var modelsGetCommand = import_cli.Command.make(
1161
+ "get",
1162
+ { modelId: modelIdArg },
1163
+ ({ modelId }) => import_effect8.Effect.gen(function* () {
1164
+ const apiClient = yield* ApiClientService;
1165
+ const model = yield* apiClient.request((client) => client.aiGateway.getModel(modelId));
1166
+ yield* import_effect8.Console.log(JSON.stringify(model, null, 2));
1167
+ }).pipe(import_effect8.Effect.catchTags(inferenceErrorHandlers))
1168
+ );
1169
+ var modelsCommand = import_cli.Command.make("models", {}, () => import_effect8.Effect.void).pipe(
1170
+ import_cli.Command.withSubcommands([modelsListCommand, modelsGetCommand])
1171
+ );
1172
+ var modelOption = import_cli.Options.text("model").pipe(
1173
+ import_cli.Options.withAlias("m"),
1174
+ import_cli.Options.withDescription("The model ID to use, e.g. openai/gpt-4o")
1175
+ );
1176
+ var streamOption = import_cli.Options.boolean("stream").pipe(import_cli.Options.withDescription("Stream the response token by token"));
1177
+ var promptArg = import_cli.Args.text({ name: "prompt" }).pipe(
1178
+ import_cli.Args.withDescription('The prompt text, or "-" to read from stdin')
1179
+ );
1180
+ var chatCommand = import_cli.Command.make(
1181
+ "chat",
1182
+ { prompt: promptArg, model: modelOption, stream: streamOption },
1183
+ ({ prompt, model, stream }) => import_effect8.Effect.gen(function* () {
1184
+ const apiClient = yield* ApiClientService;
1185
+ const text = prompt === "-" ? yield* readStdin : prompt;
1186
+ const messages = [{ role: "user", content: text }];
1187
+ if (stream) {
1188
+ const client = yield* apiClient.getClient;
1189
+ yield* import_effect8.Effect.tryPromise({
1190
+ try: async () => {
1191
+ for await (const chunk of client.aiGateway.chatCompletionsStream({ model, messages })) {
1192
+ process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
1193
+ }
1194
+ process.stdout.write("\n");
1195
+ },
1196
+ catch: (error) => new ApiRequestError({ message: error instanceof Error ? error.message : String(error) })
1197
+ });
1198
+ return;
1199
+ }
1200
+ const response = yield* apiClient.request((client) => client.aiGateway.chatCompletions({ model, messages }));
1201
+ const content = response.choices[0]?.message.content;
1202
+ yield* import_effect8.Console.log(typeof content === "string" ? content : JSON.stringify(content));
1203
+ }).pipe(import_effect8.Effect.catchTags(inferenceErrorHandlers))
1204
+ );
1205
+ var aiBase = import_cli.Command.make("ai", {}, () => import_effect8.Effect.void);
1206
+ var aiCommand = aiBase.pipe(
1207
+ import_cli.Command.withSubcommands([usageCommand, budgetsCommand, policiesCommand, modelsCommand, chatCommand])
1208
+ );
1209
+
1210
+ // src/commands/apps/index.ts
1211
+ var import_cli2 = require("@effect/cli");
1212
+ var import_effect9 = require("effect");
1213
+ var orgOption2 = import_cli2.Options.text("org").pipe(
1214
+ import_cli2.Options.withAlias("o"),
1215
+ import_cli2.Options.withDescription("Only list applications belonging to this organization ID"),
1216
+ import_cli2.Options.optional
1217
+ );
1218
+ var appsListCommand = import_cli2.Command.make(
1219
+ "list",
1220
+ { org: orgOption2 },
1221
+ ({ org }) => import_effect9.Effect.gen(function* () {
1222
+ const apiClient = yield* ApiClientService;
1223
+ const organizationId = import_effect9.Option.getOrUndefined(org);
1224
+ const { items, total } = yield* apiClient.request((client) => client.applications.list({ organizationId }));
1225
+ if (items.length === 0) {
1226
+ yield* import_effect9.Console.log("No applications found.");
1227
+ return;
1228
+ }
1229
+ for (const app of items) {
1230
+ yield* import_effect9.Console.log(`${app.id} ${app.name} (${app.organization.name})`);
1231
+ }
1232
+ yield* import_effect9.Console.log(`
1233
+ ${total} application(s).`);
1234
+ }).pipe(
1235
+ import_effect9.Effect.catchTags({
1236
+ NotAuthenticatedError: (err) => import_effect9.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1237
+ import_effect9.Effect.andThen(import_effect9.Effect.fail(err))
1238
+ ),
1239
+ ApiRequestError: (err) => import_effect9.Console.error(`Failed to list applications: ${err.message}`).pipe(import_effect9.Effect.andThen(import_effect9.Effect.fail(err)))
1240
+ })
1241
+ )
1242
+ );
1243
+ var appsBase = import_cli2.Command.make("apps", {}, () => import_effect9.Effect.void);
1244
+ var appsCommand = appsBase.pipe(import_cli2.Command.withSubcommands([appsListCommand]));
1245
+
1246
+ // src/commands/build/index.ts
1247
+ var import_cli3 = require("@effect/cli");
1248
+ var import_platform5 = require("@effect/platform");
1249
+ var import_build_utils2 = require("@gigadrive/build-utils");
1250
+ var import_effect11 = require("effect");
1251
+
1252
+ // src/services/package-manager.ts
1253
+ var import_platform4 = require("@effect/platform");
1254
+ var import_build_utils = require("@gigadrive/build-utils");
1255
+ var import_effect10 = require("effect");
1256
+ var PackageManagerService = class extends import_effect10.Effect.Service()("PackageManagerService", {
1257
+ accessors: true,
1258
+ effect: import_effect10.Effect.gen(function* () {
1259
+ const fs = yield* import_platform4.FileSystem.FileSystem;
1260
+ const pathService = yield* import_platform4.Path.Path;
1261
+ const detect = import_effect10.Effect.fn("PackageManagerService.detect")(function* (cwd) {
1262
+ yield* import_effect10.Effect.annotateCurrentSpan("cwd", cwd);
1263
+ const packageJsonPath = pathService.join(cwd, "package.json");
1264
+ const exists = yield* fs.exists(packageJsonPath).pipe(
1265
+ import_effect10.Effect.mapError(
1266
+ () => new PackageManagerNotFoundError({ message: "Failed to check for package.json", directory: cwd })
1267
+ )
1268
+ );
1269
+ if (!exists) {
1270
+ return yield* import_effect10.Effect.fail(
1271
+ new PackageManagerNotFoundError({
1272
+ message: "No package.json found",
1273
+ directory: cwd
1274
+ })
1275
+ );
1276
+ }
1277
+ const lockChecks = [
1278
+ { file: "yarn.lock", pm: "yarn" },
1279
+ { file: "pnpm-lock.yaml", pm: "pnpm" },
1280
+ { file: "bun.lockb", pm: "bun" },
1281
+ { file: "package-lock.json", pm: "npm" }
1282
+ ];
1283
+ for (const { file, pm: pm2 } of lockChecks) {
1284
+ const lockExists = yield* fs.exists(pathService.join(cwd, file)).pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.succeed(false)));
1285
+ if (lockExists) {
1286
+ yield* import_effect10.Effect.log("Package manager detected from lock file", { packageManager: pm2, lockFile: file });
1287
+ return pm2;
1288
+ }
1289
+ }
1290
+ const bunAvailable = yield* import_effect10.Effect.tryPromise({
1291
+ try: () => (0, import_build_utils.exec)({ command: "bun --version", cwd }),
1292
+ catch: () => new ExecError({ message: "bun not found", command: "bun --version" })
1293
+ }).pipe(
1294
+ import_effect10.Effect.as(true),
1295
+ import_effect10.Effect.catchTag("ExecError", () => import_effect10.Effect.succeed(false))
1296
+ );
1297
+ const pm = bunAvailable ? "bun" : "npm";
1298
+ yield* import_effect10.Effect.log("Package manager detected via fallback", { packageManager: pm });
1299
+ return pm;
1300
+ });
1301
+ const installCommand = (pm) => ({
1302
+ bun: "bun install",
1303
+ npm: "npm install",
1304
+ pnpm: "pnpm install",
1305
+ yarn: "yarn install"
1306
+ })[pm];
1307
+ const buildCommand2 = (pm) => ({
1308
+ bun: "bun run build",
1309
+ npm: "npm run build",
1310
+ pnpm: "pnpm build",
1311
+ yarn: "yarn build"
1312
+ })[pm];
1313
+ return { detect, installCommand, buildCommand: buildCommand2 };
1314
+ })
1315
+ }) {
1316
+ };
1317
+
1318
+ // src/commands/build/index.ts
1319
+ var buildCommand = import_cli3.Command.make(
1320
+ "build",
1321
+ {},
1322
+ () => import_effect11.Effect.gen(function* () {
1323
+ const pmService = yield* PackageManagerService;
1324
+ const fs = yield* import_platform5.FileSystem.FileSystem;
1325
+ const pathService = yield* import_platform5.Path.Path;
1326
+ const cwd = process.cwd();
1327
+ yield* import_effect11.Effect.log("Starting build", { cwd });
1328
+ const packageJsonPath = pathService.join(cwd, "package.json");
1329
+ const packageJsonExists = yield* fs.exists(packageJsonPath).pipe(
1330
+ import_effect11.Effect.mapError(
1331
+ () => new PackageJsonNotFoundError({ message: "Failed to check for package.json", directory: cwd })
1332
+ )
1333
+ );
1334
+ if (!packageJsonExists) {
1335
+ return yield* import_effect11.Effect.fail(new PackageJsonNotFoundError({ message: "No package.json found", directory: cwd }));
1336
+ }
1337
+ const packageJsonContent = yield* fs.readFileString(packageJsonPath, "utf-8").pipe(
1338
+ import_effect11.Effect.mapError(() => new PackageJsonNotFoundError({ message: "Failed to read package.json", directory: cwd }))
1339
+ );
1340
+ const packageJson = yield* import_effect11.Effect.try({
1341
+ try: () => JSON.parse(packageJsonContent),
1342
+ catch: (error) => new PackageJsonParseError({
1343
+ message: "Failed to parse package.json",
1344
+ directory: cwd,
1345
+ cause: error instanceof Error ? error.message : String(error)
1346
+ })
1347
+ });
1348
+ if (!packageJson.scripts?.build) {
1349
+ return yield* import_effect11.Effect.fail(new BuildScriptNotFoundError({ message: "No build script found in package.json" }));
1350
+ }
1351
+ const pm = yield* pmService.detect(cwd);
1352
+ const buildEnv = {
1353
+ ...process.env,
1354
+ NODE_ENV: "production",
1355
+ NEBULA: "1",
1356
+ VERCEL: "1",
1357
+ NOW_BUILDER: "1"
1358
+ };
1359
+ const installCmd = pmService.installCommand(pm);
1360
+ yield* import_effect11.Console.log(`Running: ${installCmd}`);
1361
+ yield* import_effect11.Effect.tryPromise({
1362
+ try: () => (0, import_build_utils2.exec)({
1363
+ command: installCmd,
1364
+ cwd,
1365
+ env: buildEnv,
1366
+ onOutput: (chunk) => {
1367
+ import_effect11.Console.log(chunk).pipe(import_effect11.Effect.runSync);
1368
+ }
1369
+ }),
1370
+ catch: (error) => new ExecError({
1371
+ message: "Install failed",
1372
+ command: installCmd,
1373
+ cause: error instanceof Error ? error.message : String(error)
1374
+ })
1375
+ });
1376
+ const buildCmd = pmService.buildCommand(pm);
1377
+ yield* import_effect11.Console.log(`Running: ${buildCmd}`);
1378
+ yield* import_effect11.Effect.tryPromise({
1379
+ try: () => (0, import_build_utils2.exec)({
1380
+ command: buildCmd,
1381
+ cwd,
1382
+ env: buildEnv,
1383
+ onOutput: (chunk) => {
1384
+ import_effect11.Console.log(chunk).pipe(import_effect11.Effect.runSync);
1385
+ }
1386
+ }),
1387
+ catch: (error) => new ExecError({
1388
+ message: "Build failed",
1389
+ command: buildCmd,
1390
+ cause: error instanceof Error ? error.message : String(error)
1391
+ })
1392
+ });
1393
+ yield* import_effect11.Console.log("Build completed successfully.");
1394
+ }).pipe(
1395
+ import_effect11.Effect.catchTags({
1396
+ PackageJsonNotFoundError: (err) => import_effect11.Console.error(err.message).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err))),
1397
+ PackageJsonParseError: (err) => import_effect11.Console.error(err.message).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err))),
1398
+ BuildScriptNotFoundError: (err) => import_effect11.Console.error(err.message).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err))),
1399
+ PackageManagerNotFoundError: (err) => import_effect11.Console.error(err.message).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err))),
1400
+ ExecError: (err) => import_effect11.Console.error(`${err.message}: ${err.cause ?? err.command}`).pipe(import_effect11.Effect.andThen(import_effect11.Effect.fail(err)))
1401
+ })
1402
+ )
1403
+ );
1404
+
1405
+ // src/commands/debug/index.ts
1406
+ var import_cli5 = require("@effect/cli");
1407
+ var import_effect14 = require("effect");
1408
+
1409
+ // src/commands/debug/config/index.ts
1410
+ var import_cli4 = require("@effect/cli");
1411
+ var import_effect13 = require("effect");
1412
+
1413
+ // src/services/project-config.ts
1414
+ var import_network_config = require("@gigadrive/network-config");
1415
+ var import_effect12 = require("effect");
1416
+ var wrapParseErrors = (effect) => effect.pipe(
1417
+ import_effect12.Effect.catchTags({
1418
+ ConfigFileNotFoundError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.filePath })),
1419
+ ConfigFileEmptyError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.filePath })),
1420
+ ConfigFileParseError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.cause })),
1421
+ ConfigVersionError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.filePath })),
1422
+ ConfigSchemaValidationError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.validationErrors.join(", ") })),
1423
+ FunctionConfigError: (e) => import_effect12.Effect.fail(new ConfigParseError({ message: e.message, cause: e.functionPath }))
1424
+ })
1425
+ );
1426
+ var ProjectConfigService = class extends import_effect12.Effect.Service()("ProjectConfigService", {
1427
+ accessors: true,
1428
+ dependencies: [import_network_config.NetworkConfigLive],
1429
+ effect: import_effect12.Effect.gen(function* () {
1430
+ const rawReader = yield* import_network_config.RawConfigReader;
1431
+ const resolve = import_effect12.Effect.fn("ProjectConfigService.resolve")(function* (cwd) {
1432
+ yield* import_effect12.Effect.annotateCurrentSpan("cwd", cwd);
1433
+ yield* import_effect12.Effect.log("Resolving project config", { cwd });
1434
+ const configPath = yield* rawReader.findConfig(cwd);
1435
+ const detection = yield* (0, import_network_config.detectFramework)(cwd).pipe(
1436
+ import_effect12.Effect.catchTag("FrameworkNotDetectedError", () => import_effect12.Effect.succeed(null)),
1437
+ import_effect12.Effect.catchTag(
1438
+ "ManifestReadError",
1439
+ (e) => import_effect12.Effect.fail(new ConfigParseError({ message: `Framework detection failed: ${e.message}`, cause: e.filePath }))
1440
+ ),
1441
+ import_effect12.Effect.catchTag(
1442
+ "ManifestParseError",
1443
+ (e) => import_effect12.Effect.fail(new ConfigParseError({ message: `Framework detection failed: ${e.message}`, cause: e.filePath }))
1444
+ )
1445
+ );
1446
+ if (detection) {
1447
+ yield* import_effect12.Effect.log("Framework auto-detected", {
1448
+ framework: detection.framework.name,
1449
+ slug: detection.framework.slug
1450
+ });
1451
+ }
1452
+ let config;
1453
+ let resolvedConfigPath = null;
1454
+ let framework;
1455
+ if (configPath && detection) {
1456
+ yield* import_effect12.Effect.log("Config file found, merging with framework defaults", { configPath });
1457
+ resolvedConfigPath = configPath;
1458
+ const userConfig = yield* wrapParseErrors((0, import_network_config.parseConfigRaw)(configPath, cwd));
1459
+ const merged = yield* (0, import_network_config.mergeWithFrameworkDefaults)(userConfig, detection.config);
1460
+ config = yield* wrapParseErrors((0, import_network_config.postProcessConfig)(merged, cwd));
1461
+ framework = { name: detection.framework.name, slug: detection.framework.slug };
1462
+ } else if (configPath) {
1463
+ yield* import_effect12.Effect.log("Config file found", { configPath });
1464
+ resolvedConfigPath = configPath;
1465
+ config = yield* wrapParseErrors((0, import_network_config.parseConfig)(configPath, cwd));
1466
+ } else if (detection) {
1467
+ config = yield* wrapParseErrors((0, import_network_config.postProcessConfig)(detection.config, cwd));
1468
+ framework = { name: detection.framework.name, slug: detection.framework.slug };
1469
+ } else {
1470
+ return yield* import_effect12.Effect.fail(
1471
+ new ConfigNotFoundError({
1472
+ message: "No config file found and no framework detected.",
1473
+ directory: cwd
1474
+ })
1475
+ );
1476
+ }
1477
+ for (const warning of config.warnings) {
1478
+ yield* import_effect12.Effect.logWarning(warning);
1479
+ }
1480
+ if (config.errors.length > 0) {
1481
+ return yield* import_effect12.Effect.fail(
1482
+ new ConfigValidationError({
1483
+ message: "Config file has validation errors",
1484
+ errors: config.errors
1485
+ })
1486
+ );
1487
+ }
1488
+ return { config, configPath: resolvedConfigPath, framework };
1489
+ });
1490
+ return { resolve };
1491
+ })
1492
+ }) {
1493
+ };
1494
+
1495
+ // src/commands/debug/config/index.ts
1496
+ var debugConfigCommand = import_cli4.Command.make(
1497
+ "config",
1498
+ {},
1499
+ () => import_effect13.Effect.gen(function* () {
1500
+ const projectConfig = yield* ProjectConfigService;
1501
+ const cwd = process.cwd();
1502
+ const { config, configPath } = yield* projectConfig.resolve(cwd);
1503
+ yield* import_effect13.Console.log(`Config file found at: ${configPath}`);
1504
+ yield* import_effect13.Console.log(JSON.stringify(config, null, 2));
1505
+ }).pipe(
1506
+ import_effect13.Effect.catchTags({
1507
+ ConfigNotFoundError: (err) => import_effect13.Console.error(err.message),
1508
+ ConfigParseError: (err) => import_effect13.Console.error(`Config parse error: ${err.message}`),
1509
+ ConfigValidationError: (err) => import_effect13.Effect.gen(function* () {
1510
+ for (const e of err.errors) {
1511
+ yield* import_effect13.Console.error(e);
1512
+ }
1513
+ yield* import_effect13.Console.error(err.message);
1514
+ })
1515
+ })
1516
+ )
1517
+ );
1518
+
1519
+ // src/commands/debug/index.ts
1520
+ var debugBase = import_cli5.Command.make("debug", {}, () => import_effect14.Effect.void);
1521
+ var debugCommand = debugBase.pipe(import_cli5.Command.withSubcommands([debugConfigCommand]));
1522
+
1523
+ // src/commands/deployments/index.ts
1524
+ var import_cli6 = require("@effect/cli");
1525
+ var import_effect15 = require("effect");
1526
+ var DEPLOYMENT_STATUSES = ["PENDING", "QUEUED", "STARTING", "BUILDING", "PROVISIONING", "ACTIVE", "FAILED"];
1527
+ var appOption2 = import_cli6.Options.text("app").pipe(
1528
+ import_cli6.Options.withAlias("a"),
1529
+ import_cli6.Options.withDescription("Filter by application ID (defaults to the linked application)"),
1530
+ import_cli6.Options.optional
1531
+ );
1532
+ var orgOption3 = import_cli6.Options.text("org").pipe(
1533
+ import_cli6.Options.withAlias("o"),
1534
+ import_cli6.Options.withDescription("Filter by organization ID"),
1535
+ import_cli6.Options.optional
1536
+ );
1537
+ var statusOption = import_cli6.Options.choice("status", DEPLOYMENT_STATUSES).pipe(
1538
+ import_cli6.Options.withAlias("s"),
1539
+ import_cli6.Options.withDescription("Filter by deployment status"),
1540
+ import_cli6.Options.optional
1541
+ );
1542
+ var resolveApplicationFilter = (app) => import_effect15.Option.match(app, {
1543
+ onSome: (id) => import_effect15.Effect.succeed(id),
1544
+ onNone: () => import_effect15.Effect.gen(function* () {
1545
+ const projectLink = yield* ProjectLinkService;
1546
+ const link = yield* projectLink.load(process.cwd());
1547
+ return import_effect15.Option.match(link, { onNone: () => void 0, onSome: (l) => l.applicationId });
1548
+ })
1549
+ });
1550
+ var deploymentsListCommand = import_cli6.Command.make(
1551
+ "list",
1552
+ { app: appOption2, org: orgOption3, status: statusOption },
1553
+ ({ app, org, status }) => import_effect15.Effect.gen(function* () {
1554
+ const apiClient = yield* ApiClientService;
1555
+ const applicationId = yield* resolveApplicationFilter(app);
1556
+ const { items, total } = yield* apiClient.request(
1557
+ (client) => client.deployments.list({
1558
+ applicationId,
1559
+ organizationId: import_effect15.Option.getOrUndefined(org),
1560
+ status: import_effect15.Option.getOrUndefined(status)
1561
+ })
1562
+ );
1563
+ if (items.length === 0) {
1564
+ yield* import_effect15.Console.log("No deployments found.");
1565
+ return;
1566
+ }
1567
+ for (const d of items) {
1568
+ yield* import_effect15.Console.log(`${d.id} ${d.status.padEnd(12)} ${d.createdAt}`);
1569
+ }
1570
+ yield* import_effect15.Console.log(`
1571
+ ${total} deployment(s).`);
1572
+ }).pipe(
1573
+ import_effect15.Effect.catchTags({
1574
+ NotAuthenticatedError: (err) => import_effect15.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1575
+ import_effect15.Effect.andThen(import_effect15.Effect.fail(err))
1576
+ ),
1577
+ ApiRequestError: (err) => import_effect15.Console.error(`Failed to list deployments: ${err.message}`).pipe(import_effect15.Effect.andThen(import_effect15.Effect.fail(err))),
1578
+ ProjectLinkReadError: (err) => import_effect15.Console.error(err.message).pipe(import_effect15.Effect.andThen(import_effect15.Effect.fail(err)))
1579
+ })
1580
+ )
1581
+ );
1582
+ var deploymentIdArg = import_cli6.Args.text({ name: "deployment-id" }).pipe(import_cli6.Args.withDescription("The deployment ID to inspect"));
1583
+ var deploymentsInspectCommand = import_cli6.Command.make(
1584
+ "inspect",
1585
+ { deploymentId: deploymentIdArg },
1586
+ ({ deploymentId }) => import_effect15.Effect.gen(function* () {
1587
+ const apiClient = yield* ApiClientService;
1588
+ const deployment = yield* apiClient.request((client) => client.deployments.get(deploymentId));
1589
+ yield* import_effect15.Console.log(`ID: ${deployment.id}`);
1590
+ yield* import_effect15.Console.log(`Application: ${deployment.applicationId}`);
1591
+ yield* import_effect15.Console.log(`Status: ${deployment.status}`);
1592
+ yield* import_effect15.Console.log(`Created: ${deployment.createdAt}`);
1593
+ yield* import_effect15.Console.log(`Updated: ${deployment.updatedAt}`);
1594
+ const hostnames = yield* apiClient.request((client) => client.deployments.getHostnames(deploymentId)).pipe(import_effect15.Effect.catchAll(() => import_effect15.Effect.succeed({ items: [], total: 0 })));
1595
+ if (hostnames.items.length > 0) {
1596
+ yield* import_effect15.Console.log("Hostnames:");
1597
+ for (const h of hostnames.items) {
1598
+ yield* import_effect15.Console.log(` https://${h.hostname}${h.active ? "" : " (inactive)"}`);
1599
+ }
1600
+ }
1601
+ }).pipe(
1602
+ import_effect15.Effect.catchTags({
1603
+ NotAuthenticatedError: (err) => import_effect15.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1604
+ import_effect15.Effect.andThen(import_effect15.Effect.fail(err))
1605
+ ),
1606
+ ApiRequestError: (err) => import_effect15.Console.error(`Failed to inspect deployment: ${err.message}`).pipe(import_effect15.Effect.andThen(import_effect15.Effect.fail(err)))
1607
+ })
1608
+ )
1609
+ );
1610
+ var deploymentsBase = import_cli6.Command.make("deployments", {}, () => import_effect15.Effect.void);
1611
+ var deploymentsCommand = deploymentsBase.pipe(
1612
+ import_cli6.Command.withSubcommands([deploymentsListCommand, deploymentsInspectCommand])
1613
+ );
1614
+
1615
+ // src/commands/env/index.ts
1616
+ var import_cli7 = require("@effect/cli");
1617
+ var import_effect19 = require("effect");
1618
+
1619
+ // src/services/env-file.ts
1620
+ var import_platform6 = require("@effect/platform");
1621
+ var import_effect16 = require("effect");
1622
+
1623
+ // src/lib/dotenv.ts
1624
+ var DEFAULT_ENV_HEADER = "# Generated by `gigadrive env pull` \u2014 do not edit by hand.";
1625
+ var UNQUOTED_SAFE = /^[A-Za-z0-9_.\-/@:+]*$/;
1626
+ var formatEnvValue = (value) => {
1627
+ if (UNQUOTED_SAFE.test(value)) {
1628
+ return value;
1629
+ }
1630
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
1631
+ return `"${escaped}"`;
1632
+ };
1633
+ var serializeEnv = (entries, opts) => {
1634
+ const header = opts?.header ?? DEFAULT_ENV_HEADER;
1635
+ if (entries.length === 0) {
1636
+ return `${header}
1637
+ `;
1638
+ }
1639
+ const body = entries.map((e) => `${e.key}=${formatEnvValue(e.value)}`).join("\n");
1640
+ return `${header}
1641
+ ${body}
1642
+ `;
1643
+ };
1644
+ var parseEnv = (content) => {
1645
+ const result = {};
1646
+ for (const rawLine of content.split("\n")) {
1647
+ const line = rawLine.trim();
1648
+ if (line.length === 0 || line.startsWith("#")) {
1649
+ continue;
1650
+ }
1651
+ const eq = line.indexOf("=");
1652
+ if (eq <= 0) {
1653
+ continue;
1654
+ }
1655
+ const key = line.slice(0, eq).trim();
1656
+ let value = line.slice(eq + 1).trim();
1657
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
1658
+ value = value.slice(1, -1).replace(/\\(\\|"|n|r|t)/g, (_, ch) => {
1659
+ switch (ch) {
1660
+ case "n":
1661
+ return "\n";
1662
+ case "r":
1663
+ return "\r";
1664
+ case "t":
1665
+ return " ";
1666
+ case '"':
1667
+ return '"';
1668
+ default:
1669
+ return "\\";
1670
+ }
1671
+ });
1672
+ }
1673
+ result[key] = value;
1674
+ }
1675
+ return result;
1676
+ };
1677
+
1678
+ // src/services/env-file.ts
1679
+ var EnvFileService = class extends import_effect16.Effect.Service()("EnvFileService", {
1680
+ accessors: true,
1681
+ effect: import_effect16.Effect.gen(function* () {
1682
+ const fs = yield* import_platform6.FileSystem.FileSystem;
1683
+ const pathService = yield* import_platform6.Path.Path;
1684
+ const exists = import_effect16.Effect.fn("EnvFileService.exists")(function* (filePath) {
1685
+ return yield* fs.exists(filePath).pipe(import_effect16.Effect.mapError(() => new EnvFileReadError({ message: `Failed to check whether ${filePath} exists` })));
1686
+ });
1687
+ const read = import_effect16.Effect.fn("EnvFileService.read")(function* (filePath) {
1688
+ const present = yield* exists(filePath);
1689
+ if (!present) return import_effect16.Option.none();
1690
+ const contents = yield* fs.readFileString(filePath, "utf8").pipe(import_effect16.Effect.mapError(() => new EnvFileReadError({ message: `Failed to read ${filePath}` })));
1691
+ return import_effect16.Option.some(contents);
1692
+ });
1693
+ const write = import_effect16.Effect.fn("EnvFileService.write")(function* (filePath, entries, opts) {
1694
+ const dir = pathService.dirname(filePath);
1695
+ yield* fs.makeDirectory(dir, { recursive: true }).pipe(
1696
+ import_effect16.Effect.mapError(
1697
+ (error) => new EnvFileWriteError({ message: `Failed to create directory for ${filePath}`, cause: String(error) })
1698
+ )
1699
+ );
1700
+ yield* fs.writeFileString(filePath, serializeEnv(entries, opts), { mode: 384 }).pipe(
1701
+ import_effect16.Effect.mapError(
1702
+ (error) => new EnvFileWriteError({ message: `Failed to write ${filePath}`, cause: String(error) })
1703
+ )
1704
+ );
1705
+ yield* fs.chmod(filePath, 384).pipe(
1706
+ import_effect16.Effect.mapError(
1707
+ (error) => new EnvFileWriteError({ message: `Failed to secure ${filePath}`, cause: String(error) })
1708
+ )
1709
+ );
1710
+ });
1711
+ const ensureGitignored = import_effect16.Effect.fn("EnvFileService.ensureGitignored")(function* (dir, entry) {
1712
+ const gitignorePath = pathService.join(dir, ".gitignore");
1713
+ const existing = yield* read(gitignorePath);
1714
+ const current = import_effect16.Option.getOrElse(existing, () => "");
1715
+ const alreadyIgnored = current.split("\n").some((line) => line.trim() === entry);
1716
+ if (alreadyIgnored) {
1717
+ return false;
1718
+ }
1719
+ const separator = current.length > 0 && !current.endsWith("\n") ? "\n" : "";
1720
+ yield* fs.writeFileString(gitignorePath, `${current}${separator}${entry}
1721
+ `).pipe(
1722
+ import_effect16.Effect.mapError(
1723
+ (error) => new EnvFileWriteError({ message: "Failed to update .gitignore", cause: String(error) })
1724
+ )
1725
+ );
1726
+ return true;
1727
+ });
1728
+ return { exists, read, write, ensureGitignored };
1729
+ })
1730
+ }) {
1731
+ };
1732
+
1733
+ // src/services/local-credentials.ts
1734
+ var import_effect18 = require("effect");
1735
+ var os3 = __toESM(require("os"), 1);
1736
+
1737
+ // src/services/dev-credentials-store.ts
1738
+ var import_platform7 = require("@effect/platform");
1739
+ var import_effect17 = require("effect");
1740
+ var os2 = __toESM(require("os"), 1);
1741
+ var DevCredentialsStore = class extends import_effect17.Effect.Service()("DevCredentialsStore", {
1742
+ accessors: true,
1743
+ effect: import_effect17.Effect.gen(function* () {
1744
+ const fs = yield* import_platform7.FileSystem.FileSystem;
1745
+ const pathService = yield* import_platform7.Path.Path;
1746
+ const homeDir = os2.homedir();
1747
+ const storeDir = pathService.join(homeDir, ".gigadrive");
1748
+ const storeFile = pathService.join(storeDir, "dev-keys.json");
1749
+ const ensureDirectory = import_effect17.Effect.gen(function* () {
1750
+ yield* fs.makeDirectory(storeDir, { recursive: true }).pipe(
1751
+ import_effect17.Effect.mapError(
1752
+ (error) => new DevCredentialsStoreWriteError({ message: "Failed to create ~/.gigadrive", cause: String(error) })
1753
+ )
1754
+ );
1755
+ yield* fs.chmod(storeDir, 448).pipe(
1756
+ import_effect17.Effect.mapError(
1757
+ (error) => new DevCredentialsStoreWriteError({
1758
+ message: "Failed to set ~/.gigadrive permissions",
1759
+ cause: String(error)
1760
+ })
1761
+ )
1762
+ );
1763
+ });
1764
+ const load = import_effect17.Effect.gen(function* () {
1765
+ const present = yield* fs.exists(storeFile).pipe(import_effect17.Effect.mapError(() => new DevCredentialsStoreReadError({ message: "Failed to check dev-keys.json" })));
1766
+ if (!present) return {};
1767
+ const data = yield* fs.readFileString(storeFile, "utf8").pipe(import_effect17.Effect.mapError(() => new DevCredentialsStoreReadError({ message: "Failed to read dev-keys.json" })));
1768
+ const json = yield* import_effect17.Effect.try({
1769
+ try: () => JSON.parse(data),
1770
+ catch: () => new DevCredentialsStoreReadError({ message: "dev-keys.json contains invalid JSON" })
1771
+ });
1772
+ return yield* import_effect17.Schema.decodeUnknown(DevKeyStore)(json).pipe(
1773
+ import_effect17.Effect.mapError(() => new DevCredentialsStoreReadError({ message: "dev-keys.json has an invalid schema" }))
1774
+ );
1775
+ });
1776
+ const get = import_effect17.Effect.fn("DevCredentialsStore.get")(function* (applicationId) {
1777
+ const store = yield* load;
1778
+ const entry = store[applicationId];
1779
+ return entry ? import_effect17.Option.some(entry) : import_effect17.Option.none();
1780
+ });
1781
+ const set = import_effect17.Effect.fn("DevCredentialsStore.set")(function* (applicationId, entry) {
1782
+ const store = yield* load;
1783
+ const next = { ...store, [applicationId]: entry };
1784
+ yield* ensureDirectory;
1785
+ yield* fs.writeFileString(storeFile, `${JSON.stringify(next, null, 2)}
1786
+ `, { mode: 384 }).pipe(
1787
+ import_effect17.Effect.mapError(
1788
+ (error) => new DevCredentialsStoreWriteError({ message: "Failed to write dev-keys.json", cause: String(error) })
1789
+ )
1790
+ );
1791
+ });
1792
+ return { get, set };
1793
+ })
1794
+ }) {
1795
+ };
1796
+
1797
+ // src/services/local-credentials.ts
1798
+ var DEV_KEY_SCOPES = ["network:env_vars:read"];
1799
+ var ApiBaseUrl2 = import_effect18.Config.string("GIGADRIVE_API_BASE_URL").pipe(import_effect18.Config.withDefault("https://api.gigadrive.network"));
1800
+ var credentialEntries = (clientId, clientSecret, baseUrl) => [
1801
+ { key: "GIGADRIVE_CLIENT_ID", value: clientId },
1802
+ { key: "GIGADRIVE_CLIENT_SECRET", value: clientSecret },
1803
+ { key: "GIGADRIVE_API_BASE_URL", value: baseUrl }
1804
+ ];
1805
+ var LocalCredentialsService = class extends import_effect18.Effect.Service()("LocalCredentialsService", {
1806
+ accessors: true,
1807
+ dependencies: [ApiClientService.Default, DevCredentialsStore.Default],
1808
+ effect: import_effect18.Effect.gen(function* () {
1809
+ const apiClient = yield* ApiClientService;
1810
+ const store = yield* DevCredentialsStore;
1811
+ const baseUrl = yield* ApiBaseUrl2;
1812
+ const keyName = (applicationId) => `cli-dev:${os3.hostname()}:${applicationId}`;
1813
+ const provision = import_effect18.Effect.fn("LocalCredentialsService.provision")(function* (input) {
1814
+ const stored = yield* store.get(input.applicationId);
1815
+ const existing = input.existingContent ? parseEnv(input.existingContent) : {};
1816
+ const existingClientId = existing["GIGADRIVE_CLIENT_ID"];
1817
+ const existingClientSecret = existing["GIGADRIVE_CLIENT_SECRET"];
1818
+ const canReuse = !input.rotate && existingClientId !== void 0 && existingClientSecret !== void 0 && import_effect18.Option.isSome(stored) && stored.value.apiKeyId === existingClientId;
1819
+ if (canReuse) {
1820
+ return credentialEntries(existingClientId, existingClientSecret, baseUrl);
1821
+ }
1822
+ if (import_effect18.Option.isSome(stored)) {
1823
+ yield* apiClient.request((client) => client.apiKeys.delete(stored.value.apiKeyId)).pipe(import_effect18.Effect.catchTag("ApiRequestError", () => import_effect18.Effect.void));
1824
+ }
1825
+ const created = yield* apiClient.request(
1826
+ (client) => client.apiKeys.create({
1827
+ name: keyName(input.applicationId),
1828
+ applicationId: input.applicationId,
1829
+ scopes: DEV_KEY_SCOPES
1830
+ })
1831
+ );
1832
+ yield* store.set(input.applicationId, { apiKeyId: created.id });
1833
+ return credentialEntries(created.id, created.secret, baseUrl);
1834
+ });
1835
+ return { provision };
1836
+ })
1837
+ }) {
1838
+ };
1839
+
1840
+ // src/commands/env/index.ts
1841
+ var appOption3 = import_cli7.Options.text("app").pipe(
1842
+ import_cli7.Options.withAlias("a"),
1843
+ import_cli7.Options.withDescription("Operate on this application (overrides the linked application)"),
1844
+ import_cli7.Options.optional
1845
+ );
1846
+ var orgOption4 = import_cli7.Options.text("org").pipe(
1847
+ import_cli7.Options.withAlias("o"),
1848
+ import_cli7.Options.withDescription("Operate on this organization instead of an application"),
1849
+ import_cli7.Options.optional
1850
+ );
1851
+ var sensitiveOption = import_cli7.Options.boolean("sensitive").pipe(
1852
+ import_cli7.Options.withDescription("Store the value as sensitive (hidden in API responses)")
1853
+ );
1854
+ var envIdOption = import_cli7.Options.text("env").pipe(
1855
+ import_cli7.Options.withAlias("e"),
1856
+ import_cli7.Options.withDescription("Environment ID for an environment-scoped variable (application scope only)"),
1857
+ import_cli7.Options.optional
1858
+ );
1859
+ var keyValueArg = import_cli7.Args.text({ name: "key=value" }).pipe(
1860
+ import_cli7.Args.withDescription("Variable assignment, e.g. DATABASE_URL=postgres://user:pass@host/db")
1861
+ );
1862
+ var keyOrIdArg = import_cli7.Args.text({ name: "key-or-id" }).pipe(import_cli7.Args.withDescription("Variable key or ID to remove"));
1863
+ var fileArg = import_cli7.Args.text({ name: "file" }).pipe(
1864
+ import_cli7.Args.withDescription("Output file to write (default: .env.local)"),
1865
+ import_cli7.Args.optional
1866
+ );
1867
+ var environmentOption = import_cli7.Options.text("environment").pipe(
1868
+ import_cli7.Options.withAlias("e"),
1869
+ import_cli7.Options.withDescription("Environment slug to resolve overrides for (e.g. preview). Omit for the local baseline"),
1870
+ import_cli7.Options.optional
1871
+ );
1872
+ var yesOption = import_cli7.Options.boolean("yes").pipe(
1873
+ import_cli7.Options.withAlias("y"),
1874
+ import_cli7.Options.withDescription("Overwrite an existing file without confirmation")
1875
+ );
1876
+ var withCredentialsOption = import_cli7.Options.boolean("with-credentials").pipe(
1877
+ import_cli7.Options.withDescription("Also provision API-key credentials so the local app can call the API as this application")
1878
+ );
1879
+ var rotateOption = import_cli7.Options.boolean("rotate").pipe(
1880
+ import_cli7.Options.withDescription("Force a fresh API key when provisioning credentials, revoking the previous one")
1881
+ );
1882
+ var resolveScope = (app, org) => import_effect19.Effect.gen(function* () {
1883
+ if (import_effect19.Option.isSome(org)) {
1884
+ return { kind: "organization", id: org.value };
1885
+ }
1886
+ const projectLink = yield* ProjectLinkService;
1887
+ const id = yield* import_effect19.Option.match(app, {
1888
+ onSome: (value) => import_effect19.Effect.succeed(value),
1889
+ onNone: () => projectLink.resolve(process.cwd()).pipe(import_effect19.Effect.map((link) => link.applicationId))
1890
+ });
1891
+ return { kind: "application", id };
1892
+ });
1893
+ var envListCommand = import_cli7.Command.make(
1894
+ "list",
1895
+ { app: appOption3, org: orgOption4 },
1896
+ ({ app, org }) => import_effect19.Effect.gen(function* () {
1897
+ const apiClient = yield* ApiClientService;
1898
+ const scope = yield* resolveScope(app, org);
1899
+ const { items } = yield* apiClient.request(
1900
+ (client) => scope.kind === "organization" ? client.organizations.envVars.list(scope.id) : client.applications.envVars.list(scope.id)
1901
+ );
1902
+ if (items.length === 0) {
1903
+ yield* import_effect19.Console.log("No environment variables set.");
1904
+ return;
1905
+ }
1906
+ for (const v of items) {
1907
+ yield* import_effect19.Console.log(`${v.key}=${v.sensitive ? "***" : v.value ?? ""}`);
1908
+ }
1909
+ }).pipe(
1910
+ import_effect19.Effect.catchTags({
1911
+ NotAuthenticatedError: (err) => import_effect19.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1912
+ import_effect19.Effect.andThen(import_effect19.Effect.fail(err))
1913
+ ),
1914
+ ApiRequestError: (err) => import_effect19.Console.error(`Failed to list environment variables: ${err.message}`).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
1915
+ ProjectNotLinkedError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
1916
+ ProjectLinkReadError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err)))
1917
+ })
1918
+ )
1919
+ );
1920
+ var envSetCommand = import_cli7.Command.make(
1921
+ "set",
1922
+ { keyValue: keyValueArg, app: appOption3, org: orgOption4, sensitive: sensitiveOption, env: envIdOption },
1923
+ ({ keyValue, app, org, sensitive, env }) => import_effect19.Effect.gen(function* () {
1924
+ const separator = keyValue.indexOf("=");
1925
+ if (separator <= 0) {
1926
+ return yield* import_effect19.Effect.fail(
1927
+ new InvalidEnvVarFormatError({
1928
+ message: 'Invalid format. Use KEY=VALUE, e.g. "gigadrive env set DATABASE_URL=postgres://...".'
1929
+ })
1930
+ );
1931
+ }
1932
+ const key = keyValue.slice(0, separator);
1933
+ const value = keyValue.slice(separator + 1);
1934
+ const apiClient = yield* ApiClientService;
1935
+ const scope = yield* resolveScope(app, org);
1936
+ const environmentId = import_effect19.Option.getOrUndefined(env);
1937
+ const created = yield* apiClient.request(
1938
+ (client) => scope.kind === "organization" ? client.organizations.envVars.create(scope.id, { key, value, sensitive }) : client.applications.envVars.create(scope.id, { key, value, sensitive, environmentId })
1939
+ );
1940
+ yield* import_effect19.Console.log(`Set ${created.key}.`);
1941
+ }).pipe(
1942
+ import_effect19.Effect.catchTags({
1943
+ InvalidEnvVarFormatError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
1944
+ NotAuthenticatedError: (err) => import_effect19.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1945
+ import_effect19.Effect.andThen(import_effect19.Effect.fail(err))
1946
+ ),
1947
+ ApiRequestError: (err) => import_effect19.Console.error(`Failed to set environment variable: ${err.message}`).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
1948
+ ProjectNotLinkedError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
1949
+ ProjectLinkReadError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err)))
1950
+ })
1951
+ )
1952
+ );
1953
+ var envRmCommand = import_cli7.Command.make(
1954
+ "rm",
1955
+ { keyOrId: keyOrIdArg, app: appOption3, org: orgOption4 },
1956
+ ({ keyOrId, app, org }) => import_effect19.Effect.gen(function* () {
1957
+ const apiClient = yield* ApiClientService;
1958
+ const scope = yield* resolveScope(app, org);
1959
+ const { items } = yield* apiClient.request(
1960
+ (client) => scope.kind === "organization" ? client.organizations.envVars.list(scope.id) : client.applications.envVars.list(scope.id)
1961
+ );
1962
+ const target = items.find((v) => v.id === keyOrId || v.key === keyOrId);
1963
+ if (target === void 0) {
1964
+ return yield* import_effect19.Effect.fail(
1965
+ new EnvVarNotFoundError({ message: `No environment variable matching "${keyOrId}".` })
1966
+ );
1967
+ }
1968
+ yield* apiClient.request(
1969
+ (client) => scope.kind === "organization" ? client.organizations.envVars.delete(scope.id, target.id) : client.applications.envVars.delete(scope.id, target.id)
1970
+ );
1971
+ yield* import_effect19.Console.log(`Removed ${target.key}.`);
1972
+ }).pipe(
1973
+ import_effect19.Effect.catchTags({
1974
+ EnvVarNotFoundError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
1975
+ NotAuthenticatedError: (err) => import_effect19.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1976
+ import_effect19.Effect.andThen(import_effect19.Effect.fail(err))
1977
+ ),
1978
+ ApiRequestError: (err) => import_effect19.Console.error(`Failed to remove environment variable: ${err.message}`).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
1979
+ ProjectNotLinkedError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
1980
+ ProjectLinkReadError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err)))
1981
+ })
1982
+ )
1983
+ );
1984
+ var envPullCommand = import_cli7.Command.make(
1985
+ "pull",
1986
+ {
1987
+ file: fileArg,
1988
+ app: appOption3,
1989
+ environment: environmentOption,
1990
+ yes: yesOption,
1991
+ withCredentials: withCredentialsOption,
1992
+ rotate: rotateOption
1993
+ },
1994
+ ({ file, app, environment, yes, withCredentials, rotate }) => import_effect19.Effect.gen(function* () {
1995
+ const apiClient = yield* ApiClientService;
1996
+ const projectLink = yield* ProjectLinkService;
1997
+ const envFile = yield* EnvFileService;
1998
+ const applicationId = yield* import_effect19.Option.match(app, {
1999
+ onSome: (value) => import_effect19.Effect.succeed(value),
2000
+ onNone: () => projectLink.resolve(process.cwd()).pipe(import_effect19.Effect.map((link) => link.applicationId))
2001
+ });
2002
+ const target = import_effect19.Option.getOrElse(file, () => ".env.local");
2003
+ const environmentSlug = import_effect19.Option.getOrUndefined(environment);
2004
+ const { items, omittedSensitive } = yield* apiClient.request(
2005
+ (client) => client.applications.envVars.pull(applicationId, environmentSlug ? { environment: environmentSlug } : void 0)
2006
+ );
2007
+ const existing = yield* envFile.read(target);
2008
+ if (import_effect19.Option.isSome(existing) && !yes) {
2009
+ const overwrite = yield* import_cli7.Prompt.run(
2010
+ import_cli7.Prompt.confirm({ message: `${target} already exists. Overwrite?`, initial: false })
866
2011
  );
867
- import_effect10.Option.match(stored, {
868
- onNone: () => {
869
- },
870
- onSome: (data) => {
871
- if (data.accessToken && data.tokenExpirationTime && Date.now() < data.tokenExpirationTime) {
872
- accessToken = data.accessToken;
873
- tokenExpirationTime = data.tokenExpirationTime;
874
- }
875
- }
876
- });
877
- if (accessToken && Date.now() < tokenExpirationTime) {
878
- return accessToken;
2012
+ if (!overwrite) {
2013
+ return yield* import_effect19.Console.log("Cancelled.");
879
2014
  }
880
- yield* import_effect10.Effect.log("Access token expired or not available. Attempting to refresh...");
881
- yield* refreshAccessToken.pipe(
882
- import_effect10.Effect.catchTag("TokenRefreshError", (err) => import_effect10.Effect.fail(new NotAuthenticatedError({ message: err.message })))
2015
+ }
2016
+ let credentialEntries2 = [];
2017
+ if (withCredentials) {
2018
+ const localCredentials = yield* LocalCredentialsService;
2019
+ credentialEntries2 = yield* localCredentials.provision({
2020
+ applicationId,
2021
+ rotate,
2022
+ existingContent: import_effect19.Option.getOrUndefined(existing)
2023
+ });
2024
+ }
2025
+ const entries = [...items.map((v) => ({ key: v.key, value: v.value })), ...credentialEntries2];
2026
+ yield* envFile.write(target, entries);
2027
+ const gitignored = yield* envFile.ensureGitignored(process.cwd(), target);
2028
+ yield* import_effect19.Console.log(
2029
+ `Pulled ${items.length} environment variable(s)${withCredentials ? " and API credentials" : ""} to ${target}.`
2030
+ );
2031
+ if (omittedSensitive > 0) {
2032
+ yield* import_effect19.Console.log(
2033
+ `Skipped ${omittedSensitive} sensitive variable(s); secrets live only in production/preview environments.`
883
2034
  );
884
- if (!accessToken) {
885
- return yield* import_effect10.Effect.fail(
886
- new NotAuthenticatedError({ message: "No valid access token available. Please log in first." })
887
- );
888
- }
889
- return accessToken;
890
- });
891
- const getUserInfo = import_effect10.Effect.gen(function* () {
892
- const config = yield* oauthConfig.getConfig;
893
- const token = yield* getAccessToken;
894
- const fetchUserInfo = (t) => import_effect10.Effect.tryPromise({
895
- try: () => fetch(config.userinfoUrl, {
896
- headers: {
897
- Authorization: `Bearer ${t}`,
898
- Accept: "application/json"
899
- }
900
- }),
901
- catch: (error) => new UserInfoFetchError({
902
- message: `Failed to fetch user info: ${error instanceof Error ? error.message : String(error)}`
2035
+ }
2036
+ if (gitignored) {
2037
+ yield* import_effect19.Console.log(`Added ${target} to .gitignore.`);
2038
+ }
2039
+ }).pipe(
2040
+ import_effect19.Effect.catchTags({
2041
+ // Ctrl+C during the overwrite prompt is a deliberate cancel — exit 0.
2042
+ QuitException: () => import_effect19.Console.log("Cancelled."),
2043
+ NotAuthenticatedError: (err) => import_effect19.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
2044
+ import_effect19.Effect.andThen(import_effect19.Effect.fail(err))
2045
+ ),
2046
+ ApiRequestError: (err) => import_effect19.Console.error(`Failed to pull environment variables: ${err.message}`).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
2047
+ ProjectNotLinkedError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
2048
+ ProjectLinkReadError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
2049
+ EnvFileReadError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
2050
+ EnvFileWriteError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
2051
+ DevCredentialsStoreReadError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err))),
2052
+ DevCredentialsStoreWriteError: (err) => import_effect19.Console.error(err.message).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err)))
2053
+ })
2054
+ )
2055
+ );
2056
+ var envBase = import_cli7.Command.make("env", {}, () => import_effect19.Effect.void);
2057
+ var envCommand = envBase.pipe(
2058
+ import_cli7.Command.withSubcommands([envListCommand, envSetCommand, envRmCommand, envPullCommand])
2059
+ );
2060
+
2061
+ // src/commands/link/index.ts
2062
+ var import_cli8 = require("@effect/cli");
2063
+ var import_effect20 = require("effect");
2064
+ var appOption4 = import_cli8.Options.text("app").pipe(
2065
+ import_cli8.Options.withAlias("a"),
2066
+ import_cli8.Options.withDescription("Application ID to link (skips the interactive picker)"),
2067
+ import_cli8.Options.optional
2068
+ );
2069
+ var orgOption5 = import_cli8.Options.text("org").pipe(
2070
+ import_cli8.Options.withAlias("o"),
2071
+ import_cli8.Options.withDescription("Only list applications belonging to this organization ID"),
2072
+ import_cli8.Options.optional
2073
+ );
2074
+ var linkCommand = import_cli8.Command.make(
2075
+ "link",
2076
+ { app: appOption4, org: orgOption5 },
2077
+ ({ app, org }) => import_effect20.Effect.gen(function* () {
2078
+ const apiClient = yield* ApiClientService;
2079
+ const projectLink = yield* ProjectLinkService;
2080
+ const cwd = process.cwd();
2081
+ const organizationId = import_effect20.Option.getOrUndefined(org);
2082
+ const { items: apps } = yield* apiClient.request((client) => client.applications.list({ organizationId }));
2083
+ if (apps.length === 0) {
2084
+ return yield* import_effect20.Effect.fail(
2085
+ new NoApplicationsFoundError({
2086
+ message: 'No applications found. Create one in the Gigadrive console, then run "gigadrive link" again.'
903
2087
  })
904
- });
905
- let response = yield* fetchUserInfo(token);
906
- if (response.status === 401 || response.status === 403) {
907
- yield* refreshAccessToken.pipe(
908
- import_effect10.Effect.catchTag("TokenRefreshError", (err) => import_effect10.Effect.fail(new UserInfoFetchError({ message: err.message })))
909
- );
910
- if (!accessToken) {
911
- return yield* import_effect10.Effect.fail(new UserInfoFetchError({ message: "Token refresh failed" }));
912
- }
913
- response = yield* fetchUserInfo(accessToken);
914
- }
915
- if (!response.ok) {
916
- const body = yield* import_effect10.Effect.tryPromise({
917
- try: () => response.text(),
918
- catch: () => new UserInfoFetchError({ message: "Failed to read user info error response" })
919
- });
920
- return yield* import_effect10.Effect.fail(
921
- new UserInfoFetchError({
922
- message: `Failed to fetch user info: ${response.status} ${response.statusText}${body ? ` - ${body}` : ""}`,
923
- statusCode: response.status
2088
+ );
2089
+ }
2090
+ const explicitAppId = import_effect20.Option.getOrUndefined(app);
2091
+ let applicationId;
2092
+ let linkedOrganizationId;
2093
+ if (explicitAppId !== void 0) {
2094
+ const match = apps.find((a) => a.id === explicitAppId);
2095
+ if (match === void 0) {
2096
+ return yield* import_effect20.Effect.fail(
2097
+ new ApplicationNotFoundError({
2098
+ message: `Application "${explicitAppId}" was not found among your applications.`
924
2099
  })
925
2100
  );
926
2101
  }
927
- const data = yield* import_effect10.Effect.tryPromise({
928
- try: () => response.json(),
929
- catch: () => new UserInfoFetchError({ message: "Failed to parse user info response" })
930
- });
931
- return data;
932
- });
933
- const inferUserName = (info) => {
934
- const isNonEmptyString = (value) => typeof value === "string" && value.trim() !== "";
935
- const name = info.name;
936
- const givenName = info.given_name;
937
- const familyName = info.family_name;
938
- const preferredUsername = info.preferred_username;
939
- const email = info.email;
940
- return isNonEmptyString(name) && name || (isNonEmptyString(givenName) && isNonEmptyString(familyName) ? `${givenName} ${familyName}` : void 0) || isNonEmptyString(preferredUsername) && preferredUsername || isNonEmptyString(email) && email || "your account";
941
- };
942
- return { login, logout, getAccessToken, getUserInfo, refreshAccessToken, inferUserName };
943
- })
944
- }) {
945
- };
2102
+ applicationId = match.id;
2103
+ linkedOrganizationId = match.organization.id;
2104
+ } else if (apps.length === 1) {
2105
+ applicationId = apps[0].id;
2106
+ linkedOrganizationId = apps[0].organization.id;
2107
+ } else {
2108
+ const chosen = yield* import_cli8.Prompt.run(
2109
+ import_cli8.Prompt.select({
2110
+ message: "Select an application to link",
2111
+ choices: apps.map((a) => ({ title: `${a.name} (${a.organization.name})`, value: a }))
2112
+ })
2113
+ );
2114
+ applicationId = chosen.id;
2115
+ linkedOrganizationId = chosen.organization.id;
2116
+ }
2117
+ yield* projectLink.save(cwd, { applicationId, organizationId: linkedOrganizationId });
2118
+ yield* import_effect20.Console.log(`Linked this directory to application ${applicationId}.`);
2119
+ yield* import_effect20.Console.log('Tip: add ".gigadrive/" to your .gitignore.');
2120
+ }).pipe(
2121
+ import_effect20.Effect.catchTags({
2122
+ // Ctrl+C during the picker is a deliberate cancel — exit 0.
2123
+ QuitException: () => import_effect20.Console.log("Cancelled."),
2124
+ NotAuthenticatedError: (err) => import_effect20.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
2125
+ import_effect20.Effect.andThen(import_effect20.Effect.fail(err))
2126
+ ),
2127
+ ApiRequestError: (err) => import_effect20.Console.error(`Failed to list applications: ${err.message}`).pipe(import_effect20.Effect.andThen(import_effect20.Effect.fail(err))),
2128
+ NoApplicationsFoundError: (err) => import_effect20.Console.error(err.message).pipe(import_effect20.Effect.andThen(import_effect20.Effect.fail(err))),
2129
+ ApplicationNotFoundError: (err) => import_effect20.Console.error(err.message).pipe(import_effect20.Effect.andThen(import_effect20.Effect.fail(err))),
2130
+ ProjectLinkWriteError: (err) => import_effect20.Console.error(err.message).pipe(import_effect20.Effect.andThen(import_effect20.Effect.fail(err)))
2131
+ })
2132
+ )
2133
+ );
2134
+ var unlinkCommand = import_cli8.Command.make(
2135
+ "unlink",
2136
+ {},
2137
+ () => import_effect20.Effect.gen(function* () {
2138
+ const projectLink = yield* ProjectLinkService;
2139
+ yield* projectLink.remove(process.cwd());
2140
+ yield* import_effect20.Console.log("Unlinked this directory.");
2141
+ }).pipe(
2142
+ import_effect20.Effect.catchTags({
2143
+ ProjectLinkWriteError: (err) => import_effect20.Console.error(err.message).pipe(import_effect20.Effect.andThen(import_effect20.Effect.fail(err)))
2144
+ })
2145
+ )
2146
+ );
946
2147
 
947
2148
  // src/commands/login/index.ts
948
- var loginCommand = import_cli4.Command.make(
2149
+ var import_cli9 = require("@effect/cli");
2150
+ var import_effect21 = require("effect");
2151
+ var loginCommand = import_cli9.Command.make(
949
2152
  "login",
950
2153
  {},
951
- () => import_effect11.Effect.gen(function* () {
952
- yield* import_effect11.Console.log("Initiating login...");
2154
+ () => import_effect21.Effect.gen(function* () {
2155
+ yield* import_effect21.Console.log("Initiating login...");
953
2156
  const auth = yield* AuthService;
954
2157
  yield* auth.login;
955
- const info = yield* auth.getUserInfo.pipe(import_effect11.Effect.catchAll(() => import_effect11.Effect.succeed({})));
2158
+ const info = yield* auth.getUserInfo.pipe(import_effect21.Effect.catchAll(() => import_effect21.Effect.succeed({})));
956
2159
  const name = auth.inferUserName(info);
957
- yield* import_effect11.Console.log(`You are now logged in as ${name}.`);
2160
+ yield* import_effect21.Console.log(`You are now logged in as ${name}.`);
958
2161
  }).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}`)
2162
+ import_effect21.Effect.catchTags({
2163
+ LoginFlowError: (err) => import_effect21.Console.error(`Login failed: ${err.message}`),
2164
+ TokenExchangeError: (err) => import_effect21.Console.error(`Login failed: ${err.message}`),
2165
+ OAuthDiscoveryError: (err) => import_effect21.Console.error(`Login failed: ${err.message}`),
2166
+ AuthStorageWriteError: (err) => import_effect21.Console.error(`Failed to save credentials: ${err.message}`)
2167
+ })
2168
+ )
2169
+ );
2170
+
2171
+ // src/commands/logout/index.ts
2172
+ var import_cli10 = require("@effect/cli");
2173
+ var import_effect22 = require("effect");
2174
+ var logoutCommand = import_cli10.Command.make(
2175
+ "logout",
2176
+ {},
2177
+ () => import_effect22.Effect.gen(function* () {
2178
+ const auth = yield* AuthService;
2179
+ yield* auth.logout;
2180
+ yield* import_effect22.Console.log("Logged out.");
2181
+ }).pipe(
2182
+ import_effect22.Effect.catchTags({
2183
+ AuthStorageWriteError: (err) => import_effect22.Console.error(`Failed to log out: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err)))
964
2184
  })
965
2185
  )
966
2186
  );
967
2187
 
968
2188
  // src/commands/platform/index.ts
969
- var import_cli6 = require("@effect/cli");
970
- var import_effect15 = require("effect");
2189
+ var import_cli12 = require("@effect/cli");
2190
+ var import_effect26 = require("effect");
971
2191
 
972
2192
  // 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");
977
- var os2 = __toESM(require("os"), 1);
2193
+ var import_cli11 = require("@effect/cli");
2194
+ var import_platform9 = require("@effect/platform");
2195
+ var import_commons = require("@gigadrive/commons");
2196
+ var import_effect25 = require("effect");
2197
+ var os4 = __toESM(require("os"), 1);
978
2198
 
979
2199
  // src/services/archive.ts
980
- var import_platform4 = require("@effect/platform");
2200
+ var import_platform8 = require("@effect/platform");
981
2201
  var import_build_utils3 = require("@gigadrive/build-utils");
982
2202
  var import_archiver = __toESM(require("archiver"), 1);
983
- var import_effect12 = require("effect");
2203
+ var import_effect23 = require("effect");
984
2204
  var import_ignore = __toESM(require("ignore"), 1);
985
2205
  var nodeFs = __toESM(require("fs"), 1);
986
2206
  var nodeFsPromises = __toESM(require("fs/promises"), 1);
987
2207
  var IGNORE_FILE_NAMES = [".gigadriveignore", ".vercelignore", ".dockerignore", ".nowignore"];
988
2208
  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([]))
2209
+ import_effect23.Effect.map((content) => content.split("\n").filter((line) => line.trim() !== "")),
2210
+ import_effect23.Effect.catchAll(() => import_effect23.Effect.succeed([]))
991
2211
  );
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}` })));
2212
+ var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) => import_effect23.Effect.gen(function* () {
2213
+ const items = yield* fs.readDirectory(dir).pipe(import_effect23.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${dir}` })));
994
2214
  for (const item of items) {
995
2215
  const fullPath = pathService.join(dir, item);
996
2216
  const relativePath = pathService.relative(baseDir, fullPath);
997
- const stat = yield* import_effect12.Effect.tryPromise({
2217
+ const stat = yield* import_effect23.Effect.tryPromise({
998
2218
  try: () => nodeFsPromises.lstat(fullPath),
999
2219
  catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
1000
2220
  });
@@ -1014,8 +2234,8 @@ var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) => impo
1014
2234
  yield* collectIgnorePatterns(fs, pathService, fullPath, baseDir, ignoreRules);
1015
2235
  }
1016
2236
  }
1017
- }).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.void));
1018
- var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect12.Effect.gen(function* () {
2237
+ }).pipe(import_effect23.Effect.catchAll(() => import_effect23.Effect.void));
2238
+ var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect23.Effect.gen(function* () {
1019
2239
  const ignoreRules = (0, import_ignore.default)();
1020
2240
  ignoreRules.add([".git/", "**/.DS_Store", "**/Thumbs.db"]);
1021
2241
  for (const ignoreFileName of IGNORE_FILE_NAMES) {
@@ -1030,14 +2250,14 @@ var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect12.Effect
1030
2250
  yield* collectIgnorePatterns(fs, pathService, baseDir, baseDir, ignoreRules);
1031
2251
  return ignoreRules;
1032
2252
  });
1033
- var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect12.Effect.gen(function* () {
2253
+ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect23.Effect.gen(function* () {
1034
2254
  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}` })));
2255
+ const walkDir = (currentDir) => import_effect23.Effect.gen(function* () {
2256
+ const items = yield* fs.readDirectory(currentDir).pipe(import_effect23.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${currentDir}` })));
1037
2257
  for (const item of items) {
1038
2258
  const fullPath = pathService.join(currentDir, item);
1039
2259
  const relativePath = pathService.relative(dir, fullPath);
1040
- const stat = yield* import_effect12.Effect.tryPromise({
2260
+ const stat = yield* import_effect23.Effect.tryPromise({
1041
2261
  try: () => nodeFsPromises.lstat(fullPath),
1042
2262
  catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
1043
2263
  });
@@ -1051,21 +2271,21 @@ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect12.E
1051
2271
  }
1052
2272
  }
1053
2273
  }
1054
- }).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.void));
2274
+ }).pipe(import_effect23.Effect.catchAll(() => import_effect23.Effect.void));
1055
2275
  yield* walkDir(dir);
1056
2276
  return filesToInclude;
1057
2277
  });
1058
- var ArchiveService = class extends import_effect12.Effect.Service()("ArchiveService", {
2278
+ var ArchiveService = class extends import_effect23.Effect.Service()("ArchiveService", {
1059
2279
  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 });
2280
+ effect: import_effect23.Effect.gen(function* () {
2281
+ const fs = yield* import_platform8.FileSystem.FileSystem;
2282
+ const pathService = yield* import_platform8.Path.Path;
2283
+ const createZipArchive = import_effect23.Effect.fn("ArchiveService.createZipArchive")(function* (inputDir, outputFile, options = {}) {
2284
+ yield* import_effect23.Effect.annotateCurrentSpan("inputDir", inputDir);
2285
+ yield* import_effect23.Effect.annotateCurrentSpan("outputFile", outputFile);
2286
+ yield* import_effect23.Effect.log("Creating zip archive", { inputDir, outputFile });
1067
2287
  const ignoreRules = options.useIgnoreFiles !== false ? yield* initializeIgnoreRules(fs, pathService, inputDir) : (0, import_ignore.default)();
1068
- const filesToInclude = options.whitelist ? yield* import_effect12.Effect.tryPromise({
2288
+ const filesToInclude = options.whitelist ? yield* import_effect23.Effect.tryPromise({
1069
2289
  try: () => Promise.all(
1070
2290
  options.whitelist.map((file) => (0, import_build_utils3.getFilesForPattern)(file, inputDir, options.excludeFiles ?? []))
1071
2291
  ),
@@ -1073,25 +2293,25 @@ var ArchiveService = class extends import_effect12.Effect.Service()("ArchiveServ
1073
2293
  message: "Failed to resolve whitelist patterns",
1074
2294
  cause: error instanceof Error ? error.message : String(error)
1075
2295
  })
1076
- }) : yield* import_effect12.Effect.gen(function* () {
2296
+ }) : yield* import_effect23.Effect.gen(function* () {
1077
2297
  if (options.excludeFiles && options.excludeFiles.length > 0) {
1078
2298
  ignoreRules.add(options.excludeFiles);
1079
2299
  }
1080
2300
  return yield* getFilesToInclude(fs, pathService, inputDir, ignoreRules).pipe(
1081
- import_effect12.Effect.map((files) => [files])
2301
+ import_effect23.Effect.map((files) => [files])
1082
2302
  );
1083
2303
  });
1084
- const archiveExists = yield* fs.exists(outputFile).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.succeed(false)));
2304
+ const archiveExists = yield* fs.exists(outputFile).pipe(import_effect23.Effect.catchAll(() => import_effect23.Effect.succeed(false)));
1085
2305
  if (archiveExists) {
1086
2306
  yield* fs.remove(outputFile).pipe(
1087
- import_effect12.Effect.mapError(
2307
+ import_effect23.Effect.mapError(
1088
2308
  (error) => new ArchiveCreateError({
1089
2309
  message: `Failed to remove existing archive: ${String(error)}`
1090
2310
  })
1091
2311
  )
1092
2312
  );
1093
2313
  }
1094
- yield* import_effect12.Effect.tryPromise({
2314
+ yield* import_effect23.Effect.tryPromise({
1095
2315
  try: () => new Promise((resolve, reject) => {
1096
2316
  const output = nodeFs.createWriteStream(outputFile);
1097
2317
  const archive = (0, import_archiver.default)("zip", { zlib: { level: 9 } });
@@ -1110,14 +2330,14 @@ var ArchiveService = class extends import_effect12.Effect.Service()("ArchiveServ
1110
2330
  })
1111
2331
  });
1112
2332
  const fileInfo = yield* fs.stat(outputFile).pipe(
1113
- import_effect12.Effect.mapError(
2333
+ import_effect23.Effect.mapError(
1114
2334
  (error) => new ArchiveCreateError({
1115
2335
  message: `Failed to stat archive: ${String(error)}`
1116
2336
  })
1117
2337
  )
1118
2338
  );
1119
2339
  const size = Number(fileInfo.size);
1120
- yield* import_effect12.Effect.log("Archive created", { size });
2340
+ yield* import_effect23.Effect.log("Archive created", { size });
1121
2341
  return { path: outputFile, size };
1122
2342
  });
1123
2343
  return { createZipArchive };
@@ -1126,250 +2346,104 @@ var ArchiveService = class extends import_effect12.Effect.Service()("ArchiveServ
1126
2346
  };
1127
2347
 
1128
2348
  // 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", {
2349
+ var import_effect24 = require("effect");
2350
+ var DeploymentApiService = class extends import_effect24.Effect.Service()("DeploymentApiService", {
1152
2351
  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
- }),
2352
+ dependencies: [ApiClientService.Default],
2353
+ effect: import_effect24.Effect.gen(function* () {
2354
+ const apiClient = yield* ApiClientService;
2355
+ const getClient = apiClient.getClient.pipe(
2356
+ import_effect24.Effect.mapError((err) => new DeploymentAuthError({ message: `Authentication failed: ${err.message}` }))
2357
+ );
2358
+ const createDeployment = import_effect24.Effect.fn("DeploymentApiService.createDeployment")(function* (applicationId) {
2359
+ yield* import_effect24.Effect.annotateCurrentSpan("applicationId", applicationId);
2360
+ yield* import_effect24.Effect.log("Creating deployment", { applicationId });
2361
+ const client = yield* getClient;
2362
+ const deployment = yield* import_effect24.Effect.tryPromise({
2363
+ try: () => client.deployments.create({ applicationId }),
1172
2364
  catch: (error) => new DeploymentCreateError({
1173
2365
  message: `Failed to create deployment: ${error instanceof Error ? error.message : String(error)}`
1174
2366
  })
1175
2367
  });
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;
2368
+ yield* import_effect24.Effect.log("Deployment created", { deploymentId: deployment.id });
2369
+ return deployment.id;
1200
2370
  });
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
- }),
2371
+ const startMultipartUpload = import_effect24.Effect.fn("DeploymentApiService.startMultipartUpload")(function* (deploymentId) {
2372
+ yield* import_effect24.Effect.annotateCurrentSpan("deploymentId", deploymentId);
2373
+ const client = yield* getClient;
2374
+ const result = yield* import_effect24.Effect.tryPromise({
2375
+ try: () => client.deployments.startUpload(deploymentId),
1209
2376
  catch: (error) => new UploadStartError({
1210
2377
  message: `Failed to start multipart upload: ${error instanceof Error ? error.message : String(error)}`
1211
2378
  })
1212
2379
  });
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;
2380
+ return result.uploadId;
1230
2381
  });
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
- ),
2382
+ const getPresignedUrl = import_effect24.Effect.fn("DeploymentApiService.getPresignedUrl")(function* (deploymentId, uploadId, partNumber) {
2383
+ const client = yield* getClient;
2384
+ const result = yield* import_effect24.Effect.tryPromise({
2385
+ try: () => client.deployments.getPresignedUrl(deploymentId, uploadId, partNumber),
1241
2386
  catch: (error) => new PresignedUrlError({
1242
2387
  message: `Failed to get presigned URL: ${error instanceof Error ? error.message : String(error)}`,
1243
2388
  partNumber
1244
2389
  })
1245
2390
  });
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;
2391
+ return result.url;
1263
2392
  });
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
- }),
2393
+ const uploadPart = import_effect24.Effect.fn("DeploymentApiService.uploadPart")(function* (presignedUrl, data, partNumber) {
2394
+ yield* import_effect24.Effect.annotateCurrentSpan("partNumber", partNumber);
2395
+ yield* import_effect24.Effect.annotateCurrentSpan("size", data.length);
2396
+ const client = yield* getClient;
2397
+ const result = yield* import_effect24.Effect.tryPromise({
2398
+ try: () => client.deployments.uploadPart(presignedUrl, data, partNumber),
1276
2399
  catch: (error) => new UploadPartError({
1277
2400
  message: `Failed to upload part ${partNumber}: ${error instanceof Error ? error.message : String(error)}`,
1278
2401
  partNumber
1279
2402
  })
1280
2403
  });
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 };
2404
+ return result;
1296
2405
  });
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
- }),
2406
+ const completeUpload = import_effect24.Effect.fn("DeploymentApiService.completeUpload")(function* (deploymentId, uploadId, parts) {
2407
+ yield* import_effect24.Effect.annotateCurrentSpan("deploymentId", deploymentId);
2408
+ yield* import_effect24.Effect.annotateCurrentSpan("partsCount", parts.length);
2409
+ const client = yield* getClient;
2410
+ yield* import_effect24.Effect.tryPromise({
2411
+ try: () => client.deployments.completeUpload(deploymentId, uploadId, parts),
1307
2412
  catch: (error) => new UploadCompleteError({
1308
2413
  message: `Failed to complete upload: ${error instanceof Error ? error.message : String(error)}`
1309
2414
  })
1310
2415
  });
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");
2416
+ yield* import_effect24.Effect.log("Upload completed successfully");
1319
2417
  });
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
- }),
2418
+ const getDeploymentStatus = import_effect24.Effect.fn("DeploymentApiService.getDeploymentStatus")(function* (deploymentId) {
2419
+ const client = yield* getClient;
2420
+ const deployment = yield* import_effect24.Effect.tryPromise({
2421
+ try: () => client.deployments.get(deploymentId),
1327
2422
  catch: (error) => new DeploymentStatusError({
1328
2423
  message: `Failed to get deployment status: ${error instanceof Error ? error.message : String(error)}`
1329
2424
  })
1330
2425
  });
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;
2426
+ return deployment.status;
1346
2427
  });
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
- }),
2428
+ const getLogs = import_effect24.Effect.fn("DeploymentApiService.getLogs")(function* (deploymentId, options) {
2429
+ const client = yield* getClient;
2430
+ const result = yield* import_effect24.Effect.tryPromise({
2431
+ try: () => client.deployments.getLogs(deploymentId, options),
1354
2432
  catch: (error) => new DeploymentLogsFetchError({
1355
2433
  message: `Failed to get logs: ${error instanceof Error ? error.message : String(error)}`
1356
2434
  })
1357
2435
  });
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" })
2436
+ return result;
2437
+ });
2438
+ const getHostnames = import_effect24.Effect.fn("DeploymentApiService.getHostnames")(function* (deploymentId) {
2439
+ const client = yield* getClient;
2440
+ const result = yield* import_effect24.Effect.tryPromise({
2441
+ try: () => client.deployments.getHostnames(deploymentId),
2442
+ catch: (error) => new DeploymentHostnamesFetchError({
2443
+ message: `Failed to get hostnames: ${error instanceof Error ? error.message : String(error)}`
2444
+ })
1368
2445
  });
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;
2446
+ return result.items;
1373
2447
  });
1374
2448
  return {
1375
2449
  createDeployment,
@@ -1378,89 +2452,102 @@ var DeploymentApiService = class extends import_effect13.Effect.Service()("Deplo
1378
2452
  uploadPart,
1379
2453
  completeUpload,
1380
2454
  getDeploymentStatus,
1381
- getLogs
2455
+ getLogs,
2456
+ getHostnames
1382
2457
  };
1383
2458
  })
1384
2459
  }) {
1385
2460
  };
1386
2461
 
1387
2462
  // src/commands/platform/deploy/index.ts
1388
- var deployCommand = import_cli5.Command.make(
2463
+ var appOption5 = import_cli11.Options.text("app").pipe(
2464
+ import_cli11.Options.withAlias("a"),
2465
+ import_cli11.Options.withDescription("Application ID to deploy to (overrides the linked application)"),
2466
+ import_cli11.Options.optional
2467
+ );
2468
+ var deployCommand = import_cli11.Command.make(
1389
2469
  "deploy",
1390
- {},
1391
- () => import_effect14.Effect.gen(function* () {
2470
+ { app: appOption5 },
2471
+ ({ app }) => import_effect25.Effect.gen(function* () {
1392
2472
  const projectConfig = yield* ProjectConfigService;
1393
2473
  const deploymentApi = yield* DeploymentApiService;
1394
2474
  const archiveService = yield* ArchiveService;
1395
- const pathService = yield* import_platform5.Path.Path;
2475
+ const projectLink = yield* ProjectLinkService;
2476
+ const pathService = yield* import_platform9.Path.Path;
1396
2477
  const cwd = process.cwd();
1397
- yield* import_effect14.Effect.log("Starting deploy command");
2478
+ const applicationId = yield* import_effect25.Option.match(app, {
2479
+ onSome: (id) => import_effect25.Effect.succeed(id),
2480
+ onNone: () => projectLink.resolve(cwd).pipe(import_effect25.Effect.map((link) => link.applicationId))
2481
+ });
2482
+ yield* import_effect25.Effect.log("Starting deploy command");
1398
2483
  const { config } = yield* projectConfig.resolve(cwd);
1399
- yield* import_effect14.Console.log("Creating deployment...");
1400
- const deploymentId = yield* deploymentApi.createDeployment("test");
1401
- yield* import_effect14.Console.log(`Deployment ID: ${deploymentId}`);
1402
- yield* import_effect14.Console.log("Creating archive...");
1403
- const archivePath = pathService.join(os2.tmpdir(), `project-${Date.now()}.zip`);
2484
+ yield* import_effect25.Console.log("Creating deployment...");
2485
+ const deploymentId = yield* deploymentApi.createDeployment(applicationId);
2486
+ yield* import_effect25.Console.log(`Deployment ID: ${deploymentId}`);
2487
+ yield* import_effect25.Console.log("Creating archive...");
2488
+ const archivePath = pathService.join(os4.tmpdir(), `project-${Date.now()}.zip`);
1404
2489
  const archive = yield* archiveService.createZipArchive(config.userArchive?.rootOverwrite || cwd, archivePath, {
1405
2490
  whitelist: config.userArchive?.fileWhitelist,
1406
2491
  excludeFiles: config.excludeFiles,
1407
2492
  useIgnoreFiles: false,
1408
2493
  useManagedIgnore: false
1409
2494
  });
1410
- yield* import_effect14.Console.log(`Archive created (${(0, import_commons2.formatFileSize)(archive.size)})`);
1411
- yield* import_effect14.Console.log("Uploading archive...");
2495
+ yield* import_effect25.Console.log(`Archive created (${(0, import_commons.formatFileSize)(archive.size)})`);
2496
+ yield* import_effect25.Console.log("Uploading archive...");
1412
2497
  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.");
2498
+ yield* import_effect25.Console.log("Upload complete.");
2499
+ yield* import_effect25.Console.log("The deployment pipeline is now being provisioned. This may take a few seconds.");
1415
2500
  yield* pollDeployment(deploymentId);
1416
2501
  }).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* () {
2502
+ import_effect25.Effect.catchTags({
2503
+ ProjectNotLinkedError: (err) => import_effect25.Console.error(err.message).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2504
+ ProjectLinkReadError: (err) => import_effect25.Console.error(err.message).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2505
+ ConfigNotFoundError: (err) => import_effect25.Console.error(err.message).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2506
+ ConfigParseError: (err) => import_effect25.Console.error(`Config parse error: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2507
+ ConfigValidationError: (err) => import_effect25.Effect.gen(function* () {
1421
2508
  for (const e of err.errors) {
1422
- yield* import_effect14.Console.error(e);
2509
+ yield* import_effect25.Console.error(e);
1423
2510
  }
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)))
2511
+ yield* import_effect25.Console.error(err.message);
2512
+ }).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2513
+ DeploymentCreateError: (err) => import_effect25.Console.error(`Deployment creation failed: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2514
+ ArchiveCreateError: (err) => import_effect25.Console.error(`Archive creation failed: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2515
+ UploadStartError: (err) => import_effect25.Console.error(`Upload failed: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2516
+ UploadPartError: (err) => import_effect25.Console.error(`Upload failed: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2517
+ UploadCompleteError: (err) => import_effect25.Console.error(`Upload failed: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2518
+ PresignedUrlError: (err) => import_effect25.Console.error(`Upload failed: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2519
+ DeploymentAuthError: (err) => import_effect25.Console.error(`Authentication failed: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2520
+ DeploymentFailedError: (err) => import_effect25.Console.error(err.message).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err)))
1434
2521
  })
1435
2522
  )
1436
2523
  );
1437
- var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect14.Effect.gen(function* () {
2524
+ var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect25.Effect.gen(function* () {
1438
2525
  const deploymentApi = yield* DeploymentApiService;
1439
- const fs = yield* import_platform5.FileSystem.FileSystem;
2526
+ const fs = yield* import_platform9.FileSystem.FileSystem;
1440
2527
  const uploadId = yield* deploymentApi.startMultipartUpload(deploymentId);
1441
2528
  const FILE_CHUNK_SIZE = 10 * 1024 * 1024;
1442
2529
  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(
2530
+ yield* import_effect25.Effect.log("Starting multipart upload", { uploadId, numChunks, fileSize });
2531
+ const parts = yield* import_effect25.Effect.all(
1445
2532
  Array.from({ length: numChunks }, (_, i) => {
1446
2533
  const partNumber = i + 1;
1447
2534
  const start = i * FILE_CHUNK_SIZE;
1448
2535
  const bytesToRead = partNumber < numChunks ? FILE_CHUNK_SIZE : fileSize - start;
1449
- return import_effect14.Effect.gen(function* () {
2536
+ return import_effect25.Effect.gen(function* () {
1450
2537
  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) => {
2538
+ const chunkData = yield* import_effect25.Stream.runCollect(fs.stream(archivePath, { offset: start, bytesToRead })).pipe(
2539
+ import_effect25.Effect.map((chunks) => {
1453
2540
  const arrays = Array.from(chunks);
1454
2541
  return Buffer.concat(arrays);
1455
2542
  }),
1456
- import_effect14.Effect.mapError(
2543
+ import_effect25.Effect.mapError(
1457
2544
  (error) => new UploadPartError({
1458
2545
  message: `Failed to read chunk ${partNumber}: ${String(error)}`,
1459
2546
  partNumber
1460
2547
  })
1461
2548
  )
1462
2549
  );
1463
- yield* import_effect14.Effect.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
2550
+ yield* import_effect25.Effect.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
1464
2551
  return yield* deploymentApi.uploadPart(presignedUrl, chunkData, partNumber);
1465
2552
  });
1466
2553
  }),
@@ -1468,20 +2555,20 @@ var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect14.Eff
1468
2555
  );
1469
2556
  yield* deploymentApi.completeUpload(deploymentId, uploadId, parts);
1470
2557
  });
1471
- var pollDeployment = (deploymentId) => import_effect14.Effect.gen(function* () {
2558
+ var pollDeployment = (deploymentId) => import_effect25.Effect.gen(function* () {
1472
2559
  const deploymentApi = yield* DeploymentApiService;
1473
2560
  let lastStatus = "PENDING";
1474
2561
  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)));
2562
+ yield* import_effect25.Console.log("Status: Pending");
2563
+ yield* import_effect25.Effect.repeat(
2564
+ import_effect25.Effect.gen(function* () {
2565
+ const newStatus = yield* deploymentApi.getDeploymentStatus(deploymentId).pipe(import_effect25.Effect.catchTag("DeploymentStatusError", () => import_effect25.Effect.succeed(lastStatus)));
1479
2566
  if (newStatus !== lastStatus) {
1480
2567
  lastStatus = newStatus;
1481
2568
  const label = newStatus.charAt(0) + newStatus.slice(1).toLowerCase();
1482
- yield* import_effect14.Console.log(`Status: ${label}`);
2569
+ yield* import_effect25.Console.log(`Status: ${label}`);
1483
2570
  }
1484
- const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(import_effect14.Effect.catchTag("DeploymentLogsFetchError", () => import_effect14.Effect.succeed(null)));
2571
+ const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(import_effect25.Effect.catchTag("DeploymentLogsFetchError", () => import_effect25.Effect.succeed(null)));
1485
2572
  if (logsPage) {
1486
2573
  for (const logEntry of logsPage.items) {
1487
2574
  logOffset++;
@@ -1489,79 +2576,247 @@ var pollDeployment = (deploymentId) => import_effect14.Effect.gen(function* () {
1489
2576
  }
1490
2577
  }
1491
2578
  if (newStatus === "ACTIVE") {
1492
- yield* import_effect14.Console.log(`Deployed to https://${deploymentId}.gigadrivedev.com`);
1493
- return yield* import_effect14.Effect.fail("done");
2579
+ const hostnames = yield* deploymentApi.getHostnames(deploymentId).pipe(import_effect25.Effect.catchTag("DeploymentHostnamesFetchError", () => import_effect25.Effect.succeed([])));
2580
+ const active = hostnames.find((h) => h.active) ?? hostnames.at(0);
2581
+ if (active) {
2582
+ yield* import_effect25.Console.log(`Deployed to https://${active.hostname}`);
2583
+ } else {
2584
+ yield* import_effect25.Console.log("Deployment is live.");
2585
+ }
2586
+ return yield* import_effect25.Effect.fail("done");
1494
2587
  }
1495
2588
  if (newStatus === "FAILED") {
1496
- return yield* import_effect14.Effect.fail(
2589
+ return yield* import_effect25.Effect.fail(
1497
2590
  new DeploymentFailedError({
1498
2591
  message: "The deployment failed. Please check the logs for more information."
1499
2592
  })
1500
2593
  );
1501
2594
  }
1502
2595
  }),
1503
- import_effect14.Schedule.addDelay(import_effect14.Schedule.forever, () => import_effect14.Duration.seconds(1))
2596
+ import_effect25.Schedule.addDelay(import_effect25.Schedule.forever, () => import_effect25.Duration.seconds(1))
1504
2597
  ).pipe(
1505
- import_effect14.Effect.catchAll((err) => {
1506
- if (err === "done") return import_effect14.Effect.void;
1507
- return import_effect14.Effect.fail(err);
2598
+ import_effect25.Effect.catchAll((err) => {
2599
+ if (err === "done") return import_effect25.Effect.void;
2600
+ return import_effect25.Effect.fail(err);
1508
2601
  })
1509
2602
  );
1510
2603
  });
1511
2604
  var printLog = (logEntry) => {
1512
2605
  switch (logEntry.type) {
1513
2606
  case "ERROR":
1514
- return import_effect14.Console.error(logEntry.message);
2607
+ return import_effect25.Console.error(logEntry.message);
1515
2608
  case "WARN":
1516
- return import_effect14.Console.warn(logEntry.message);
2609
+ return import_effect25.Console.warn(logEntry.message);
1517
2610
  default:
1518
- return import_effect14.Console.log(logEntry.message);
2611
+ return import_effect25.Console.log(logEntry.message);
1519
2612
  }
1520
2613
  };
1521
2614
 
1522
2615
  // 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]));
2616
+ var platformBase = import_cli12.Command.make("platform", {}, () => import_effect26.Effect.void);
2617
+ var platformCommand = platformBase.pipe(import_cli12.Command.withSubcommands([deployCommand]));
2618
+
2619
+ // src/commands/setup/index.ts
2620
+ var import_cli13 = require("@effect/cli");
2621
+ var import_effect27 = require("effect");
2622
+ var fileArg2 = import_cli13.Args.text({ name: "file" }).pipe(
2623
+ import_cli13.Args.withDescription("Output file to write (default: .env.local)"),
2624
+ import_cli13.Args.optional
2625
+ );
2626
+ var appOption6 = import_cli13.Options.text("app").pipe(
2627
+ import_cli13.Options.withAlias("a"),
2628
+ import_cli13.Options.withDescription("Application ID to set up (skips the interactive picker)"),
2629
+ import_cli13.Options.optional
2630
+ );
2631
+ var orgOption6 = import_cli13.Options.text("org").pipe(
2632
+ import_cli13.Options.withAlias("o"),
2633
+ import_cli13.Options.withDescription("Only list applications belonging to this organization ID when picking"),
2634
+ import_cli13.Options.optional
2635
+ );
2636
+ var environmentOption2 = import_cli13.Options.text("environment").pipe(
2637
+ import_cli13.Options.withAlias("e"),
2638
+ import_cli13.Options.withDescription("Environment slug to resolve overrides for (e.g. preview)"),
2639
+ import_cli13.Options.optional
2640
+ );
2641
+ var yesOption2 = import_cli13.Options.boolean("yes").pipe(
2642
+ import_cli13.Options.withAlias("y"),
2643
+ import_cli13.Options.withDescription("Overwrite an existing file without confirmation")
2644
+ );
2645
+ var rotateOption2 = import_cli13.Options.boolean("rotate").pipe(
2646
+ import_cli13.Options.withDescription("Force a fresh API key, revoking the previous one")
2647
+ );
2648
+ var setupCommand = import_cli13.Command.make(
2649
+ "setup",
2650
+ {
2651
+ file: fileArg2,
2652
+ app: appOption6,
2653
+ org: orgOption6,
2654
+ environment: environmentOption2,
2655
+ yes: yesOption2,
2656
+ rotate: rotateOption2
2657
+ },
2658
+ ({ file, app, org, environment, yes, rotate }) => import_effect27.Effect.gen(function* () {
2659
+ const apiClient = yield* ApiClientService;
2660
+ const projectLink = yield* ProjectLinkService;
2661
+ const envFile = yield* EnvFileService;
2662
+ const localCredentials = yield* LocalCredentialsService;
2663
+ const cwd = process.cwd();
2664
+ const existingLink = yield* projectLink.load(cwd);
2665
+ let applicationId;
2666
+ if (import_effect27.Option.isSome(existingLink)) {
2667
+ applicationId = existingLink.value.applicationId;
2668
+ if (import_effect27.Option.isSome(app) || import_effect27.Option.isSome(org)) {
2669
+ yield* import_effect27.Console.log(
2670
+ `Note: this directory is already linked to ${applicationId}; ignoring --app/--org. Run "gigadrive unlink" first to target a different application.`
2671
+ );
2672
+ }
2673
+ } else {
2674
+ const organizationId = import_effect27.Option.getOrUndefined(org);
2675
+ const { items: apps } = yield* apiClient.request((client) => client.applications.list({ organizationId }));
2676
+ if (apps.length === 0) {
2677
+ return yield* import_effect27.Effect.fail(
2678
+ new NoApplicationsFoundError({
2679
+ message: 'No applications found. Create one in the Gigadrive console, then run "gigadrive setup" again.'
2680
+ })
2681
+ );
2682
+ }
2683
+ const explicitAppId = import_effect27.Option.getOrUndefined(app);
2684
+ let chosen;
2685
+ if (explicitAppId !== void 0) {
2686
+ const match = apps.find((a) => a.id === explicitAppId);
2687
+ if (match === void 0) {
2688
+ return yield* import_effect27.Effect.fail(
2689
+ new ApplicationNotFoundError({
2690
+ message: `Application "${explicitAppId}" was not found among your applications.`
2691
+ })
2692
+ );
2693
+ }
2694
+ chosen = match;
2695
+ } else if (apps.length === 1) {
2696
+ chosen = apps[0];
2697
+ } else {
2698
+ chosen = yield* import_cli13.Prompt.run(
2699
+ import_cli13.Prompt.select({
2700
+ message: "Select an application to set up",
2701
+ choices: apps.map((a) => ({ title: `${a.name} (${a.organization.name})`, value: a }))
2702
+ })
2703
+ );
2704
+ }
2705
+ applicationId = chosen.id;
2706
+ yield* projectLink.save(cwd, { applicationId, organizationId: chosen.organization.id });
2707
+ yield* import_effect27.Console.log(`Linked this directory to application ${applicationId}.`);
2708
+ }
2709
+ const target = import_effect27.Option.getOrElse(file, () => ".env.local");
2710
+ const environmentSlug = import_effect27.Option.getOrUndefined(environment);
2711
+ const { items, omittedSensitive } = yield* apiClient.request(
2712
+ (client) => client.applications.envVars.pull(applicationId, environmentSlug ? { environment: environmentSlug } : void 0)
2713
+ );
2714
+ const existing = yield* envFile.read(target);
2715
+ if (import_effect27.Option.isSome(existing) && !yes) {
2716
+ const overwrite = yield* import_cli13.Prompt.run(
2717
+ import_cli13.Prompt.confirm({ message: `${target} already exists. Overwrite?`, initial: false })
2718
+ );
2719
+ if (!overwrite) {
2720
+ return yield* import_effect27.Console.log("Cancelled.");
2721
+ }
2722
+ }
2723
+ const credentialEntries2 = yield* localCredentials.provision({
2724
+ applicationId,
2725
+ rotate,
2726
+ existingContent: import_effect27.Option.getOrUndefined(existing)
2727
+ });
2728
+ const entries = [...items.map((v) => ({ key: v.key, value: v.value })), ...credentialEntries2];
2729
+ yield* envFile.write(target, entries);
2730
+ const gitignored = yield* envFile.ensureGitignored(cwd, target);
2731
+ yield* import_effect27.Console.log("\nLocal dev environment ready.");
2732
+ yield* import_effect27.Console.log(` \u2022 Wrote ${items.length} variable(s) and API credentials to ${target}.`);
2733
+ if (omittedSensitive > 0) {
2734
+ yield* import_effect27.Console.log(
2735
+ ` \u2022 Skipped ${omittedSensitive} sensitive variable(s) (only production/preview hold secrets).`
2736
+ );
2737
+ }
2738
+ if (gitignored) {
2739
+ yield* import_effect27.Console.log(` \u2022 Added ${target} to .gitignore.`);
2740
+ }
2741
+ yield* import_effect27.Console.log(" \u2022 Your app can now call the Gigadrive API via the SDK (GIGADRIVE_CLIENT_ID/SECRET).");
2742
+ }).pipe(
2743
+ import_effect27.Effect.catchTags({
2744
+ // Ctrl+C during a prompt is a deliberate cancel — exit 0.
2745
+ QuitException: () => import_effect27.Console.log("Cancelled."),
2746
+ NotAuthenticatedError: (err) => import_effect27.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
2747
+ import_effect27.Effect.andThen(import_effect27.Effect.fail(err))
2748
+ ),
2749
+ ApiRequestError: (err) => import_effect27.Console.error(`Setup failed: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
2750
+ NoApplicationsFoundError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
2751
+ ApplicationNotFoundError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
2752
+ ProjectLinkReadError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
2753
+ ProjectLinkWriteError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
2754
+ EnvFileReadError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
2755
+ EnvFileWriteError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
2756
+ DevCredentialsStoreReadError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
2757
+ DevCredentialsStoreWriteError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err)))
2758
+ })
2759
+ )
2760
+ );
1525
2761
 
1526
2762
  // 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(
2763
+ var import_cli14 = require("@effect/cli");
2764
+ var import_effect28 = require("effect");
2765
+ var whoamiCommand = import_cli14.Command.make(
1530
2766
  "whoami",
1531
2767
  {},
1532
- () => import_effect16.Effect.gen(function* () {
2768
+ () => import_effect28.Effect.gen(function* () {
1533
2769
  const auth = yield* AuthService;
1534
2770
  const info = yield* auth.getUserInfo;
1535
- yield* import_effect16.Console.log(JSON.stringify(info, null, 2));
2771
+ yield* import_effect28.Console.log(JSON.stringify(info, null, 2));
1536
2772
  }).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}`)
2773
+ import_effect28.Effect.catchTags({
2774
+ NotAuthenticatedError: () => import_effect28.Console.log('You are not logged in. Run "gigadrive login" to authenticate.'),
2775
+ UserInfoFetchError: (err) => import_effect28.Console.error(`Failed to retrieve user info: ${err.message}`),
2776
+ OAuthDiscoveryError: (err) => import_effect28.Console.error(`Failed to retrieve user info: ${err.message}`)
1541
2777
  })
1542
2778
  )
1543
2779
  );
1544
2780
 
1545
2781
  // 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])
2782
+ var gigadrive = import_cli15.Command.make("gigadrive", {}, () => import_effect29.Effect.void).pipe(
2783
+ import_cli15.Command.withSubcommands([
2784
+ loginCommand,
2785
+ logoutCommand,
2786
+ whoamiCommand,
2787
+ linkCommand,
2788
+ unlinkCommand,
2789
+ appsCommand,
2790
+ envCommand,
2791
+ deploymentsCommand,
2792
+ aiCommand,
2793
+ buildCommand,
2794
+ debugCommand,
2795
+ platformCommand,
2796
+ setupCommand
2797
+ ])
1548
2798
  );
1549
- var cli = import_cli8.Command.run(gigadrive, {
2799
+ var cli = import_cli15.Command.run(gigadrive, {
1550
2800
  name: "Gigadrive CLI",
1551
- version: "2.0.0"
2801
+ version: package_default.version
1552
2802
  });
1553
- var BaseServices = import_effect17.Layer.mergeAll(
2803
+ var BaseServices = import_effect29.Layer.mergeAll(
1554
2804
  OAuthConfigService.Default,
1555
2805
  AuthStorageService.Default,
1556
2806
  PackageManagerService.Default,
1557
2807
  ArchiveService.Default,
1558
2808
  import_network_config2.NetworkConfigLive,
1559
- ProjectConfigService.Default
1560
- ).pipe(import_effect17.Layer.provideMerge(AuthService.Default));
1561
- var DeploymentApiLive = import_effect17.Layer.provide(DeploymentApiService.Default, BaseServices);
1562
- var ServicesLive = import_effect17.Layer.mergeAll(BaseServices, DeploymentApiLive);
1563
- var AppLive = import_effect17.Layer.mergeAll(ServicesLive, import_effect17.Logger.minimumLogLevel(import_effect17.LogLevel.Info)).pipe(
1564
- import_effect17.Layer.provideMerge(import_platform_node.NodeContext.layer)
1565
- );
1566
- import_platform_node.NodeRuntime.runMain(import_effect17.Effect.provide(cli(process.argv), AppLive));
2809
+ ProjectConfigService.Default,
2810
+ ProjectLinkService.Default,
2811
+ EnvFileService.Default,
2812
+ DevCredentialsStore.Default
2813
+ ).pipe(import_effect29.Layer.provideMerge(AuthService.Default));
2814
+ var ApiClientLive = import_effect29.Layer.provide(ApiClientService.Default, BaseServices);
2815
+ var DeploymentApiLive = import_effect29.Layer.provide(DeploymentApiService.Default, BaseServices);
2816
+ var LocalCredentialsLive = import_effect29.Layer.provide(LocalCredentialsService.Default, BaseServices);
2817
+ var ServicesLive = import_effect29.Layer.mergeAll(BaseServices, ApiClientLive, DeploymentApiLive, LocalCredentialsLive);
2818
+ var AppLive = import_effect29.Layer.mergeAll(ServicesLive, import_effect29.Logger.minimumLogLevel(import_effect29.LogLevel.Info)).pipe(
2819
+ import_effect29.Layer.provideMerge(import_platform_node.NodeContext.layer)
2820
+ );
2821
+ import_platform_node.NodeRuntime.runMain(import_effect29.Effect.provide(cli(process.argv), AppLive));
1567
2822
  //# sourceMappingURL=index.cjs.map