gigadrive 2.4.4 → 2.5.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.mjs CHANGED
@@ -9,7 +9,7 @@ import { Effect as Effect27, Layer, LogLevel, Logger } from "effect";
9
9
  // package.json
10
10
  var package_default = {
11
11
  name: "gigadrive",
12
- version: "2.4.4",
12
+ version: "2.5.0",
13
13
  repository: {
14
14
  type: "git",
15
15
  url: "https://github.com/Gigadrive/sdk.git",
@@ -154,6 +154,13 @@ var ApplicationNotFoundError = class extends Schema.TaggedError()(
154
154
  }
155
155
  ) {
156
156
  };
157
+ var NoOrganizationsFoundError = class extends Schema.TaggedError()(
158
+ "NoOrganizationsFoundError",
159
+ {
160
+ message: Schema.String
161
+ }
162
+ ) {
163
+ };
157
164
  var EnvFileReadError = class extends Schema.TaggedError()("EnvFileReadError", {
158
165
  message: Schema.String,
159
166
  cause: Schema.optional(Schema.String)
@@ -490,8 +497,11 @@ var CLI_SCOPES = [
490
497
  "openid",
491
498
  "profile",
492
499
  "email",
500
+ // Platform discovery used for zero-config application onboarding
501
+ "platform:organizations:read",
493
502
  // Network API capabilities
494
503
  "network:applications:read",
504
+ "network:applications:write",
495
505
  "network:env_vars:read",
496
506
  "network:env_vars:write",
497
507
  "network:env_vars:delete",
@@ -1083,13 +1093,13 @@ var readStdin = Effect6.async((resume) => {
1083
1093
  stdin.resume();
1084
1094
  });
1085
1095
  var readJsonInput = (file) => Effect6.gen(function* () {
1086
- const path = Option4.getOrUndefined(file);
1096
+ const path2 = Option4.getOrUndefined(file);
1087
1097
  let raw;
1088
- if (path === void 0 || path === "-") {
1098
+ if (path2 === void 0 || path2 === "-") {
1089
1099
  raw = yield* readStdin;
1090
1100
  } else {
1091
1101
  const fs = yield* FileSystem3.FileSystem;
1092
- raw = yield* fs.readFileString(path, "utf8").pipe(Effect6.mapError((e) => new ApiRequestError({ message: `Failed to read ${path}: ${String(e)}` })));
1102
+ raw = yield* fs.readFileString(path2, "utf8").pipe(Effect6.mapError((e) => new ApiRequestError({ message: `Failed to read ${path2}: ${String(e)}` })));
1093
1103
  }
1094
1104
  return yield* Effect6.try({
1095
1105
  try: () => JSON.parse(raw),
@@ -1309,14 +1319,30 @@ var aiCommand = aiBase.pipe(
1309
1319
  // src/commands/apps/index.ts
1310
1320
  import { Command as Command2, Options as Options2 } from "@effect/cli";
1311
1321
  import { Console as Console3, Effect as Effect7, Option as Option5 } from "effect";
1312
- var orgOption2 = Options2.text("org").pipe(
1322
+ var listOrgOption = Options2.text("org").pipe(
1313
1323
  Options2.withAlias("o"),
1314
1324
  Options2.withDescription("Only list applications belonging to this organization ID"),
1315
1325
  Options2.optional
1316
1326
  );
1327
+ var createOrgOption = Options2.text("org").pipe(
1328
+ Options2.withAlias("o"),
1329
+ Options2.withDescription("Organization ID that will own the application")
1330
+ );
1331
+ var nameOption = Options2.text("name").pipe(
1332
+ Options2.withAlias("n"),
1333
+ Options2.withDescription("Application display name")
1334
+ );
1335
+ var slugOption = Options2.text("slug").pipe(
1336
+ Options2.withDescription("Optional lowercase application slug"),
1337
+ Options2.optional
1338
+ );
1339
+ var rootDirectoryOption = Options2.text("root-directory").pipe(
1340
+ Options2.withDescription("Optional project directory relative to the repository root"),
1341
+ Options2.optional
1342
+ );
1317
1343
  var appsListCommand = Command2.make(
1318
1344
  "list",
1319
- { org: orgOption2 },
1345
+ { org: listOrgOption },
1320
1346
  ({ org }) => Effect7.gen(function* () {
1321
1347
  const apiClient = yield* ApiClientService;
1322
1348
  const organizationId = Option5.getOrUndefined(org);
@@ -1339,8 +1365,35 @@ ${total} application(s).`);
1339
1365
  })
1340
1366
  )
1341
1367
  );
1368
+ var appsCreateCommand = Command2.make(
1369
+ "create",
1370
+ { org: createOrgOption, name: nameOption, slug: slugOption, rootDirectory: rootDirectoryOption },
1371
+ ({ org, name, slug, rootDirectory }) => Effect7.gen(function* () {
1372
+ const apiClient = yield* ApiClientService;
1373
+ const application = yield* apiClient.request(
1374
+ (client) => client.applications.create({
1375
+ organizationId: org,
1376
+ name,
1377
+ ...Option5.match(slug, { onNone: () => ({}), onSome: (value) => ({ slug: value }) }),
1378
+ ...Option5.match(rootDirectory, {
1379
+ onNone: () => ({}),
1380
+ onSome: (value) => ({ rootDirectory: value })
1381
+ })
1382
+ })
1383
+ );
1384
+ yield* Console3.log(`Created application ${application.name} (${application.id}).`);
1385
+ yield* Console3.log(`Run "gigadrive link --app ${application.id}" to link the current directory.`);
1386
+ }).pipe(
1387
+ Effect7.catchTags({
1388
+ NotAuthenticatedError: (err) => Console3.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1389
+ Effect7.andThen(Effect7.fail(err))
1390
+ ),
1391
+ ApiRequestError: (err) => Console3.error(`Failed to create application: ${err.message}`).pipe(Effect7.andThen(Effect7.fail(err)))
1392
+ })
1393
+ )
1394
+ );
1342
1395
  var appsBase = Command2.make("apps", {}, () => Effect7.void);
1343
- var appsCommand = appsBase.pipe(Command2.withSubcommands([appsListCommand]));
1396
+ var appsCommand = appsBase.pipe(Command2.withSubcommands([appsListCommand, appsCreateCommand]));
1344
1397
 
1345
1398
  // src/commands/build/index.ts
1346
1399
  import { Command as Command3 } from "@effect/cli";
@@ -1636,7 +1689,7 @@ var appOption2 = Options3.text("app").pipe(
1636
1689
  Options3.withDescription("Filter by application ID (defaults to the linked application)"),
1637
1690
  Options3.optional
1638
1691
  );
1639
- var orgOption3 = Options3.text("org").pipe(
1692
+ var orgOption2 = Options3.text("org").pipe(
1640
1693
  Options3.withAlias("o"),
1641
1694
  Options3.withDescription("Filter by organization ID"),
1642
1695
  Options3.optional
@@ -1656,7 +1709,7 @@ var resolveApplicationFilter = (app) => Option6.match(app, {
1656
1709
  });
1657
1710
  var deploymentsListCommand = Command6.make(
1658
1711
  "list",
1659
- { app: appOption2, org: orgOption3, status: statusOption },
1712
+ { app: appOption2, org: orgOption2, status: statusOption },
1660
1713
  ({ app, org, status }) => Effect13.gen(function* () {
1661
1714
  const apiClient = yield* ApiClientService;
1662
1715
  const applicationId = yield* resolveApplicationFilter(app);
@@ -1950,7 +2003,7 @@ var appOption3 = Options4.text("app").pipe(
1950
2003
  Options4.withDescription("Operate on this application (overrides the linked application)"),
1951
2004
  Options4.optional
1952
2005
  );
1953
- var orgOption4 = Options4.text("org").pipe(
2006
+ var orgOption3 = Options4.text("org").pipe(
1954
2007
  Options4.withAlias("o"),
1955
2008
  Options4.withDescription("Operate on this organization instead of an application"),
1956
2009
  Options4.optional
@@ -1999,7 +2052,7 @@ var resolveScope = (app, org) => Effect17.gen(function* () {
1999
2052
  });
2000
2053
  var envListCommand = Command7.make(
2001
2054
  "list",
2002
- { app: appOption3, org: orgOption4 },
2055
+ { app: appOption3, org: orgOption3 },
2003
2056
  ({ app, org }) => Effect17.gen(function* () {
2004
2057
  const apiClient = yield* ApiClientService;
2005
2058
  const scope = yield* resolveScope(app, org);
@@ -2026,7 +2079,7 @@ var envListCommand = Command7.make(
2026
2079
  );
2027
2080
  var envSetCommand = Command7.make(
2028
2081
  "set",
2029
- { keyValue: keyValueArg, app: appOption3, org: orgOption4, sensitive: sensitiveOption, env: envIdOption },
2082
+ { keyValue: keyValueArg, app: appOption3, org: orgOption3, sensitive: sensitiveOption, env: envIdOption },
2030
2083
  ({ keyValue, app, org, sensitive, env }) => Effect17.gen(function* () {
2031
2084
  const separator = keyValue.indexOf("=");
2032
2085
  if (separator <= 0) {
@@ -2059,7 +2112,7 @@ var envSetCommand = Command7.make(
2059
2112
  );
2060
2113
  var envRmCommand = Command7.make(
2061
2114
  "rm",
2062
- { keyOrId: keyOrIdArg, app: appOption3, org: orgOption4 },
2115
+ { keyOrId: keyOrIdArg, app: appOption3, org: orgOption3 },
2063
2116
  ({ keyOrId, app, org }) => Effect17.gen(function* () {
2064
2117
  const apiClient = yield* ApiClientService;
2065
2118
  const scope = yield* resolveScope(app, org);
@@ -2173,14 +2226,14 @@ var appOption4 = Options5.text("app").pipe(
2173
2226
  Options5.withDescription("Application ID to link (skips the interactive picker)"),
2174
2227
  Options5.optional
2175
2228
  );
2176
- var orgOption5 = Options5.text("org").pipe(
2229
+ var orgOption4 = Options5.text("org").pipe(
2177
2230
  Options5.withAlias("o"),
2178
2231
  Options5.withDescription("Only list applications belonging to this organization ID"),
2179
2232
  Options5.optional
2180
2233
  );
2181
2234
  var linkCommand = Command8.make(
2182
2235
  "link",
2183
- { app: appOption4, org: orgOption5 },
2236
+ { app: appOption4, org: orgOption4 },
2184
2237
  ({ app, org }) => Effect18.gen(function* () {
2185
2238
  const apiClient = yield* ApiClientService;
2186
2239
  const projectLink = yield* ProjectLinkService;
@@ -2300,11 +2353,12 @@ import { Command as Command12 } from "@effect/cli";
2300
2353
  import { Effect as Effect24 } from "effect";
2301
2354
 
2302
2355
  // src/commands/platform/deploy/index.ts
2303
- import { Command as Command11, Options as Options6 } from "@effect/cli";
2356
+ import { Command as Command11, Options as Options6, Prompt as Prompt3 } from "@effect/cli";
2304
2357
  import { FileSystem as FileSystem9, Path as Path8 } from "@effect/platform";
2305
2358
  import { formatFileSize } from "@gigadrive/commons";
2306
2359
  import { Console as Console11, Duration as Duration2, Effect as Effect23, Option as Option12, Schedule, Stream } from "effect";
2307
2360
  import * as os4 from "os";
2361
+ import * as path from "path";
2308
2362
 
2309
2363
  // src/services/archive.ts
2310
2364
  import { FileSystem as FileSystem8, Path as Path7 } from "@effect/platform";
@@ -2575,22 +2629,83 @@ var appOption5 = Options6.text("app").pipe(
2575
2629
  Options6.withDescription("Application ID to deploy to (overrides the linked application)"),
2576
2630
  Options6.optional
2577
2631
  );
2632
+ var orgOption5 = Options6.text("org").pipe(
2633
+ Options6.withAlias("o"),
2634
+ Options6.withDescription("Organization ID to use when automatically creating an application"),
2635
+ Options6.optional
2636
+ );
2637
+ var nameOption2 = Options6.text("name").pipe(
2638
+ Options6.withAlias("n"),
2639
+ Options6.withDescription("Application name to use when automatically creating an application"),
2640
+ Options6.optional
2641
+ );
2642
+ var createAndLinkDeploymentApplication = (input) => Effect23.gen(function* () {
2643
+ const projectLink = yield* ProjectLinkService;
2644
+ const apiClient = yield* ApiClientService;
2645
+ const { items: organizations } = yield* apiClient.request((client) => client.organizations.list());
2646
+ const availableOrganizations = Option12.match(input.org, {
2647
+ onNone: () => organizations,
2648
+ onSome: (organizationId) => organizations.filter((organization2) => organization2.id === organizationId)
2649
+ });
2650
+ if (availableOrganizations.length === 0) {
2651
+ return yield* new NoOrganizationsFoundError({
2652
+ message: Option12.match(input.org, {
2653
+ onNone: () => 'No organization is available for this deployment. Create an organization, then run "gigadrive deploy" again.',
2654
+ onSome: (organizationId) => `Organization "${organizationId}" was not found among your organizations.`
2655
+ })
2656
+ });
2657
+ }
2658
+ const organization = availableOrganizations.length === 1 ? availableOrganizations[0] : yield* Prompt3.run(
2659
+ Prompt3.select({
2660
+ message: "Select an organization for this application",
2661
+ choices: availableOrganizations.map((candidate) => ({
2662
+ title: candidate.name,
2663
+ value: candidate
2664
+ }))
2665
+ })
2666
+ );
2667
+ const inferredName = path.basename(input.cwd) || "Application";
2668
+ const applicationName = Option12.match(input.name, {
2669
+ onNone: () => inferredName,
2670
+ onSome: (name) => name
2671
+ }).trim();
2672
+ const application = yield* apiClient.request(
2673
+ (client) => client.applications.create({
2674
+ organizationId: organization.id,
2675
+ name: applicationName || inferredName
2676
+ })
2677
+ );
2678
+ yield* projectLink.save(input.cwd, {
2679
+ applicationId: application.id,
2680
+ organizationId: organization.id
2681
+ });
2682
+ yield* Console11.log(`Created and linked application ${application.name} (${application.id}).`);
2683
+ return application.id;
2684
+ });
2685
+ var resolveLinkedDeploymentApplication = (input) => Effect23.gen(function* () {
2686
+ const projectLink = yield* ProjectLinkService;
2687
+ const existingLink = yield* projectLink.load(input.cwd);
2688
+ return yield* Option12.match(existingLink, {
2689
+ onNone: () => createAndLinkDeploymentApplication(input),
2690
+ onSome: (link) => Effect23.succeed(link.applicationId)
2691
+ });
2692
+ });
2693
+ var resolveDeploymentApplication = (input) => Option12.match(input.app, {
2694
+ onNone: () => resolveLinkedDeploymentApplication(input),
2695
+ onSome: Effect23.succeed
2696
+ });
2578
2697
  var deployCommand = Command11.make(
2579
2698
  "deploy",
2580
- { app: appOption5 },
2581
- ({ app }) => Effect23.gen(function* () {
2699
+ { app: appOption5, org: orgOption5, name: nameOption2 },
2700
+ ({ app, org, name }) => Effect23.gen(function* () {
2582
2701
  const projectConfig = yield* ProjectConfigService;
2583
2702
  const deploymentApi = yield* DeploymentApiService;
2584
2703
  const archiveService = yield* ArchiveService;
2585
- const projectLink = yield* ProjectLinkService;
2586
2704
  const pathService = yield* Path8.Path;
2587
2705
  const cwd = process.cwd();
2588
- const applicationId = yield* Option12.match(app, {
2589
- onSome: (id) => Effect23.succeed(id),
2590
- onNone: () => projectLink.resolve(cwd).pipe(Effect23.map((link) => link.applicationId))
2591
- });
2592
2706
  yield* Effect23.log("Starting deploy command");
2593
2707
  const { config } = yield* projectConfig.resolve(cwd);
2708
+ const applicationId = yield* resolveDeploymentApplication({ app, org, name, cwd });
2594
2709
  yield* Console11.log("Creating deployment...");
2595
2710
  const deploymentId = yield* deploymentApi.createDeployment(applicationId);
2596
2711
  yield* Console11.log(`Deployment ID: ${deploymentId}`);
@@ -2610,8 +2725,14 @@ var deployCommand = Command11.make(
2610
2725
  yield* pollDeployment(deploymentId);
2611
2726
  }).pipe(
2612
2727
  Effect23.catchTags({
2613
- ProjectNotLinkedError: (err) => Console11.error(err.message).pipe(Effect23.andThen(Effect23.fail(err))),
2728
+ QuitException: () => Console11.log("Cancelled."),
2729
+ NotAuthenticatedError: (err) => Console11.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
2730
+ Effect23.andThen(Effect23.fail(err))
2731
+ ),
2732
+ ApiRequestError: (err) => Console11.error(`Application setup failed: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
2733
+ NoOrganizationsFoundError: (err) => Console11.error(err.message).pipe(Effect23.andThen(Effect23.fail(err))),
2614
2734
  ProjectLinkReadError: (err) => Console11.error(err.message).pipe(Effect23.andThen(Effect23.fail(err))),
2735
+ ProjectLinkWriteError: (err) => Console11.error(err.message).pipe(Effect23.andThen(Effect23.fail(err))),
2615
2736
  ConfigNotFoundError: (err) => Console11.error(err.message).pipe(Effect23.andThen(Effect23.fail(err))),
2616
2737
  ConfigParseError: (err) => Console11.error(`Config parse error: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
2617
2738
  ConfigValidationError: (err) => Effect23.gen(function* () {
@@ -2727,7 +2848,7 @@ var platformBase = Command12.make("platform", {}, () => Effect24.void);
2727
2848
  var platformCommand = platformBase.pipe(Command12.withSubcommands([deployCommand]));
2728
2849
 
2729
2850
  // src/commands/setup/index.ts
2730
- import { Args as Args4, Command as Command13, Options as Options7, Prompt as Prompt3 } from "@effect/cli";
2851
+ import { Args as Args4, Command as Command13, Options as Options7, Prompt as Prompt4 } from "@effect/cli";
2731
2852
  import { Console as Console12, Effect as Effect25, Option as Option13 } from "effect";
2732
2853
  var fileArg2 = Args4.text({ name: "file" }).pipe(
2733
2854
  Args4.withDescription("Output file to write (default: .env.local)"),
@@ -2805,8 +2926,8 @@ var setupCommand = Command13.make(
2805
2926
  } else if (apps.length === 1) {
2806
2927
  chosen = apps[0];
2807
2928
  } else {
2808
- chosen = yield* Prompt3.run(
2809
- Prompt3.select({
2929
+ chosen = yield* Prompt4.run(
2930
+ Prompt4.select({
2810
2931
  message: "Select an application to set up",
2811
2932
  choices: apps.map((a) => ({ title: `${a.name} (${a.organization.name})`, value: a }))
2812
2933
  })
@@ -2823,8 +2944,8 @@ var setupCommand = Command13.make(
2823
2944
  );
2824
2945
  const existing = yield* envFile.read(target);
2825
2946
  if (Option13.isSome(existing) && !yes) {
2826
- const overwrite = yield* Prompt3.run(
2827
- Prompt3.confirm({ message: `${target} already exists. Overwrite?`, initial: false })
2947
+ const overwrite = yield* Prompt4.run(
2948
+ Prompt4.confirm({ message: `${target} already exists. Overwrite?`, initial: false })
2828
2949
  );
2829
2950
  if (!overwrite) {
2830
2951
  return yield* Console12.log("Cancelled.");
@@ -2902,6 +3023,7 @@ var gigadrive = Command15.make("gigadrive", {}, () => Effect27.void).pipe(
2902
3023
  aiCommand,
2903
3024
  buildCommand,
2904
3025
  debugCommand,
3026
+ deployCommand,
2905
3027
  platformCommand,
2906
3028
  setupCommand
2907
3029
  ])