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/README.md CHANGED
@@ -1 +1,23 @@
1
- # @gigadrive/network-cli
1
+ # Gigadrive CLI
2
+
3
+ Deploy a supported framework from its project directory without a Gigadrive
4
+ configuration file:
5
+
6
+ ```bash
7
+ pnpm dlx gigadrive login
8
+ pnpm dlx gigadrive deploy
9
+ ```
10
+
11
+ For an unlinked project, `deploy` selects the authenticated user's organization,
12
+ creates a Network application, saves the local `.gigadrive/project.json` link,
13
+ auto-detects the framework, and deploys it. Use `--org` when the user belongs to
14
+ multiple organizations and a non-interactive deployment must select one.
15
+
16
+ Applications can also be created independently:
17
+
18
+ ```bash
19
+ pnpm dlx gigadrive apps create --org <organization-id> --name <application-name>
20
+ ```
21
+
22
+ The legacy `gigadrive platform deploy` command remains available as an alias for
23
+ `gigadrive deploy`.
package/dist/index.cjs CHANGED
@@ -32,7 +32,7 @@ var import_effect29 = require("effect");
32
32
  // package.json
33
33
  var package_default = {
34
34
  name: "gigadrive",
35
- version: "2.4.4",
35
+ version: "2.5.0",
36
36
  repository: {
37
37
  type: "git",
38
38
  url: "https://github.com/Gigadrive/sdk.git",
@@ -177,6 +177,13 @@ var ApplicationNotFoundError = class extends import_effect.Schema.TaggedError()(
177
177
  }
178
178
  ) {
179
179
  };
180
+ var NoOrganizationsFoundError = class extends import_effect.Schema.TaggedError()(
181
+ "NoOrganizationsFoundError",
182
+ {
183
+ message: import_effect.Schema.String
184
+ }
185
+ ) {
186
+ };
180
187
  var EnvFileReadError = class extends import_effect.Schema.TaggedError()("EnvFileReadError", {
181
188
  message: import_effect.Schema.String,
182
189
  cause: import_effect.Schema.optional(import_effect.Schema.String)
@@ -513,8 +520,11 @@ var CLI_SCOPES = [
513
520
  "openid",
514
521
  "profile",
515
522
  "email",
523
+ // Platform discovery used for zero-config application onboarding
524
+ "platform:organizations:read",
516
525
  // Network API capabilities
517
526
  "network:applications:read",
527
+ "network:applications:write",
518
528
  "network:env_vars:read",
519
529
  "network:env_vars:write",
520
530
  "network:env_vars:delete",
@@ -1106,13 +1116,13 @@ var readStdin = import_effect8.Effect.async((resume) => {
1106
1116
  stdin.resume();
1107
1117
  });
1108
1118
  var readJsonInput = (file) => import_effect8.Effect.gen(function* () {
1109
- const path = import_effect8.Option.getOrUndefined(file);
1119
+ const path2 = import_effect8.Option.getOrUndefined(file);
1110
1120
  let raw;
1111
- if (path === void 0 || path === "-") {
1121
+ if (path2 === void 0 || path2 === "-") {
1112
1122
  raw = yield* readStdin;
1113
1123
  } else {
1114
1124
  const fs = yield* import_platform3.FileSystem.FileSystem;
1115
- raw = yield* fs.readFileString(path, "utf8").pipe(import_effect8.Effect.mapError((e) => new ApiRequestError({ message: `Failed to read ${path}: ${String(e)}` })));
1125
+ raw = yield* fs.readFileString(path2, "utf8").pipe(import_effect8.Effect.mapError((e) => new ApiRequestError({ message: `Failed to read ${path2}: ${String(e)}` })));
1116
1126
  }
1117
1127
  return yield* import_effect8.Effect.try({
1118
1128
  try: () => JSON.parse(raw),
@@ -1332,14 +1342,30 @@ var aiCommand = aiBase.pipe(
1332
1342
  // src/commands/apps/index.ts
1333
1343
  var import_cli2 = require("@effect/cli");
1334
1344
  var import_effect9 = require("effect");
1335
- var orgOption2 = import_cli2.Options.text("org").pipe(
1345
+ var listOrgOption = import_cli2.Options.text("org").pipe(
1336
1346
  import_cli2.Options.withAlias("o"),
1337
1347
  import_cli2.Options.withDescription("Only list applications belonging to this organization ID"),
1338
1348
  import_cli2.Options.optional
1339
1349
  );
1350
+ var createOrgOption = import_cli2.Options.text("org").pipe(
1351
+ import_cli2.Options.withAlias("o"),
1352
+ import_cli2.Options.withDescription("Organization ID that will own the application")
1353
+ );
1354
+ var nameOption = import_cli2.Options.text("name").pipe(
1355
+ import_cli2.Options.withAlias("n"),
1356
+ import_cli2.Options.withDescription("Application display name")
1357
+ );
1358
+ var slugOption = import_cli2.Options.text("slug").pipe(
1359
+ import_cli2.Options.withDescription("Optional lowercase application slug"),
1360
+ import_cli2.Options.optional
1361
+ );
1362
+ var rootDirectoryOption = import_cli2.Options.text("root-directory").pipe(
1363
+ import_cli2.Options.withDescription("Optional project directory relative to the repository root"),
1364
+ import_cli2.Options.optional
1365
+ );
1340
1366
  var appsListCommand = import_cli2.Command.make(
1341
1367
  "list",
1342
- { org: orgOption2 },
1368
+ { org: listOrgOption },
1343
1369
  ({ org }) => import_effect9.Effect.gen(function* () {
1344
1370
  const apiClient = yield* ApiClientService;
1345
1371
  const organizationId = import_effect9.Option.getOrUndefined(org);
@@ -1362,8 +1388,35 @@ ${total} application(s).`);
1362
1388
  })
1363
1389
  )
1364
1390
  );
1391
+ var appsCreateCommand = import_cli2.Command.make(
1392
+ "create",
1393
+ { org: createOrgOption, name: nameOption, slug: slugOption, rootDirectory: rootDirectoryOption },
1394
+ ({ org, name, slug, rootDirectory }) => import_effect9.Effect.gen(function* () {
1395
+ const apiClient = yield* ApiClientService;
1396
+ const application = yield* apiClient.request(
1397
+ (client) => client.applications.create({
1398
+ organizationId: org,
1399
+ name,
1400
+ ...import_effect9.Option.match(slug, { onNone: () => ({}), onSome: (value) => ({ slug: value }) }),
1401
+ ...import_effect9.Option.match(rootDirectory, {
1402
+ onNone: () => ({}),
1403
+ onSome: (value) => ({ rootDirectory: value })
1404
+ })
1405
+ })
1406
+ );
1407
+ yield* import_effect9.Console.log(`Created application ${application.name} (${application.id}).`);
1408
+ yield* import_effect9.Console.log(`Run "gigadrive link --app ${application.id}" to link the current directory.`);
1409
+ }).pipe(
1410
+ import_effect9.Effect.catchTags({
1411
+ NotAuthenticatedError: (err) => import_effect9.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1412
+ import_effect9.Effect.andThen(import_effect9.Effect.fail(err))
1413
+ ),
1414
+ ApiRequestError: (err) => import_effect9.Console.error(`Failed to create application: ${err.message}`).pipe(import_effect9.Effect.andThen(import_effect9.Effect.fail(err)))
1415
+ })
1416
+ )
1417
+ );
1365
1418
  var appsBase = import_cli2.Command.make("apps", {}, () => import_effect9.Effect.void);
1366
- var appsCommand = appsBase.pipe(import_cli2.Command.withSubcommands([appsListCommand]));
1419
+ var appsCommand = appsBase.pipe(import_cli2.Command.withSubcommands([appsListCommand, appsCreateCommand]));
1367
1420
 
1368
1421
  // src/commands/build/index.ts
1369
1422
  var import_cli3 = require("@effect/cli");
@@ -1651,7 +1704,7 @@ var appOption2 = import_cli6.Options.text("app").pipe(
1651
1704
  import_cli6.Options.withDescription("Filter by application ID (defaults to the linked application)"),
1652
1705
  import_cli6.Options.optional
1653
1706
  );
1654
- var orgOption3 = import_cli6.Options.text("org").pipe(
1707
+ var orgOption2 = import_cli6.Options.text("org").pipe(
1655
1708
  import_cli6.Options.withAlias("o"),
1656
1709
  import_cli6.Options.withDescription("Filter by organization ID"),
1657
1710
  import_cli6.Options.optional
@@ -1671,7 +1724,7 @@ var resolveApplicationFilter = (app) => import_effect15.Option.match(app, {
1671
1724
  });
1672
1725
  var deploymentsListCommand = import_cli6.Command.make(
1673
1726
  "list",
1674
- { app: appOption2, org: orgOption3, status: statusOption },
1727
+ { app: appOption2, org: orgOption2, status: statusOption },
1675
1728
  ({ app, org, status }) => import_effect15.Effect.gen(function* () {
1676
1729
  const apiClient = yield* ApiClientService;
1677
1730
  const applicationId = yield* resolveApplicationFilter(app);
@@ -1965,7 +2018,7 @@ var appOption3 = import_cli7.Options.text("app").pipe(
1965
2018
  import_cli7.Options.withDescription("Operate on this application (overrides the linked application)"),
1966
2019
  import_cli7.Options.optional
1967
2020
  );
1968
- var orgOption4 = import_cli7.Options.text("org").pipe(
2021
+ var orgOption3 = import_cli7.Options.text("org").pipe(
1969
2022
  import_cli7.Options.withAlias("o"),
1970
2023
  import_cli7.Options.withDescription("Operate on this organization instead of an application"),
1971
2024
  import_cli7.Options.optional
@@ -2014,7 +2067,7 @@ var resolveScope = (app, org) => import_effect19.Effect.gen(function* () {
2014
2067
  });
2015
2068
  var envListCommand = import_cli7.Command.make(
2016
2069
  "list",
2017
- { app: appOption3, org: orgOption4 },
2070
+ { app: appOption3, org: orgOption3 },
2018
2071
  ({ app, org }) => import_effect19.Effect.gen(function* () {
2019
2072
  const apiClient = yield* ApiClientService;
2020
2073
  const scope = yield* resolveScope(app, org);
@@ -2041,7 +2094,7 @@ var envListCommand = import_cli7.Command.make(
2041
2094
  );
2042
2095
  var envSetCommand = import_cli7.Command.make(
2043
2096
  "set",
2044
- { keyValue: keyValueArg, app: appOption3, org: orgOption4, sensitive: sensitiveOption, env: envIdOption },
2097
+ { keyValue: keyValueArg, app: appOption3, org: orgOption3, sensitive: sensitiveOption, env: envIdOption },
2045
2098
  ({ keyValue, app, org, sensitive, env }) => import_effect19.Effect.gen(function* () {
2046
2099
  const separator = keyValue.indexOf("=");
2047
2100
  if (separator <= 0) {
@@ -2074,7 +2127,7 @@ var envSetCommand = import_cli7.Command.make(
2074
2127
  );
2075
2128
  var envRmCommand = import_cli7.Command.make(
2076
2129
  "rm",
2077
- { keyOrId: keyOrIdArg, app: appOption3, org: orgOption4 },
2130
+ { keyOrId: keyOrIdArg, app: appOption3, org: orgOption3 },
2078
2131
  ({ keyOrId, app, org }) => import_effect19.Effect.gen(function* () {
2079
2132
  const apiClient = yield* ApiClientService;
2080
2133
  const scope = yield* resolveScope(app, org);
@@ -2188,14 +2241,14 @@ var appOption4 = import_cli8.Options.text("app").pipe(
2188
2241
  import_cli8.Options.withDescription("Application ID to link (skips the interactive picker)"),
2189
2242
  import_cli8.Options.optional
2190
2243
  );
2191
- var orgOption5 = import_cli8.Options.text("org").pipe(
2244
+ var orgOption4 = import_cli8.Options.text("org").pipe(
2192
2245
  import_cli8.Options.withAlias("o"),
2193
2246
  import_cli8.Options.withDescription("Only list applications belonging to this organization ID"),
2194
2247
  import_cli8.Options.optional
2195
2248
  );
2196
2249
  var linkCommand = import_cli8.Command.make(
2197
2250
  "link",
2198
- { app: appOption4, org: orgOption5 },
2251
+ { app: appOption4, org: orgOption4 },
2199
2252
  ({ app, org }) => import_effect20.Effect.gen(function* () {
2200
2253
  const apiClient = yield* ApiClientService;
2201
2254
  const projectLink = yield* ProjectLinkService;
@@ -2320,6 +2373,7 @@ var import_platform9 = require("@effect/platform");
2320
2373
  var import_commons = require("@gigadrive/commons");
2321
2374
  var import_effect25 = require("effect");
2322
2375
  var os4 = __toESM(require("os"), 1);
2376
+ var path = __toESM(require("path"), 1);
2323
2377
 
2324
2378
  // src/services/archive.ts
2325
2379
  var import_platform8 = require("@effect/platform");
@@ -2590,22 +2644,83 @@ var appOption5 = import_cli11.Options.text("app").pipe(
2590
2644
  import_cli11.Options.withDescription("Application ID to deploy to (overrides the linked application)"),
2591
2645
  import_cli11.Options.optional
2592
2646
  );
2647
+ var orgOption5 = import_cli11.Options.text("org").pipe(
2648
+ import_cli11.Options.withAlias("o"),
2649
+ import_cli11.Options.withDescription("Organization ID to use when automatically creating an application"),
2650
+ import_cli11.Options.optional
2651
+ );
2652
+ var nameOption2 = import_cli11.Options.text("name").pipe(
2653
+ import_cli11.Options.withAlias("n"),
2654
+ import_cli11.Options.withDescription("Application name to use when automatically creating an application"),
2655
+ import_cli11.Options.optional
2656
+ );
2657
+ var createAndLinkDeploymentApplication = (input) => import_effect25.Effect.gen(function* () {
2658
+ const projectLink = yield* ProjectLinkService;
2659
+ const apiClient = yield* ApiClientService;
2660
+ const { items: organizations } = yield* apiClient.request((client) => client.organizations.list());
2661
+ const availableOrganizations = import_effect25.Option.match(input.org, {
2662
+ onNone: () => organizations,
2663
+ onSome: (organizationId) => organizations.filter((organization2) => organization2.id === organizationId)
2664
+ });
2665
+ if (availableOrganizations.length === 0) {
2666
+ return yield* new NoOrganizationsFoundError({
2667
+ message: import_effect25.Option.match(input.org, {
2668
+ onNone: () => 'No organization is available for this deployment. Create an organization, then run "gigadrive deploy" again.',
2669
+ onSome: (organizationId) => `Organization "${organizationId}" was not found among your organizations.`
2670
+ })
2671
+ });
2672
+ }
2673
+ const organization = availableOrganizations.length === 1 ? availableOrganizations[0] : yield* import_cli11.Prompt.run(
2674
+ import_cli11.Prompt.select({
2675
+ message: "Select an organization for this application",
2676
+ choices: availableOrganizations.map((candidate) => ({
2677
+ title: candidate.name,
2678
+ value: candidate
2679
+ }))
2680
+ })
2681
+ );
2682
+ const inferredName = path.basename(input.cwd) || "Application";
2683
+ const applicationName = import_effect25.Option.match(input.name, {
2684
+ onNone: () => inferredName,
2685
+ onSome: (name) => name
2686
+ }).trim();
2687
+ const application = yield* apiClient.request(
2688
+ (client) => client.applications.create({
2689
+ organizationId: organization.id,
2690
+ name: applicationName || inferredName
2691
+ })
2692
+ );
2693
+ yield* projectLink.save(input.cwd, {
2694
+ applicationId: application.id,
2695
+ organizationId: organization.id
2696
+ });
2697
+ yield* import_effect25.Console.log(`Created and linked application ${application.name} (${application.id}).`);
2698
+ return application.id;
2699
+ });
2700
+ var resolveLinkedDeploymentApplication = (input) => import_effect25.Effect.gen(function* () {
2701
+ const projectLink = yield* ProjectLinkService;
2702
+ const existingLink = yield* projectLink.load(input.cwd);
2703
+ return yield* import_effect25.Option.match(existingLink, {
2704
+ onNone: () => createAndLinkDeploymentApplication(input),
2705
+ onSome: (link) => import_effect25.Effect.succeed(link.applicationId)
2706
+ });
2707
+ });
2708
+ var resolveDeploymentApplication = (input) => import_effect25.Option.match(input.app, {
2709
+ onNone: () => resolveLinkedDeploymentApplication(input),
2710
+ onSome: import_effect25.Effect.succeed
2711
+ });
2593
2712
  var deployCommand = import_cli11.Command.make(
2594
2713
  "deploy",
2595
- { app: appOption5 },
2596
- ({ app }) => import_effect25.Effect.gen(function* () {
2714
+ { app: appOption5, org: orgOption5, name: nameOption2 },
2715
+ ({ app, org, name }) => import_effect25.Effect.gen(function* () {
2597
2716
  const projectConfig = yield* ProjectConfigService;
2598
2717
  const deploymentApi = yield* DeploymentApiService;
2599
2718
  const archiveService = yield* ArchiveService;
2600
- const projectLink = yield* ProjectLinkService;
2601
2719
  const pathService = yield* import_platform9.Path.Path;
2602
2720
  const cwd = process.cwd();
2603
- const applicationId = yield* import_effect25.Option.match(app, {
2604
- onSome: (id) => import_effect25.Effect.succeed(id),
2605
- onNone: () => projectLink.resolve(cwd).pipe(import_effect25.Effect.map((link) => link.applicationId))
2606
- });
2607
2721
  yield* import_effect25.Effect.log("Starting deploy command");
2608
2722
  const { config } = yield* projectConfig.resolve(cwd);
2723
+ const applicationId = yield* resolveDeploymentApplication({ app, org, name, cwd });
2609
2724
  yield* import_effect25.Console.log("Creating deployment...");
2610
2725
  const deploymentId = yield* deploymentApi.createDeployment(applicationId);
2611
2726
  yield* import_effect25.Console.log(`Deployment ID: ${deploymentId}`);
@@ -2625,8 +2740,14 @@ var deployCommand = import_cli11.Command.make(
2625
2740
  yield* pollDeployment(deploymentId);
2626
2741
  }).pipe(
2627
2742
  import_effect25.Effect.catchTags({
2628
- ProjectNotLinkedError: (err) => import_effect25.Console.error(err.message).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2743
+ QuitException: () => import_effect25.Console.log("Cancelled."),
2744
+ NotAuthenticatedError: (err) => import_effect25.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
2745
+ import_effect25.Effect.andThen(import_effect25.Effect.fail(err))
2746
+ ),
2747
+ ApiRequestError: (err) => import_effect25.Console.error(`Application setup failed: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2748
+ NoOrganizationsFoundError: (err) => import_effect25.Console.error(err.message).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2629
2749
  ProjectLinkReadError: (err) => import_effect25.Console.error(err.message).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2750
+ ProjectLinkWriteError: (err) => import_effect25.Console.error(err.message).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2630
2751
  ConfigNotFoundError: (err) => import_effect25.Console.error(err.message).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2631
2752
  ConfigParseError: (err) => import_effect25.Console.error(`Config parse error: ${err.message}`).pipe(import_effect25.Effect.andThen(import_effect25.Effect.fail(err))),
2632
2753
  ConfigValidationError: (err) => import_effect25.Effect.gen(function* () {
@@ -2917,6 +3038,7 @@ var gigadrive = import_cli15.Command.make("gigadrive", {}, () => import_effect29
2917
3038
  aiCommand,
2918
3039
  buildCommand,
2919
3040
  debugCommand,
3041
+ deployCommand,
2920
3042
  platformCommand,
2921
3043
  setupCommand
2922
3044
  ])