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 +2118 -863
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2179 -924
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
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
|
|
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
|
|
30
|
+
var import_effect29 = require("effect");
|
|
31
31
|
|
|
32
|
-
//
|
|
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
|
|
35
|
-
var
|
|
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/
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
new
|
|
197
|
-
message: "
|
|
198
|
-
|
|
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
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
|
|
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/
|
|
339
|
-
var import_network_config = require("@gigadrive/network-config");
|
|
431
|
+
// src/services/oauth-config.ts
|
|
340
432
|
var import_effect4 = require("effect");
|
|
341
|
-
var
|
|
342
|
-
import_effect4.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
)
|
|
351
|
-
|
|
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 =
|
|
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*
|
|
601
|
-
const response = yield*
|
|
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*
|
|
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*
|
|
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*
|
|
622
|
-
|
|
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*
|
|
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*
|
|
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
|
|
544
|
+
var AuthService = class extends import_effect5.Effect.Service()("AuthService", {
|
|
662
545
|
accessors: true,
|
|
663
546
|
dependencies: [OAuthConfigService.Default, AuthStorageService.Default],
|
|
664
|
-
effect:
|
|
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 =
|
|
552
|
+
const logout = import_effect5.Effect.gen(function* () {
|
|
670
553
|
accessToken = null;
|
|
671
554
|
tokenExpirationTime = 0;
|
|
672
555
|
yield* storage.remove;
|
|
673
|
-
yield*
|
|
556
|
+
yield* import_effect5.Effect.log("Logged out");
|
|
674
557
|
});
|
|
675
|
-
const exchangeCodeForTokens = (config, authCode, redirectUri, codeVerifier) =>
|
|
676
|
-
const response = yield*
|
|
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*
|
|
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*
|
|
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*
|
|
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) =>
|
|
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(
|
|
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
|
-
|
|
614
|
+
import_effect5.Console.log(`Please open this URL in your browser to log in:
|
|
732
615
|
${authUrl.toString()}
|
|
733
|
-
`).pipe(
|
|
734
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
656
|
+
resume(import_effect5.Effect.fail(new LoginFlowError({ message: `Local server error: ${err.message}` })));
|
|
774
657
|
});
|
|
775
658
|
});
|
|
776
659
|
});
|
|
777
|
-
const login =
|
|
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*
|
|
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*
|
|
679
|
+
yield* import_effect5.Effect.log("Login successful");
|
|
797
680
|
return true;
|
|
798
681
|
});
|
|
799
|
-
const refreshAccessToken =
|
|
682
|
+
const refreshAccessToken = import_effect5.Effect.gen(function* () {
|
|
800
683
|
const stored = yield* storage.load.pipe(
|
|
801
|
-
|
|
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
|
-
|
|
687
|
+
import_effect5.Effect.catchTag("OAuthDiscoveryError", (err) => import_effect5.Effect.fail(new TokenRefreshError({ message: err.message })))
|
|
805
688
|
);
|
|
806
|
-
const authData = yield*
|
|
807
|
-
onNone: () =>
|
|
808
|
-
onSome: (data) => data.refreshToken ?
|
|
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*
|
|
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(
|
|
827
|
-
return yield*
|
|
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*
|
|
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*
|
|
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*
|
|
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(
|
|
857
|
-
yield*
|
|
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 =
|
|
861
|
-
if (accessToken && Date.now() < tokenExpirationTime) {
|
|
862
|
-
return accessToken;
|
|
863
|
-
}
|
|
864
|
-
const stored = yield* storage.load.pipe(
|
|
865
|
-
|
|
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
|
-
|
|
868
|
-
|
|
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
|
-
|
|
881
|
-
|
|
882
|
-
|
|
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
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
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
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
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
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
}
|
|
942
|
-
|
|
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
|
|
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
|
-
() =>
|
|
952
|
-
yield*
|
|
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(
|
|
2158
|
+
const info = yield* auth.getUserInfo.pipe(import_effect21.Effect.catchAll(() => import_effect21.Effect.succeed({})));
|
|
956
2159
|
const name = auth.inferUserName(info);
|
|
957
|
-
yield*
|
|
2160
|
+
yield* import_effect21.Console.log(`You are now logged in as ${name}.`);
|
|
958
2161
|
}).pipe(
|
|
959
|
-
|
|
960
|
-
LoginFlowError: (err) =>
|
|
961
|
-
TokenExchangeError: (err) =>
|
|
962
|
-
OAuthDiscoveryError: (err) =>
|
|
963
|
-
AuthStorageWriteError: (err) =>
|
|
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
|
|
970
|
-
var
|
|
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
|
|
974
|
-
var
|
|
975
|
-
var
|
|
976
|
-
var
|
|
977
|
-
var
|
|
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
|
|
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
|
|
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
|
-
|
|
990
|
-
|
|
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) =>
|
|
993
|
-
const items = yield* fs.readDirectory(dir).pipe(
|
|
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*
|
|
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(
|
|
1018
|
-
var initializeIgnoreRules = (fs, pathService, baseDir) =>
|
|
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) =>
|
|
2253
|
+
var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect23.Effect.gen(function* () {
|
|
1034
2254
|
const filesToInclude = [];
|
|
1035
|
-
const walkDir = (currentDir) =>
|
|
1036
|
-
const items = yield* fs.readDirectory(currentDir).pipe(
|
|
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*
|
|
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(
|
|
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
|
|
2278
|
+
var ArchiveService = class extends import_effect23.Effect.Service()("ArchiveService", {
|
|
1059
2279
|
accessors: true,
|
|
1060
|
-
effect:
|
|
1061
|
-
const fs = yield*
|
|
1062
|
-
const pathService = yield*
|
|
1063
|
-
const createZipArchive =
|
|
1064
|
-
yield*
|
|
1065
|
-
yield*
|
|
1066
|
-
yield*
|
|
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*
|
|
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*
|
|
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
|
-
|
|
2301
|
+
import_effect23.Effect.map((files) => [files])
|
|
1082
2302
|
);
|
|
1083
2303
|
});
|
|
1084
|
-
const archiveExists = yield* fs.exists(outputFile).pipe(
|
|
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
|
-
|
|
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*
|
|
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
|
-
|
|
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*
|
|
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
|
|
1130
|
-
var
|
|
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
|
-
|
|
1154
|
-
|
|
1155
|
-
const
|
|
1156
|
-
const
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
yield*
|
|
1164
|
-
|
|
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
|
-
|
|
1177
|
-
|
|
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 =
|
|
1202
|
-
yield*
|
|
1203
|
-
const
|
|
1204
|
-
const
|
|
1205
|
-
try: () =>
|
|
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
|
-
|
|
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 =
|
|
1232
|
-
const
|
|
1233
|
-
const
|
|
1234
|
-
try: () =>
|
|
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
|
-
|
|
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 =
|
|
1265
|
-
yield*
|
|
1266
|
-
yield*
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
|
|
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
|
-
|
|
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 =
|
|
1298
|
-
yield*
|
|
1299
|
-
yield*
|
|
1300
|
-
const
|
|
1301
|
-
|
|
1302
|
-
try: () =>
|
|
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
|
-
|
|
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 =
|
|
1321
|
-
const
|
|
1322
|
-
const
|
|
1323
|
-
try: () =>
|
|
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
|
-
|
|
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 =
|
|
1348
|
-
const
|
|
1349
|
-
const
|
|
1350
|
-
try: () =>
|
|
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
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
() =>
|
|
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
|
|
2475
|
+
const projectLink = yield* ProjectLinkService;
|
|
2476
|
+
const pathService = yield* import_platform9.Path.Path;
|
|
1396
2477
|
const cwd = process.cwd();
|
|
1397
|
-
yield*
|
|
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*
|
|
1400
|
-
const deploymentId = yield* deploymentApi.createDeployment(
|
|
1401
|
-
yield*
|
|
1402
|
-
yield*
|
|
1403
|
-
const archivePath = pathService.join(
|
|
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*
|
|
1411
|
-
yield*
|
|
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*
|
|
1414
|
-
yield*
|
|
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
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
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*
|
|
2509
|
+
yield* import_effect25.Console.error(e);
|
|
1423
2510
|
}
|
|
1424
|
-
yield*
|
|
1425
|
-
}).pipe(
|
|
1426
|
-
DeploymentCreateError: (err) =>
|
|
1427
|
-
ArchiveCreateError: (err) =>
|
|
1428
|
-
UploadStartError: (err) =>
|
|
1429
|
-
UploadPartError: (err) =>
|
|
1430
|
-
UploadCompleteError: (err) =>
|
|
1431
|
-
PresignedUrlError: (err) =>
|
|
1432
|
-
DeploymentAuthError: (err) =>
|
|
1433
|
-
DeploymentFailedError: (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) =>
|
|
2524
|
+
var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect25.Effect.gen(function* () {
|
|
1438
2525
|
const deploymentApi = yield* DeploymentApiService;
|
|
1439
|
-
const fs = yield*
|
|
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*
|
|
1444
|
-
const parts = yield*
|
|
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
|
|
2536
|
+
return import_effect25.Effect.gen(function* () {
|
|
1450
2537
|
const presignedUrl = yield* deploymentApi.getPresignedUrl(deploymentId, uploadId, partNumber);
|
|
1451
|
-
const chunkData = yield*
|
|
1452
|
-
|
|
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
|
-
|
|
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*
|
|
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) =>
|
|
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*
|
|
1476
|
-
yield*
|
|
1477
|
-
|
|
1478
|
-
const newStatus = yield* deploymentApi.getDeploymentStatus(deploymentId).pipe(
|
|
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*
|
|
2569
|
+
yield* import_effect25.Console.log(`Status: ${label}`);
|
|
1483
2570
|
}
|
|
1484
|
-
const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(
|
|
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*
|
|
1493
|
-
|
|
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*
|
|
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
|
-
|
|
2596
|
+
import_effect25.Schedule.addDelay(import_effect25.Schedule.forever, () => import_effect25.Duration.seconds(1))
|
|
1504
2597
|
).pipe(
|
|
1505
|
-
|
|
1506
|
-
if (err === "done") return
|
|
1507
|
-
return
|
|
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
|
|
2607
|
+
return import_effect25.Console.error(logEntry.message);
|
|
1515
2608
|
case "WARN":
|
|
1516
|
-
return
|
|
2609
|
+
return import_effect25.Console.warn(logEntry.message);
|
|
1517
2610
|
default:
|
|
1518
|
-
return
|
|
2611
|
+
return import_effect25.Console.log(logEntry.message);
|
|
1519
2612
|
}
|
|
1520
2613
|
};
|
|
1521
2614
|
|
|
1522
2615
|
// src/commands/platform/index.ts
|
|
1523
|
-
var platformBase =
|
|
1524
|
-
var platformCommand = platformBase.pipe(
|
|
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
|
|
1528
|
-
var
|
|
1529
|
-
var whoamiCommand =
|
|
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
|
-
() =>
|
|
2768
|
+
() => import_effect28.Effect.gen(function* () {
|
|
1533
2769
|
const auth = yield* AuthService;
|
|
1534
2770
|
const info = yield* auth.getUserInfo;
|
|
1535
|
-
yield*
|
|
2771
|
+
yield* import_effect28.Console.log(JSON.stringify(info, null, 2));
|
|
1536
2772
|
}).pipe(
|
|
1537
|
-
|
|
1538
|
-
NotAuthenticatedError: () =>
|
|
1539
|
-
UserInfoFetchError: (err) =>
|
|
1540
|
-
OAuthDiscoveryError: (err) =>
|
|
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 =
|
|
1547
|
-
|
|
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 =
|
|
2799
|
+
var cli = import_cli15.Command.run(gigadrive, {
|
|
1550
2800
|
name: "Gigadrive CLI",
|
|
1551
|
-
version:
|
|
2801
|
+
version: package_default.version
|
|
1552
2802
|
});
|
|
1553
|
-
var BaseServices =
|
|
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
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
);
|
|
1566
|
-
|
|
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
|