gigadrive 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -24,15 +24,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_cli14 = require("@effect/cli");
27
+ var import_cli15 = require("@effect/cli");
28
28
  var import_platform_node = require("@effect/platform-node");
29
29
  var import_network_config2 = require("@gigadrive/network-config");
30
- var import_effect25 = require("effect");
30
+ var import_effect29 = require("effect");
31
31
 
32
32
  // package.json
33
33
  var package_default = {
34
34
  name: "gigadrive",
35
- version: "2.2.0",
35
+ version: "2.3.0",
36
36
  repository: {
37
37
  type: "git",
38
38
  url: "https://github.com/Gigadrive/sdk.git",
@@ -158,6 +158,31 @@ var ApplicationNotFoundError = class extends import_effect.Schema.TaggedError()(
158
158
  }
159
159
  ) {
160
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
+ };
161
186
  var ConfigNotFoundError = class extends import_effect.Schema.TaggedError()("ConfigNotFoundError", {
162
187
  message: import_effect.Schema.String,
163
188
  directory: import_effect.Schema.String
@@ -323,6 +348,10 @@ var ProjectLink = import_effect2.Schema.Struct({
323
348
  applicationId: import_effect2.Schema.String,
324
349
  organizationId: import_effect2.Schema.optional(import_effect2.Schema.String)
325
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 });
326
355
 
327
356
  // src/services/auth-storage.ts
328
357
  var AuthStorageService = class extends import_effect3.Effect.Service()("AuthStorageService", {
@@ -403,7 +432,11 @@ var AuthStorageService = class extends import_effect3.Effect.Service()("AuthStor
403
432
  var import_effect4 = require("effect");
404
433
  var OAuthEnvConfig = import_effect4.Config.all({
405
434
  issuerUrl: import_effect4.Config.string("GIGADRIVE_NETWORK_OAUTH_ISSUER_URL").pipe(import_effect4.Config.withDefault("https://idp.gigadrive.de")),
406
- clientId: import_effect4.Config.string("GIGADRIVE_NETWORK_OAUTH_CLIENT_ID").pipe(import_effect4.Config.withDefault("todo_add_client_id"))
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
+ )
407
440
  });
408
441
  var OpenIdDiscoveryDocument = import_effect4.Schema.Struct({
409
442
  issuer: import_effect4.Schema.optional(import_effect4.Schema.String),
@@ -411,7 +444,33 @@ var OpenIdDiscoveryDocument = import_effect4.Schema.Struct({
411
444
  token_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String),
412
445
  userinfo_endpoint: import_effect4.Schema.optional(import_effect4.Schema.String)
413
446
  });
414
- var DEFAULT_SCOPE = "offline_access openid profile email";
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(" ");
415
474
  var OAuthConfigService = class extends import_effect4.Effect.Service()("OAuthConfigService", {
416
475
  accessors: true,
417
476
  effect: import_effect4.Effect.gen(function* () {
@@ -1555,7 +1614,230 @@ var deploymentsCommand = deploymentsBase.pipe(
1555
1614
 
1556
1615
  // src/commands/env/index.ts
1557
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");
1558
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
1559
1841
  var appOption3 = import_cli7.Options.text("app").pipe(
1560
1842
  import_cli7.Options.withAlias("a"),
1561
1843
  import_cli7.Options.withDescription("Operate on this application (overrides the linked application)"),
@@ -1578,51 +1860,70 @@ var keyValueArg = import_cli7.Args.text({ name: "key=value" }).pipe(
1578
1860
  import_cli7.Args.withDescription("Variable assignment, e.g. DATABASE_URL=postgres://user:pass@host/db")
1579
1861
  );
1580
1862
  var keyOrIdArg = import_cli7.Args.text({ name: "key-or-id" }).pipe(import_cli7.Args.withDescription("Variable key or ID to remove"));
1581
- var resolveScope = (app, org) => import_effect16.Effect.gen(function* () {
1582
- if (import_effect16.Option.isSome(org)) {
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)) {
1583
1884
  return { kind: "organization", id: org.value };
1584
1885
  }
1585
1886
  const projectLink = yield* ProjectLinkService;
1586
- const id = yield* import_effect16.Option.match(app, {
1587
- onSome: (value) => import_effect16.Effect.succeed(value),
1588
- onNone: () => projectLink.resolve(process.cwd()).pipe(import_effect16.Effect.map((link) => link.applicationId))
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))
1589
1890
  });
1590
1891
  return { kind: "application", id };
1591
1892
  });
1592
1893
  var envListCommand = import_cli7.Command.make(
1593
1894
  "list",
1594
1895
  { app: appOption3, org: orgOption4 },
1595
- ({ app, org }) => import_effect16.Effect.gen(function* () {
1896
+ ({ app, org }) => import_effect19.Effect.gen(function* () {
1596
1897
  const apiClient = yield* ApiClientService;
1597
1898
  const scope = yield* resolveScope(app, org);
1598
1899
  const { items } = yield* apiClient.request(
1599
1900
  (client) => scope.kind === "organization" ? client.organizations.envVars.list(scope.id) : client.applications.envVars.list(scope.id)
1600
1901
  );
1601
1902
  if (items.length === 0) {
1602
- yield* import_effect16.Console.log("No environment variables set.");
1903
+ yield* import_effect19.Console.log("No environment variables set.");
1603
1904
  return;
1604
1905
  }
1605
1906
  for (const v of items) {
1606
- yield* import_effect16.Console.log(`${v.key}=${v.sensitive ? "***" : v.value ?? ""}`);
1907
+ yield* import_effect19.Console.log(`${v.key}=${v.sensitive ? "***" : v.value ?? ""}`);
1607
1908
  }
1608
1909
  }).pipe(
1609
- import_effect16.Effect.catchTags({
1610
- NotAuthenticatedError: (err) => import_effect16.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1611
- import_effect16.Effect.andThen(import_effect16.Effect.fail(err))
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))
1612
1913
  ),
1613
- ApiRequestError: (err) => import_effect16.Console.error(`Failed to list environment variables: ${err.message}`).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1614
- ProjectNotLinkedError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1615
- ProjectLinkReadError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err)))
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)))
1616
1917
  })
1617
1918
  )
1618
1919
  );
1619
1920
  var envSetCommand = import_cli7.Command.make(
1620
1921
  "set",
1621
1922
  { keyValue: keyValueArg, app: appOption3, org: orgOption4, sensitive: sensitiveOption, env: envIdOption },
1622
- ({ keyValue, app, org, sensitive, env }) => import_effect16.Effect.gen(function* () {
1923
+ ({ keyValue, app, org, sensitive, env }) => import_effect19.Effect.gen(function* () {
1623
1924
  const separator = keyValue.indexOf("=");
1624
1925
  if (separator <= 0) {
1625
- return yield* import_effect16.Effect.fail(
1926
+ return yield* import_effect19.Effect.fail(
1626
1927
  new InvalidEnvVarFormatError({
1627
1928
  message: 'Invalid format. Use KEY=VALUE, e.g. "gigadrive env set DATABASE_URL=postgres://...".'
1628
1929
  })
@@ -1632,27 +1933,27 @@ var envSetCommand = import_cli7.Command.make(
1632
1933
  const value = keyValue.slice(separator + 1);
1633
1934
  const apiClient = yield* ApiClientService;
1634
1935
  const scope = yield* resolveScope(app, org);
1635
- const environmentId = import_effect16.Option.getOrUndefined(env);
1936
+ const environmentId = import_effect19.Option.getOrUndefined(env);
1636
1937
  const created = yield* apiClient.request(
1637
1938
  (client) => scope.kind === "organization" ? client.organizations.envVars.create(scope.id, { key, value, sensitive }) : client.applications.envVars.create(scope.id, { key, value, sensitive, environmentId })
1638
1939
  );
1639
- yield* import_effect16.Console.log(`Set ${created.key}.`);
1940
+ yield* import_effect19.Console.log(`Set ${created.key}.`);
1640
1941
  }).pipe(
1641
- import_effect16.Effect.catchTags({
1642
- InvalidEnvVarFormatError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1643
- NotAuthenticatedError: (err) => import_effect16.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1644
- import_effect16.Effect.andThen(import_effect16.Effect.fail(err))
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))
1645
1946
  ),
1646
- ApiRequestError: (err) => import_effect16.Console.error(`Failed to set environment variable: ${err.message}`).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1647
- ProjectNotLinkedError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1648
- ProjectLinkReadError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err)))
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)))
1649
1950
  })
1650
1951
  )
1651
1952
  );
1652
1953
  var envRmCommand = import_cli7.Command.make(
1653
1954
  "rm",
1654
1955
  { keyOrId: keyOrIdArg, app: appOption3, org: orgOption4 },
1655
- ({ keyOrId, app, org }) => import_effect16.Effect.gen(function* () {
1956
+ ({ keyOrId, app, org }) => import_effect19.Effect.gen(function* () {
1656
1957
  const apiClient = yield* ApiClientService;
1657
1958
  const scope = yield* resolveScope(app, org);
1658
1959
  const { items } = yield* apiClient.request(
@@ -1660,32 +1961,106 @@ var envRmCommand = import_cli7.Command.make(
1660
1961
  );
1661
1962
  const target = items.find((v) => v.id === keyOrId || v.key === keyOrId);
1662
1963
  if (target === void 0) {
1663
- return yield* import_effect16.Effect.fail(
1964
+ return yield* import_effect19.Effect.fail(
1664
1965
  new EnvVarNotFoundError({ message: `No environment variable matching "${keyOrId}".` })
1665
1966
  );
1666
1967
  }
1667
1968
  yield* apiClient.request(
1668
1969
  (client) => scope.kind === "organization" ? client.organizations.envVars.delete(scope.id, target.id) : client.applications.envVars.delete(scope.id, target.id)
1669
1970
  );
1670
- yield* import_effect16.Console.log(`Removed ${target.key}.`);
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 })
2011
+ );
2012
+ if (!overwrite) {
2013
+ return yield* import_effect19.Console.log("Cancelled.");
2014
+ }
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.`
2034
+ );
2035
+ }
2036
+ if (gitignored) {
2037
+ yield* import_effect19.Console.log(`Added ${target} to .gitignore.`);
2038
+ }
1671
2039
  }).pipe(
1672
- import_effect16.Effect.catchTags({
1673
- EnvVarNotFoundError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1674
- NotAuthenticatedError: (err) => import_effect16.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1675
- import_effect16.Effect.andThen(import_effect16.Effect.fail(err))
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))
1676
2045
  ),
1677
- ApiRequestError: (err) => import_effect16.Console.error(`Failed to remove environment variable: ${err.message}`).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1678
- ProjectNotLinkedError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err))),
1679
- ProjectLinkReadError: (err) => import_effect16.Console.error(err.message).pipe(import_effect16.Effect.andThen(import_effect16.Effect.fail(err)))
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)))
1680
2053
  })
1681
2054
  )
1682
2055
  );
1683
- var envBase = import_cli7.Command.make("env", {}, () => import_effect16.Effect.void);
1684
- var envCommand = envBase.pipe(import_cli7.Command.withSubcommands([envListCommand, envSetCommand, envRmCommand]));
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
+ );
1685
2060
 
1686
2061
  // src/commands/link/index.ts
1687
2062
  var import_cli8 = require("@effect/cli");
1688
- var import_effect17 = require("effect");
2063
+ var import_effect20 = require("effect");
1689
2064
  var appOption4 = import_cli8.Options.text("app").pipe(
1690
2065
  import_cli8.Options.withAlias("a"),
1691
2066
  import_cli8.Options.withDescription("Application ID to link (skips the interactive picker)"),
@@ -1699,26 +2074,26 @@ var orgOption5 = import_cli8.Options.text("org").pipe(
1699
2074
  var linkCommand = import_cli8.Command.make(
1700
2075
  "link",
1701
2076
  { app: appOption4, org: orgOption5 },
1702
- ({ app, org }) => import_effect17.Effect.gen(function* () {
2077
+ ({ app, org }) => import_effect20.Effect.gen(function* () {
1703
2078
  const apiClient = yield* ApiClientService;
1704
2079
  const projectLink = yield* ProjectLinkService;
1705
2080
  const cwd = process.cwd();
1706
- const organizationId = import_effect17.Option.getOrUndefined(org);
2081
+ const organizationId = import_effect20.Option.getOrUndefined(org);
1707
2082
  const { items: apps } = yield* apiClient.request((client) => client.applications.list({ organizationId }));
1708
2083
  if (apps.length === 0) {
1709
- return yield* import_effect17.Effect.fail(
2084
+ return yield* import_effect20.Effect.fail(
1710
2085
  new NoApplicationsFoundError({
1711
2086
  message: 'No applications found. Create one in the Gigadrive console, then run "gigadrive link" again.'
1712
2087
  })
1713
2088
  );
1714
2089
  }
1715
- const explicitAppId = import_effect17.Option.getOrUndefined(app);
2090
+ const explicitAppId = import_effect20.Option.getOrUndefined(app);
1716
2091
  let applicationId;
1717
2092
  let linkedOrganizationId;
1718
2093
  if (explicitAppId !== void 0) {
1719
2094
  const match = apps.find((a) => a.id === explicitAppId);
1720
2095
  if (match === void 0) {
1721
- return yield* import_effect17.Effect.fail(
2096
+ return yield* import_effect20.Effect.fail(
1722
2097
  new ApplicationNotFoundError({
1723
2098
  message: `Application "${explicitAppId}" was not found among your applications.`
1724
2099
  })
@@ -1740,106 +2115,106 @@ var linkCommand = import_cli8.Command.make(
1740
2115
  linkedOrganizationId = chosen.organization.id;
1741
2116
  }
1742
2117
  yield* projectLink.save(cwd, { applicationId, organizationId: linkedOrganizationId });
1743
- yield* import_effect17.Console.log(`Linked this directory to application ${applicationId}.`);
1744
- yield* import_effect17.Console.log('Tip: add ".gigadrive/" to your .gitignore.');
2118
+ yield* import_effect20.Console.log(`Linked this directory to application ${applicationId}.`);
2119
+ yield* import_effect20.Console.log('Tip: add ".gigadrive/" to your .gitignore.');
1745
2120
  }).pipe(
1746
- import_effect17.Effect.catchTags({
2121
+ import_effect20.Effect.catchTags({
1747
2122
  // Ctrl+C during the picker is a deliberate cancel — exit 0.
1748
- QuitException: () => import_effect17.Console.log("Cancelled."),
1749
- NotAuthenticatedError: (err) => import_effect17.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
1750
- import_effect17.Effect.andThen(import_effect17.Effect.fail(err))
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))
1751
2126
  ),
1752
- ApiRequestError: (err) => import_effect17.Console.error(`Failed to list applications: ${err.message}`).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err))),
1753
- NoApplicationsFoundError: (err) => import_effect17.Console.error(err.message).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err))),
1754
- ApplicationNotFoundError: (err) => import_effect17.Console.error(err.message).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err))),
1755
- ProjectLinkWriteError: (err) => import_effect17.Console.error(err.message).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err)))
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)))
1756
2131
  })
1757
2132
  )
1758
2133
  );
1759
2134
  var unlinkCommand = import_cli8.Command.make(
1760
2135
  "unlink",
1761
2136
  {},
1762
- () => import_effect17.Effect.gen(function* () {
2137
+ () => import_effect20.Effect.gen(function* () {
1763
2138
  const projectLink = yield* ProjectLinkService;
1764
2139
  yield* projectLink.remove(process.cwd());
1765
- yield* import_effect17.Console.log("Unlinked this directory.");
2140
+ yield* import_effect20.Console.log("Unlinked this directory.");
1766
2141
  }).pipe(
1767
- import_effect17.Effect.catchTags({
1768
- ProjectLinkWriteError: (err) => import_effect17.Console.error(err.message).pipe(import_effect17.Effect.andThen(import_effect17.Effect.fail(err)))
2142
+ import_effect20.Effect.catchTags({
2143
+ ProjectLinkWriteError: (err) => import_effect20.Console.error(err.message).pipe(import_effect20.Effect.andThen(import_effect20.Effect.fail(err)))
1769
2144
  })
1770
2145
  )
1771
2146
  );
1772
2147
 
1773
2148
  // src/commands/login/index.ts
1774
2149
  var import_cli9 = require("@effect/cli");
1775
- var import_effect18 = require("effect");
2150
+ var import_effect21 = require("effect");
1776
2151
  var loginCommand = import_cli9.Command.make(
1777
2152
  "login",
1778
2153
  {},
1779
- () => import_effect18.Effect.gen(function* () {
1780
- yield* import_effect18.Console.log("Initiating login...");
2154
+ () => import_effect21.Effect.gen(function* () {
2155
+ yield* import_effect21.Console.log("Initiating login...");
1781
2156
  const auth = yield* AuthService;
1782
2157
  yield* auth.login;
1783
- const info = yield* auth.getUserInfo.pipe(import_effect18.Effect.catchAll(() => import_effect18.Effect.succeed({})));
2158
+ const info = yield* auth.getUserInfo.pipe(import_effect21.Effect.catchAll(() => import_effect21.Effect.succeed({})));
1784
2159
  const name = auth.inferUserName(info);
1785
- yield* import_effect18.Console.log(`You are now logged in as ${name}.`);
2160
+ yield* import_effect21.Console.log(`You are now logged in as ${name}.`);
1786
2161
  }).pipe(
1787
- import_effect18.Effect.catchTags({
1788
- LoginFlowError: (err) => import_effect18.Console.error(`Login failed: ${err.message}`),
1789
- TokenExchangeError: (err) => import_effect18.Console.error(`Login failed: ${err.message}`),
1790
- OAuthDiscoveryError: (err) => import_effect18.Console.error(`Login failed: ${err.message}`),
1791
- AuthStorageWriteError: (err) => import_effect18.Console.error(`Failed to save credentials: ${err.message}`)
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}`)
1792
2167
  })
1793
2168
  )
1794
2169
  );
1795
2170
 
1796
2171
  // src/commands/logout/index.ts
1797
2172
  var import_cli10 = require("@effect/cli");
1798
- var import_effect19 = require("effect");
2173
+ var import_effect22 = require("effect");
1799
2174
  var logoutCommand = import_cli10.Command.make(
1800
2175
  "logout",
1801
2176
  {},
1802
- () => import_effect19.Effect.gen(function* () {
2177
+ () => import_effect22.Effect.gen(function* () {
1803
2178
  const auth = yield* AuthService;
1804
2179
  yield* auth.logout;
1805
- yield* import_effect19.Console.log("Logged out.");
2180
+ yield* import_effect22.Console.log("Logged out.");
1806
2181
  }).pipe(
1807
- import_effect19.Effect.catchTags({
1808
- AuthStorageWriteError: (err) => import_effect19.Console.error(`Failed to log out: ${err.message}`).pipe(import_effect19.Effect.andThen(import_effect19.Effect.fail(err)))
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)))
1809
2184
  })
1810
2185
  )
1811
2186
  );
1812
2187
 
1813
2188
  // src/commands/platform/index.ts
1814
2189
  var import_cli12 = require("@effect/cli");
1815
- var import_effect23 = require("effect");
2190
+ var import_effect26 = require("effect");
1816
2191
 
1817
2192
  // src/commands/platform/deploy/index.ts
1818
2193
  var import_cli11 = require("@effect/cli");
1819
- var import_platform7 = require("@effect/platform");
2194
+ var import_platform9 = require("@effect/platform");
1820
2195
  var import_commons = require("@gigadrive/commons");
1821
- var import_effect22 = require("effect");
1822
- var os2 = __toESM(require("os"), 1);
2196
+ var import_effect25 = require("effect");
2197
+ var os4 = __toESM(require("os"), 1);
1823
2198
 
1824
2199
  // src/services/archive.ts
1825
- var import_platform6 = require("@effect/platform");
2200
+ var import_platform8 = require("@effect/platform");
1826
2201
  var import_build_utils3 = require("@gigadrive/build-utils");
1827
2202
  var import_archiver = __toESM(require("archiver"), 1);
1828
- var import_effect20 = require("effect");
2203
+ var import_effect23 = require("effect");
1829
2204
  var import_ignore = __toESM(require("ignore"), 1);
1830
2205
  var nodeFs = __toESM(require("fs"), 1);
1831
2206
  var nodeFsPromises = __toESM(require("fs/promises"), 1);
1832
2207
  var IGNORE_FILE_NAMES = [".gigadriveignore", ".vercelignore", ".dockerignore", ".nowignore"];
1833
2208
  var readIgnoreFile = (fs, ignorePath) => fs.readFileString(ignorePath, "utf8").pipe(
1834
- import_effect20.Effect.map((content) => content.split("\n").filter((line) => line.trim() !== "")),
1835
- import_effect20.Effect.catchAll(() => import_effect20.Effect.succeed([]))
2209
+ import_effect23.Effect.map((content) => content.split("\n").filter((line) => line.trim() !== "")),
2210
+ import_effect23.Effect.catchAll(() => import_effect23.Effect.succeed([]))
1836
2211
  );
1837
- var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) => import_effect20.Effect.gen(function* () {
1838
- const items = yield* fs.readDirectory(dir).pipe(import_effect20.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${dir}` })));
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}` })));
1839
2214
  for (const item of items) {
1840
2215
  const fullPath = pathService.join(dir, item);
1841
2216
  const relativePath = pathService.relative(baseDir, fullPath);
1842
- const stat = yield* import_effect20.Effect.tryPromise({
2217
+ const stat = yield* import_effect23.Effect.tryPromise({
1843
2218
  try: () => nodeFsPromises.lstat(fullPath),
1844
2219
  catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
1845
2220
  });
@@ -1859,8 +2234,8 @@ var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) => impo
1859
2234
  yield* collectIgnorePatterns(fs, pathService, fullPath, baseDir, ignoreRules);
1860
2235
  }
1861
2236
  }
1862
- }).pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.void));
1863
- var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect20.Effect.gen(function* () {
2237
+ }).pipe(import_effect23.Effect.catchAll(() => import_effect23.Effect.void));
2238
+ var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect23.Effect.gen(function* () {
1864
2239
  const ignoreRules = (0, import_ignore.default)();
1865
2240
  ignoreRules.add([".git/", "**/.DS_Store", "**/Thumbs.db"]);
1866
2241
  for (const ignoreFileName of IGNORE_FILE_NAMES) {
@@ -1875,14 +2250,14 @@ var initializeIgnoreRules = (fs, pathService, baseDir) => import_effect20.Effect
1875
2250
  yield* collectIgnorePatterns(fs, pathService, baseDir, baseDir, ignoreRules);
1876
2251
  return ignoreRules;
1877
2252
  });
1878
- var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect20.Effect.gen(function* () {
2253
+ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect23.Effect.gen(function* () {
1879
2254
  const filesToInclude = [];
1880
- const walkDir = (currentDir) => import_effect20.Effect.gen(function* () {
1881
- const items = yield* fs.readDirectory(currentDir).pipe(import_effect20.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${currentDir}` })));
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}` })));
1882
2257
  for (const item of items) {
1883
2258
  const fullPath = pathService.join(currentDir, item);
1884
2259
  const relativePath = pathService.relative(dir, fullPath);
1885
- const stat = yield* import_effect20.Effect.tryPromise({
2260
+ const stat = yield* import_effect23.Effect.tryPromise({
1886
2261
  try: () => nodeFsPromises.lstat(fullPath),
1887
2262
  catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
1888
2263
  });
@@ -1896,21 +2271,21 @@ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect20.E
1896
2271
  }
1897
2272
  }
1898
2273
  }
1899
- }).pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.void));
2274
+ }).pipe(import_effect23.Effect.catchAll(() => import_effect23.Effect.void));
1900
2275
  yield* walkDir(dir);
1901
2276
  return filesToInclude;
1902
2277
  });
1903
- var ArchiveService = class extends import_effect20.Effect.Service()("ArchiveService", {
2278
+ var ArchiveService = class extends import_effect23.Effect.Service()("ArchiveService", {
1904
2279
  accessors: true,
1905
- effect: import_effect20.Effect.gen(function* () {
1906
- const fs = yield* import_platform6.FileSystem.FileSystem;
1907
- const pathService = yield* import_platform6.Path.Path;
1908
- const createZipArchive = import_effect20.Effect.fn("ArchiveService.createZipArchive")(function* (inputDir, outputFile, options = {}) {
1909
- yield* import_effect20.Effect.annotateCurrentSpan("inputDir", inputDir);
1910
- yield* import_effect20.Effect.annotateCurrentSpan("outputFile", outputFile);
1911
- yield* import_effect20.Effect.log("Creating zip archive", { inputDir, outputFile });
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 });
1912
2287
  const ignoreRules = options.useIgnoreFiles !== false ? yield* initializeIgnoreRules(fs, pathService, inputDir) : (0, import_ignore.default)();
1913
- const filesToInclude = options.whitelist ? yield* import_effect20.Effect.tryPromise({
2288
+ const filesToInclude = options.whitelist ? yield* import_effect23.Effect.tryPromise({
1914
2289
  try: () => Promise.all(
1915
2290
  options.whitelist.map((file) => (0, import_build_utils3.getFilesForPattern)(file, inputDir, options.excludeFiles ?? []))
1916
2291
  ),
@@ -1918,25 +2293,25 @@ var ArchiveService = class extends import_effect20.Effect.Service()("ArchiveServ
1918
2293
  message: "Failed to resolve whitelist patterns",
1919
2294
  cause: error instanceof Error ? error.message : String(error)
1920
2295
  })
1921
- }) : yield* import_effect20.Effect.gen(function* () {
2296
+ }) : yield* import_effect23.Effect.gen(function* () {
1922
2297
  if (options.excludeFiles && options.excludeFiles.length > 0) {
1923
2298
  ignoreRules.add(options.excludeFiles);
1924
2299
  }
1925
2300
  return yield* getFilesToInclude(fs, pathService, inputDir, ignoreRules).pipe(
1926
- import_effect20.Effect.map((files) => [files])
2301
+ import_effect23.Effect.map((files) => [files])
1927
2302
  );
1928
2303
  });
1929
- const archiveExists = yield* fs.exists(outputFile).pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.succeed(false)));
2304
+ const archiveExists = yield* fs.exists(outputFile).pipe(import_effect23.Effect.catchAll(() => import_effect23.Effect.succeed(false)));
1930
2305
  if (archiveExists) {
1931
2306
  yield* fs.remove(outputFile).pipe(
1932
- import_effect20.Effect.mapError(
2307
+ import_effect23.Effect.mapError(
1933
2308
  (error) => new ArchiveCreateError({
1934
2309
  message: `Failed to remove existing archive: ${String(error)}`
1935
2310
  })
1936
2311
  )
1937
2312
  );
1938
2313
  }
1939
- yield* import_effect20.Effect.tryPromise({
2314
+ yield* import_effect23.Effect.tryPromise({
1940
2315
  try: () => new Promise((resolve, reject) => {
1941
2316
  const output = nodeFs.createWriteStream(outputFile);
1942
2317
  const archive = (0, import_archiver.default)("zip", { zlib: { level: 9 } });
@@ -1955,14 +2330,14 @@ var ArchiveService = class extends import_effect20.Effect.Service()("ArchiveServ
1955
2330
  })
1956
2331
  });
1957
2332
  const fileInfo = yield* fs.stat(outputFile).pipe(
1958
- import_effect20.Effect.mapError(
2333
+ import_effect23.Effect.mapError(
1959
2334
  (error) => new ArchiveCreateError({
1960
2335
  message: `Failed to stat archive: ${String(error)}`
1961
2336
  })
1962
2337
  )
1963
2338
  );
1964
2339
  const size = Number(fileInfo.size);
1965
- yield* import_effect20.Effect.log("Archive created", { size });
2340
+ yield* import_effect23.Effect.log("Archive created", { size });
1966
2341
  return { path: outputFile, size };
1967
2342
  });
1968
2343
  return { createZipArchive };
@@ -1971,32 +2346,32 @@ var ArchiveService = class extends import_effect20.Effect.Service()("ArchiveServ
1971
2346
  };
1972
2347
 
1973
2348
  // src/services/deployment-api.ts
1974
- var import_effect21 = require("effect");
1975
- var DeploymentApiService = class extends import_effect21.Effect.Service()("DeploymentApiService", {
2349
+ var import_effect24 = require("effect");
2350
+ var DeploymentApiService = class extends import_effect24.Effect.Service()("DeploymentApiService", {
1976
2351
  accessors: true,
1977
2352
  dependencies: [ApiClientService.Default],
1978
- effect: import_effect21.Effect.gen(function* () {
2353
+ effect: import_effect24.Effect.gen(function* () {
1979
2354
  const apiClient = yield* ApiClientService;
1980
2355
  const getClient = apiClient.getClient.pipe(
1981
- import_effect21.Effect.mapError((err) => new DeploymentAuthError({ message: `Authentication failed: ${err.message}` }))
2356
+ import_effect24.Effect.mapError((err) => new DeploymentAuthError({ message: `Authentication failed: ${err.message}` }))
1982
2357
  );
1983
- const createDeployment = import_effect21.Effect.fn("DeploymentApiService.createDeployment")(function* (applicationId) {
1984
- yield* import_effect21.Effect.annotateCurrentSpan("applicationId", applicationId);
1985
- yield* import_effect21.Effect.log("Creating deployment", { applicationId });
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 });
1986
2361
  const client = yield* getClient;
1987
- const deployment = yield* import_effect21.Effect.tryPromise({
2362
+ const deployment = yield* import_effect24.Effect.tryPromise({
1988
2363
  try: () => client.deployments.create({ applicationId }),
1989
2364
  catch: (error) => new DeploymentCreateError({
1990
2365
  message: `Failed to create deployment: ${error instanceof Error ? error.message : String(error)}`
1991
2366
  })
1992
2367
  });
1993
- yield* import_effect21.Effect.log("Deployment created", { deploymentId: deployment.id });
2368
+ yield* import_effect24.Effect.log("Deployment created", { deploymentId: deployment.id });
1994
2369
  return deployment.id;
1995
2370
  });
1996
- const startMultipartUpload = import_effect21.Effect.fn("DeploymentApiService.startMultipartUpload")(function* (deploymentId) {
1997
- yield* import_effect21.Effect.annotateCurrentSpan("deploymentId", deploymentId);
2371
+ const startMultipartUpload = import_effect24.Effect.fn("DeploymentApiService.startMultipartUpload")(function* (deploymentId) {
2372
+ yield* import_effect24.Effect.annotateCurrentSpan("deploymentId", deploymentId);
1998
2373
  const client = yield* getClient;
1999
- const result = yield* import_effect21.Effect.tryPromise({
2374
+ const result = yield* import_effect24.Effect.tryPromise({
2000
2375
  try: () => client.deployments.startUpload(deploymentId),
2001
2376
  catch: (error) => new UploadStartError({
2002
2377
  message: `Failed to start multipart upload: ${error instanceof Error ? error.message : String(error)}`
@@ -2004,9 +2379,9 @@ var DeploymentApiService = class extends import_effect21.Effect.Service()("Deplo
2004
2379
  });
2005
2380
  return result.uploadId;
2006
2381
  });
2007
- const getPresignedUrl = import_effect21.Effect.fn("DeploymentApiService.getPresignedUrl")(function* (deploymentId, uploadId, partNumber) {
2382
+ const getPresignedUrl = import_effect24.Effect.fn("DeploymentApiService.getPresignedUrl")(function* (deploymentId, uploadId, partNumber) {
2008
2383
  const client = yield* getClient;
2009
- const result = yield* import_effect21.Effect.tryPromise({
2384
+ const result = yield* import_effect24.Effect.tryPromise({
2010
2385
  try: () => client.deployments.getPresignedUrl(deploymentId, uploadId, partNumber),
2011
2386
  catch: (error) => new PresignedUrlError({
2012
2387
  message: `Failed to get presigned URL: ${error instanceof Error ? error.message : String(error)}`,
@@ -2015,11 +2390,11 @@ var DeploymentApiService = class extends import_effect21.Effect.Service()("Deplo
2015
2390
  });
2016
2391
  return result.url;
2017
2392
  });
2018
- const uploadPart = import_effect21.Effect.fn("DeploymentApiService.uploadPart")(function* (presignedUrl, data, partNumber) {
2019
- yield* import_effect21.Effect.annotateCurrentSpan("partNumber", partNumber);
2020
- yield* import_effect21.Effect.annotateCurrentSpan("size", data.length);
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);
2021
2396
  const client = yield* getClient;
2022
- const result = yield* import_effect21.Effect.tryPromise({
2397
+ const result = yield* import_effect24.Effect.tryPromise({
2023
2398
  try: () => client.deployments.uploadPart(presignedUrl, data, partNumber),
2024
2399
  catch: (error) => new UploadPartError({
2025
2400
  message: `Failed to upload part ${partNumber}: ${error instanceof Error ? error.message : String(error)}`,
@@ -2028,21 +2403,21 @@ var DeploymentApiService = class extends import_effect21.Effect.Service()("Deplo
2028
2403
  });
2029
2404
  return result;
2030
2405
  });
2031
- const completeUpload = import_effect21.Effect.fn("DeploymentApiService.completeUpload")(function* (deploymentId, uploadId, parts) {
2032
- yield* import_effect21.Effect.annotateCurrentSpan("deploymentId", deploymentId);
2033
- yield* import_effect21.Effect.annotateCurrentSpan("partsCount", parts.length);
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);
2034
2409
  const client = yield* getClient;
2035
- yield* import_effect21.Effect.tryPromise({
2410
+ yield* import_effect24.Effect.tryPromise({
2036
2411
  try: () => client.deployments.completeUpload(deploymentId, uploadId, parts),
2037
2412
  catch: (error) => new UploadCompleteError({
2038
2413
  message: `Failed to complete upload: ${error instanceof Error ? error.message : String(error)}`
2039
2414
  })
2040
2415
  });
2041
- yield* import_effect21.Effect.log("Upload completed successfully");
2416
+ yield* import_effect24.Effect.log("Upload completed successfully");
2042
2417
  });
2043
- const getDeploymentStatus = import_effect21.Effect.fn("DeploymentApiService.getDeploymentStatus")(function* (deploymentId) {
2418
+ const getDeploymentStatus = import_effect24.Effect.fn("DeploymentApiService.getDeploymentStatus")(function* (deploymentId) {
2044
2419
  const client = yield* getClient;
2045
- const deployment = yield* import_effect21.Effect.tryPromise({
2420
+ const deployment = yield* import_effect24.Effect.tryPromise({
2046
2421
  try: () => client.deployments.get(deploymentId),
2047
2422
  catch: (error) => new DeploymentStatusError({
2048
2423
  message: `Failed to get deployment status: ${error instanceof Error ? error.message : String(error)}`
@@ -2050,9 +2425,9 @@ var DeploymentApiService = class extends import_effect21.Effect.Service()("Deplo
2050
2425
  });
2051
2426
  return deployment.status;
2052
2427
  });
2053
- const getLogs = import_effect21.Effect.fn("DeploymentApiService.getLogs")(function* (deploymentId, options) {
2428
+ const getLogs = import_effect24.Effect.fn("DeploymentApiService.getLogs")(function* (deploymentId, options) {
2054
2429
  const client = yield* getClient;
2055
- const result = yield* import_effect21.Effect.tryPromise({
2430
+ const result = yield* import_effect24.Effect.tryPromise({
2056
2431
  try: () => client.deployments.getLogs(deploymentId, options),
2057
2432
  catch: (error) => new DeploymentLogsFetchError({
2058
2433
  message: `Failed to get logs: ${error instanceof Error ? error.message : String(error)}`
@@ -2060,9 +2435,9 @@ var DeploymentApiService = class extends import_effect21.Effect.Service()("Deplo
2060
2435
  });
2061
2436
  return result;
2062
2437
  });
2063
- const getHostnames = import_effect21.Effect.fn("DeploymentApiService.getHostnames")(function* (deploymentId) {
2438
+ const getHostnames = import_effect24.Effect.fn("DeploymentApiService.getHostnames")(function* (deploymentId) {
2064
2439
  const client = yield* getClient;
2065
- const result = yield* import_effect21.Effect.tryPromise({
2440
+ const result = yield* import_effect24.Effect.tryPromise({
2066
2441
  try: () => client.deployments.getHostnames(deploymentId),
2067
2442
  catch: (error) => new DeploymentHostnamesFetchError({
2068
2443
  message: `Failed to get hostnames: ${error instanceof Error ? error.message : String(error)}`
@@ -2093,86 +2468,86 @@ var appOption5 = import_cli11.Options.text("app").pipe(
2093
2468
  var deployCommand = import_cli11.Command.make(
2094
2469
  "deploy",
2095
2470
  { app: appOption5 },
2096
- ({ app }) => import_effect22.Effect.gen(function* () {
2471
+ ({ app }) => import_effect25.Effect.gen(function* () {
2097
2472
  const projectConfig = yield* ProjectConfigService;
2098
2473
  const deploymentApi = yield* DeploymentApiService;
2099
2474
  const archiveService = yield* ArchiveService;
2100
2475
  const projectLink = yield* ProjectLinkService;
2101
- const pathService = yield* import_platform7.Path.Path;
2476
+ const pathService = yield* import_platform9.Path.Path;
2102
2477
  const cwd = process.cwd();
2103
- const applicationId = yield* import_effect22.Option.match(app, {
2104
- onSome: (id) => import_effect22.Effect.succeed(id),
2105
- onNone: () => projectLink.resolve(cwd).pipe(import_effect22.Effect.map((link) => link.applicationId))
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))
2106
2481
  });
2107
- yield* import_effect22.Effect.log("Starting deploy command");
2482
+ yield* import_effect25.Effect.log("Starting deploy command");
2108
2483
  const { config } = yield* projectConfig.resolve(cwd);
2109
- yield* import_effect22.Console.log("Creating deployment...");
2484
+ yield* import_effect25.Console.log("Creating deployment...");
2110
2485
  const deploymentId = yield* deploymentApi.createDeployment(applicationId);
2111
- yield* import_effect22.Console.log(`Deployment ID: ${deploymentId}`);
2112
- yield* import_effect22.Console.log("Creating archive...");
2113
- const archivePath = pathService.join(os2.tmpdir(), `project-${Date.now()}.zip`);
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`);
2114
2489
  const archive = yield* archiveService.createZipArchive(config.userArchive?.rootOverwrite || cwd, archivePath, {
2115
2490
  whitelist: config.userArchive?.fileWhitelist,
2116
2491
  excludeFiles: config.excludeFiles,
2117
2492
  useIgnoreFiles: false,
2118
2493
  useManagedIgnore: false
2119
2494
  });
2120
- yield* import_effect22.Console.log(`Archive created (${(0, import_commons.formatFileSize)(archive.size)})`);
2121
- yield* import_effect22.Console.log("Uploading archive...");
2495
+ yield* import_effect25.Console.log(`Archive created (${(0, import_commons.formatFileSize)(archive.size)})`);
2496
+ yield* import_effect25.Console.log("Uploading archive...");
2122
2497
  yield* uploadArchive(deploymentId, archivePath, archive.size);
2123
- yield* import_effect22.Console.log("Upload complete.");
2124
- yield* import_effect22.Console.log("The deployment pipeline is now being provisioned. This may take a few seconds.");
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.");
2125
2500
  yield* pollDeployment(deploymentId);
2126
2501
  }).pipe(
2127
- import_effect22.Effect.catchTags({
2128
- ProjectNotLinkedError: (err) => import_effect22.Console.error(err.message).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2129
- ProjectLinkReadError: (err) => import_effect22.Console.error(err.message).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2130
- ConfigNotFoundError: (err) => import_effect22.Console.error(err.message).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2131
- ConfigParseError: (err) => import_effect22.Console.error(`Config parse error: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2132
- ConfigValidationError: (err) => import_effect22.Effect.gen(function* () {
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* () {
2133
2508
  for (const e of err.errors) {
2134
- yield* import_effect22.Console.error(e);
2509
+ yield* import_effect25.Console.error(e);
2135
2510
  }
2136
- yield* import_effect22.Console.error(err.message);
2137
- }).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2138
- DeploymentCreateError: (err) => import_effect22.Console.error(`Deployment creation failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2139
- ArchiveCreateError: (err) => import_effect22.Console.error(`Archive creation failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2140
- UploadStartError: (err) => import_effect22.Console.error(`Upload failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2141
- UploadPartError: (err) => import_effect22.Console.error(`Upload failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2142
- UploadCompleteError: (err) => import_effect22.Console.error(`Upload failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2143
- PresignedUrlError: (err) => import_effect22.Console.error(`Upload failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2144
- DeploymentAuthError: (err) => import_effect22.Console.error(`Authentication failed: ${err.message}`).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err))),
2145
- DeploymentFailedError: (err) => import_effect22.Console.error(err.message).pipe(import_effect22.Effect.andThen(import_effect22.Effect.fail(err)))
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)))
2146
2521
  })
2147
2522
  )
2148
2523
  );
2149
- var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect22.Effect.gen(function* () {
2524
+ var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect25.Effect.gen(function* () {
2150
2525
  const deploymentApi = yield* DeploymentApiService;
2151
- const fs = yield* import_platform7.FileSystem.FileSystem;
2526
+ const fs = yield* import_platform9.FileSystem.FileSystem;
2152
2527
  const uploadId = yield* deploymentApi.startMultipartUpload(deploymentId);
2153
2528
  const FILE_CHUNK_SIZE = 10 * 1024 * 1024;
2154
2529
  const numChunks = Math.ceil(fileSize / FILE_CHUNK_SIZE);
2155
- yield* import_effect22.Effect.log("Starting multipart upload", { uploadId, numChunks, fileSize });
2156
- const parts = yield* import_effect22.Effect.all(
2530
+ yield* import_effect25.Effect.log("Starting multipart upload", { uploadId, numChunks, fileSize });
2531
+ const parts = yield* import_effect25.Effect.all(
2157
2532
  Array.from({ length: numChunks }, (_, i) => {
2158
2533
  const partNumber = i + 1;
2159
2534
  const start = i * FILE_CHUNK_SIZE;
2160
2535
  const bytesToRead = partNumber < numChunks ? FILE_CHUNK_SIZE : fileSize - start;
2161
- return import_effect22.Effect.gen(function* () {
2536
+ return import_effect25.Effect.gen(function* () {
2162
2537
  const presignedUrl = yield* deploymentApi.getPresignedUrl(deploymentId, uploadId, partNumber);
2163
- const chunkData = yield* import_effect22.Stream.runCollect(fs.stream(archivePath, { offset: start, bytesToRead })).pipe(
2164
- import_effect22.Effect.map((chunks) => {
2538
+ const chunkData = yield* import_effect25.Stream.runCollect(fs.stream(archivePath, { offset: start, bytesToRead })).pipe(
2539
+ import_effect25.Effect.map((chunks) => {
2165
2540
  const arrays = Array.from(chunks);
2166
2541
  return Buffer.concat(arrays);
2167
2542
  }),
2168
- import_effect22.Effect.mapError(
2543
+ import_effect25.Effect.mapError(
2169
2544
  (error) => new UploadPartError({
2170
2545
  message: `Failed to read chunk ${partNumber}: ${String(error)}`,
2171
2546
  partNumber
2172
2547
  })
2173
2548
  )
2174
2549
  );
2175
- yield* import_effect22.Effect.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
2550
+ yield* import_effect25.Effect.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
2176
2551
  return yield* deploymentApi.uploadPart(presignedUrl, chunkData, partNumber);
2177
2552
  });
2178
2553
  }),
@@ -2180,20 +2555,20 @@ var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect22.Eff
2180
2555
  );
2181
2556
  yield* deploymentApi.completeUpload(deploymentId, uploadId, parts);
2182
2557
  });
2183
- var pollDeployment = (deploymentId) => import_effect22.Effect.gen(function* () {
2558
+ var pollDeployment = (deploymentId) => import_effect25.Effect.gen(function* () {
2184
2559
  const deploymentApi = yield* DeploymentApiService;
2185
2560
  let lastStatus = "PENDING";
2186
2561
  let logOffset = 0;
2187
- yield* import_effect22.Console.log("Status: Pending");
2188
- yield* import_effect22.Effect.repeat(
2189
- import_effect22.Effect.gen(function* () {
2190
- const newStatus = yield* deploymentApi.getDeploymentStatus(deploymentId).pipe(import_effect22.Effect.catchTag("DeploymentStatusError", () => import_effect22.Effect.succeed(lastStatus)));
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)));
2191
2566
  if (newStatus !== lastStatus) {
2192
2567
  lastStatus = newStatus;
2193
2568
  const label = newStatus.charAt(0) + newStatus.slice(1).toLowerCase();
2194
- yield* import_effect22.Console.log(`Status: ${label}`);
2569
+ yield* import_effect25.Console.log(`Status: ${label}`);
2195
2570
  }
2196
- const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(import_effect22.Effect.catchTag("DeploymentLogsFetchError", () => import_effect22.Effect.succeed(null)));
2571
+ const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(import_effect25.Effect.catchTag("DeploymentLogsFetchError", () => import_effect25.Effect.succeed(null)));
2197
2572
  if (logsPage) {
2198
2573
  for (const logEntry of logsPage.items) {
2199
2574
  logOffset++;
@@ -2201,68 +2576,211 @@ var pollDeployment = (deploymentId) => import_effect22.Effect.gen(function* () {
2201
2576
  }
2202
2577
  }
2203
2578
  if (newStatus === "ACTIVE") {
2204
- const hostnames = yield* deploymentApi.getHostnames(deploymentId).pipe(import_effect22.Effect.catchTag("DeploymentHostnamesFetchError", () => import_effect22.Effect.succeed([])));
2579
+ const hostnames = yield* deploymentApi.getHostnames(deploymentId).pipe(import_effect25.Effect.catchTag("DeploymentHostnamesFetchError", () => import_effect25.Effect.succeed([])));
2205
2580
  const active = hostnames.find((h) => h.active) ?? hostnames.at(0);
2206
2581
  if (active) {
2207
- yield* import_effect22.Console.log(`Deployed to https://${active.hostname}`);
2582
+ yield* import_effect25.Console.log(`Deployed to https://${active.hostname}`);
2208
2583
  } else {
2209
- yield* import_effect22.Console.log("Deployment is live.");
2584
+ yield* import_effect25.Console.log("Deployment is live.");
2210
2585
  }
2211
- return yield* import_effect22.Effect.fail("done");
2586
+ return yield* import_effect25.Effect.fail("done");
2212
2587
  }
2213
2588
  if (newStatus === "FAILED") {
2214
- return yield* import_effect22.Effect.fail(
2589
+ return yield* import_effect25.Effect.fail(
2215
2590
  new DeploymentFailedError({
2216
2591
  message: "The deployment failed. Please check the logs for more information."
2217
2592
  })
2218
2593
  );
2219
2594
  }
2220
2595
  }),
2221
- import_effect22.Schedule.addDelay(import_effect22.Schedule.forever, () => import_effect22.Duration.seconds(1))
2596
+ import_effect25.Schedule.addDelay(import_effect25.Schedule.forever, () => import_effect25.Duration.seconds(1))
2222
2597
  ).pipe(
2223
- import_effect22.Effect.catchAll((err) => {
2224
- if (err === "done") return import_effect22.Effect.void;
2225
- return import_effect22.Effect.fail(err);
2598
+ import_effect25.Effect.catchAll((err) => {
2599
+ if (err === "done") return import_effect25.Effect.void;
2600
+ return import_effect25.Effect.fail(err);
2226
2601
  })
2227
2602
  );
2228
2603
  });
2229
2604
  var printLog = (logEntry) => {
2230
2605
  switch (logEntry.type) {
2231
2606
  case "ERROR":
2232
- return import_effect22.Console.error(logEntry.message);
2607
+ return import_effect25.Console.error(logEntry.message);
2233
2608
  case "WARN":
2234
- return import_effect22.Console.warn(logEntry.message);
2609
+ return import_effect25.Console.warn(logEntry.message);
2235
2610
  default:
2236
- return import_effect22.Console.log(logEntry.message);
2611
+ return import_effect25.Console.log(logEntry.message);
2237
2612
  }
2238
2613
  };
2239
2614
 
2240
2615
  // src/commands/platform/index.ts
2241
- var platformBase = import_cli12.Command.make("platform", {}, () => import_effect23.Effect.void);
2616
+ var platformBase = import_cli12.Command.make("platform", {}, () => import_effect26.Effect.void);
2242
2617
  var platformCommand = platformBase.pipe(import_cli12.Command.withSubcommands([deployCommand]));
2243
2618
 
2244
- // src/commands/whoami/index.ts
2619
+ // src/commands/setup/index.ts
2245
2620
  var import_cli13 = require("@effect/cli");
2246
- var import_effect24 = require("effect");
2247
- var whoamiCommand = import_cli13.Command.make(
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
+ );
2761
+
2762
+ // src/commands/whoami/index.ts
2763
+ var import_cli14 = require("@effect/cli");
2764
+ var import_effect28 = require("effect");
2765
+ var whoamiCommand = import_cli14.Command.make(
2248
2766
  "whoami",
2249
2767
  {},
2250
- () => import_effect24.Effect.gen(function* () {
2768
+ () => import_effect28.Effect.gen(function* () {
2251
2769
  const auth = yield* AuthService;
2252
2770
  const info = yield* auth.getUserInfo;
2253
- yield* import_effect24.Console.log(JSON.stringify(info, null, 2));
2771
+ yield* import_effect28.Console.log(JSON.stringify(info, null, 2));
2254
2772
  }).pipe(
2255
- import_effect24.Effect.catchTags({
2256
- NotAuthenticatedError: () => import_effect24.Console.log('You are not logged in. Run "gigadrive login" to authenticate.'),
2257
- UserInfoFetchError: (err) => import_effect24.Console.error(`Failed to retrieve user info: ${err.message}`),
2258
- OAuthDiscoveryError: (err) => import_effect24.Console.error(`Failed to retrieve user info: ${err.message}`)
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}`)
2259
2777
  })
2260
2778
  )
2261
2779
  );
2262
2780
 
2263
2781
  // src/index.ts
2264
- var gigadrive = import_cli14.Command.make("gigadrive", {}, () => import_effect25.Effect.void).pipe(
2265
- import_cli14.Command.withSubcommands([
2782
+ var gigadrive = import_cli15.Command.make("gigadrive", {}, () => import_effect29.Effect.void).pipe(
2783
+ import_cli15.Command.withSubcommands([
2266
2784
  loginCommand,
2267
2785
  logoutCommand,
2268
2786
  whoamiCommand,
@@ -2274,27 +2792,31 @@ var gigadrive = import_cli14.Command.make("gigadrive", {}, () => import_effect25
2274
2792
  aiCommand,
2275
2793
  buildCommand,
2276
2794
  debugCommand,
2277
- platformCommand
2795
+ platformCommand,
2796
+ setupCommand
2278
2797
  ])
2279
2798
  );
2280
- var cli = import_cli14.Command.run(gigadrive, {
2799
+ var cli = import_cli15.Command.run(gigadrive, {
2281
2800
  name: "Gigadrive CLI",
2282
2801
  version: package_default.version
2283
2802
  });
2284
- var BaseServices = import_effect25.Layer.mergeAll(
2803
+ var BaseServices = import_effect29.Layer.mergeAll(
2285
2804
  OAuthConfigService.Default,
2286
2805
  AuthStorageService.Default,
2287
2806
  PackageManagerService.Default,
2288
2807
  ArchiveService.Default,
2289
2808
  import_network_config2.NetworkConfigLive,
2290
2809
  ProjectConfigService.Default,
2291
- ProjectLinkService.Default
2292
- ).pipe(import_effect25.Layer.provideMerge(AuthService.Default));
2293
- var ApiClientLive = import_effect25.Layer.provide(ApiClientService.Default, BaseServices);
2294
- var DeploymentApiLive = import_effect25.Layer.provide(DeploymentApiService.Default, BaseServices);
2295
- var ServicesLive = import_effect25.Layer.mergeAll(BaseServices, ApiClientLive, DeploymentApiLive);
2296
- var AppLive = import_effect25.Layer.mergeAll(ServicesLive, import_effect25.Logger.minimumLogLevel(import_effect25.LogLevel.Info)).pipe(
2297
- import_effect25.Layer.provideMerge(import_platform_node.NodeContext.layer)
2298
- );
2299
- import_platform_node.NodeRuntime.runMain(import_effect25.Effect.provide(cli(process.argv), AppLive));
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));
2300
2822
  //# sourceMappingURL=index.cjs.map