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 +745 -223
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +706 -184
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command15 } from "@effect/cli";
|
|
5
5
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
6
6
|
import { NetworkConfigLive as NetworkConfigLive2 } from "@gigadrive/network-config";
|
|
7
|
-
import { Effect as
|
|
7
|
+
import { Effect as Effect27, Layer, LogLevel, Logger } from "effect";
|
|
8
8
|
|
|
9
9
|
// package.json
|
|
10
10
|
var package_default = {
|
|
11
11
|
name: "gigadrive",
|
|
12
|
-
version: "2.
|
|
12
|
+
version: "2.3.0",
|
|
13
13
|
repository: {
|
|
14
14
|
type: "git",
|
|
15
15
|
url: "https://github.com/Gigadrive/sdk.git",
|
|
@@ -135,6 +135,31 @@ var ApplicationNotFoundError = class extends Schema.TaggedError()(
|
|
|
135
135
|
}
|
|
136
136
|
) {
|
|
137
137
|
};
|
|
138
|
+
var EnvFileReadError = class extends Schema.TaggedError()("EnvFileReadError", {
|
|
139
|
+
message: Schema.String,
|
|
140
|
+
cause: Schema.optional(Schema.String)
|
|
141
|
+
}) {
|
|
142
|
+
};
|
|
143
|
+
var EnvFileWriteError = class extends Schema.TaggedError()("EnvFileWriteError", {
|
|
144
|
+
message: Schema.String,
|
|
145
|
+
cause: Schema.optional(Schema.String)
|
|
146
|
+
}) {
|
|
147
|
+
};
|
|
148
|
+
var DevCredentialsStoreReadError = class extends Schema.TaggedError()(
|
|
149
|
+
"DevCredentialsStoreReadError",
|
|
150
|
+
{
|
|
151
|
+
message: Schema.String
|
|
152
|
+
}
|
|
153
|
+
) {
|
|
154
|
+
};
|
|
155
|
+
var DevCredentialsStoreWriteError = class extends Schema.TaggedError()(
|
|
156
|
+
"DevCredentialsStoreWriteError",
|
|
157
|
+
{
|
|
158
|
+
message: Schema.String,
|
|
159
|
+
cause: Schema.optional(Schema.String)
|
|
160
|
+
}
|
|
161
|
+
) {
|
|
162
|
+
};
|
|
138
163
|
var ConfigNotFoundError = class extends Schema.TaggedError()("ConfigNotFoundError", {
|
|
139
164
|
message: Schema.String,
|
|
140
165
|
directory: Schema.String
|
|
@@ -300,6 +325,10 @@ var ProjectLink = Schema2.Struct({
|
|
|
300
325
|
applicationId: Schema2.String,
|
|
301
326
|
organizationId: Schema2.optional(Schema2.String)
|
|
302
327
|
});
|
|
328
|
+
var DevKeyEntry = Schema2.Struct({
|
|
329
|
+
apiKeyId: Schema2.String
|
|
330
|
+
});
|
|
331
|
+
var DevKeyStore = Schema2.Record({ key: Schema2.String, value: DevKeyEntry });
|
|
303
332
|
|
|
304
333
|
// src/services/auth-storage.ts
|
|
305
334
|
var AuthStorageService = class extends Effect.Service()("AuthStorageService", {
|
|
@@ -380,7 +409,11 @@ var AuthStorageService = class extends Effect.Service()("AuthStorageService", {
|
|
|
380
409
|
import { Config, Effect as Effect2, Schema as Schema4 } from "effect";
|
|
381
410
|
var OAuthEnvConfig = Config.all({
|
|
382
411
|
issuerUrl: Config.string("GIGADRIVE_NETWORK_OAUTH_ISSUER_URL").pipe(Config.withDefault("https://idp.gigadrive.de")),
|
|
383
|
-
|
|
412
|
+
// Default is the first-party CLI's public OAuth client (seeded in the Network
|
|
413
|
+
// repo). Override with GIGADRIVE_NETWORK_OAUTH_CLIENT_ID per environment.
|
|
414
|
+
clientId: Config.string("GIGADRIVE_NETWORK_OAUTH_CLIENT_ID").pipe(
|
|
415
|
+
Config.withDefault("0195c11c-0000-7000-8000-000000000001")
|
|
416
|
+
)
|
|
384
417
|
});
|
|
385
418
|
var OpenIdDiscoveryDocument = Schema4.Struct({
|
|
386
419
|
issuer: Schema4.optional(Schema4.String),
|
|
@@ -388,7 +421,33 @@ var OpenIdDiscoveryDocument = Schema4.Struct({
|
|
|
388
421
|
token_endpoint: Schema4.optional(Schema4.String),
|
|
389
422
|
userinfo_endpoint: Schema4.optional(Schema4.String)
|
|
390
423
|
});
|
|
391
|
-
var
|
|
424
|
+
var CLI_SCOPES = [
|
|
425
|
+
// Identity / OIDC
|
|
426
|
+
"offline_access",
|
|
427
|
+
"openid",
|
|
428
|
+
"profile",
|
|
429
|
+
"email",
|
|
430
|
+
// Network API capabilities
|
|
431
|
+
"network:applications:read",
|
|
432
|
+
"network:env_vars:read",
|
|
433
|
+
"network:env_vars:write",
|
|
434
|
+
"network:env_vars:delete",
|
|
435
|
+
"network:deployments:read",
|
|
436
|
+
"network:deployments:write",
|
|
437
|
+
"network:deployments:trigger",
|
|
438
|
+
"network:ai_gateway:chat",
|
|
439
|
+
"network:ai_gateway:models",
|
|
440
|
+
"network:ai_gateway:usage:read",
|
|
441
|
+
"network:ai_gateway:budgets:read",
|
|
442
|
+
"network:ai_gateway:budgets:write",
|
|
443
|
+
"network:ai_gateway:policies:read",
|
|
444
|
+
"network:ai_gateway:policies:write",
|
|
445
|
+
// Provisioning local-dev API credentials
|
|
446
|
+
"platform:api_keys:read",
|
|
447
|
+
"platform:api_keys:write",
|
|
448
|
+
"platform:api_keys:delete"
|
|
449
|
+
];
|
|
450
|
+
var DEFAULT_SCOPE = CLI_SCOPES.join(" ");
|
|
392
451
|
var OAuthConfigService = class extends Effect2.Service()("OAuthConfigService", {
|
|
393
452
|
accessors: true,
|
|
394
453
|
effect: Effect2.gen(function* () {
|
|
@@ -1539,8 +1598,231 @@ var deploymentsCommand = deploymentsBase.pipe(
|
|
|
1539
1598
|
);
|
|
1540
1599
|
|
|
1541
1600
|
// src/commands/env/index.ts
|
|
1542
|
-
import { Args as Args3, Command as Command7, Options as Options4 } from "@effect/cli";
|
|
1543
|
-
import { Console as Console7, Effect as
|
|
1601
|
+
import { Args as Args3, Command as Command7, Options as Options4, Prompt } from "@effect/cli";
|
|
1602
|
+
import { Console as Console7, Effect as Effect17, Option as Option10 } from "effect";
|
|
1603
|
+
|
|
1604
|
+
// src/services/env-file.ts
|
|
1605
|
+
import { FileSystem as FileSystem6, Path as Path5 } from "@effect/platform";
|
|
1606
|
+
import { Effect as Effect14, Option as Option7 } from "effect";
|
|
1607
|
+
|
|
1608
|
+
// src/lib/dotenv.ts
|
|
1609
|
+
var DEFAULT_ENV_HEADER = "# Generated by `gigadrive env pull` \u2014 do not edit by hand.";
|
|
1610
|
+
var UNQUOTED_SAFE = /^[A-Za-z0-9_.\-/@:+]*$/;
|
|
1611
|
+
var formatEnvValue = (value) => {
|
|
1612
|
+
if (UNQUOTED_SAFE.test(value)) {
|
|
1613
|
+
return value;
|
|
1614
|
+
}
|
|
1615
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
1616
|
+
return `"${escaped}"`;
|
|
1617
|
+
};
|
|
1618
|
+
var serializeEnv = (entries, opts) => {
|
|
1619
|
+
const header = opts?.header ?? DEFAULT_ENV_HEADER;
|
|
1620
|
+
if (entries.length === 0) {
|
|
1621
|
+
return `${header}
|
|
1622
|
+
`;
|
|
1623
|
+
}
|
|
1624
|
+
const body = entries.map((e) => `${e.key}=${formatEnvValue(e.value)}`).join("\n");
|
|
1625
|
+
return `${header}
|
|
1626
|
+
${body}
|
|
1627
|
+
`;
|
|
1628
|
+
};
|
|
1629
|
+
var parseEnv = (content) => {
|
|
1630
|
+
const result = {};
|
|
1631
|
+
for (const rawLine of content.split("\n")) {
|
|
1632
|
+
const line = rawLine.trim();
|
|
1633
|
+
if (line.length === 0 || line.startsWith("#")) {
|
|
1634
|
+
continue;
|
|
1635
|
+
}
|
|
1636
|
+
const eq = line.indexOf("=");
|
|
1637
|
+
if (eq <= 0) {
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
const key = line.slice(0, eq).trim();
|
|
1641
|
+
let value = line.slice(eq + 1).trim();
|
|
1642
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
1643
|
+
value = value.slice(1, -1).replace(/\\(\\|"|n|r|t)/g, (_, ch) => {
|
|
1644
|
+
switch (ch) {
|
|
1645
|
+
case "n":
|
|
1646
|
+
return "\n";
|
|
1647
|
+
case "r":
|
|
1648
|
+
return "\r";
|
|
1649
|
+
case "t":
|
|
1650
|
+
return " ";
|
|
1651
|
+
case '"':
|
|
1652
|
+
return '"';
|
|
1653
|
+
default:
|
|
1654
|
+
return "\\";
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
result[key] = value;
|
|
1659
|
+
}
|
|
1660
|
+
return result;
|
|
1661
|
+
};
|
|
1662
|
+
|
|
1663
|
+
// src/services/env-file.ts
|
|
1664
|
+
var EnvFileService = class extends Effect14.Service()("EnvFileService", {
|
|
1665
|
+
accessors: true,
|
|
1666
|
+
effect: Effect14.gen(function* () {
|
|
1667
|
+
const fs = yield* FileSystem6.FileSystem;
|
|
1668
|
+
const pathService = yield* Path5.Path;
|
|
1669
|
+
const exists = Effect14.fn("EnvFileService.exists")(function* (filePath) {
|
|
1670
|
+
return yield* fs.exists(filePath).pipe(Effect14.mapError(() => new EnvFileReadError({ message: `Failed to check whether ${filePath} exists` })));
|
|
1671
|
+
});
|
|
1672
|
+
const read = Effect14.fn("EnvFileService.read")(function* (filePath) {
|
|
1673
|
+
const present = yield* exists(filePath);
|
|
1674
|
+
if (!present) return Option7.none();
|
|
1675
|
+
const contents = yield* fs.readFileString(filePath, "utf8").pipe(Effect14.mapError(() => new EnvFileReadError({ message: `Failed to read ${filePath}` })));
|
|
1676
|
+
return Option7.some(contents);
|
|
1677
|
+
});
|
|
1678
|
+
const write = Effect14.fn("EnvFileService.write")(function* (filePath, entries, opts) {
|
|
1679
|
+
const dir = pathService.dirname(filePath);
|
|
1680
|
+
yield* fs.makeDirectory(dir, { recursive: true }).pipe(
|
|
1681
|
+
Effect14.mapError(
|
|
1682
|
+
(error) => new EnvFileWriteError({ message: `Failed to create directory for ${filePath}`, cause: String(error) })
|
|
1683
|
+
)
|
|
1684
|
+
);
|
|
1685
|
+
yield* fs.writeFileString(filePath, serializeEnv(entries, opts), { mode: 384 }).pipe(
|
|
1686
|
+
Effect14.mapError(
|
|
1687
|
+
(error) => new EnvFileWriteError({ message: `Failed to write ${filePath}`, cause: String(error) })
|
|
1688
|
+
)
|
|
1689
|
+
);
|
|
1690
|
+
yield* fs.chmod(filePath, 384).pipe(
|
|
1691
|
+
Effect14.mapError(
|
|
1692
|
+
(error) => new EnvFileWriteError({ message: `Failed to secure ${filePath}`, cause: String(error) })
|
|
1693
|
+
)
|
|
1694
|
+
);
|
|
1695
|
+
});
|
|
1696
|
+
const ensureGitignored = Effect14.fn("EnvFileService.ensureGitignored")(function* (dir, entry) {
|
|
1697
|
+
const gitignorePath = pathService.join(dir, ".gitignore");
|
|
1698
|
+
const existing = yield* read(gitignorePath);
|
|
1699
|
+
const current = Option7.getOrElse(existing, () => "");
|
|
1700
|
+
const alreadyIgnored = current.split("\n").some((line) => line.trim() === entry);
|
|
1701
|
+
if (alreadyIgnored) {
|
|
1702
|
+
return false;
|
|
1703
|
+
}
|
|
1704
|
+
const separator = current.length > 0 && !current.endsWith("\n") ? "\n" : "";
|
|
1705
|
+
yield* fs.writeFileString(gitignorePath, `${current}${separator}${entry}
|
|
1706
|
+
`).pipe(
|
|
1707
|
+
Effect14.mapError(
|
|
1708
|
+
(error) => new EnvFileWriteError({ message: "Failed to update .gitignore", cause: String(error) })
|
|
1709
|
+
)
|
|
1710
|
+
);
|
|
1711
|
+
return true;
|
|
1712
|
+
});
|
|
1713
|
+
return { exists, read, write, ensureGitignored };
|
|
1714
|
+
})
|
|
1715
|
+
}) {
|
|
1716
|
+
};
|
|
1717
|
+
|
|
1718
|
+
// src/services/local-credentials.ts
|
|
1719
|
+
import { Config as Config3, Effect as Effect16, Option as Option9 } from "effect";
|
|
1720
|
+
import * as os3 from "os";
|
|
1721
|
+
|
|
1722
|
+
// src/services/dev-credentials-store.ts
|
|
1723
|
+
import { FileSystem as FileSystem7, Path as Path6 } from "@effect/platform";
|
|
1724
|
+
import { Effect as Effect15, Option as Option8, Schema as Schema6 } from "effect";
|
|
1725
|
+
import * as os2 from "os";
|
|
1726
|
+
var DevCredentialsStore = class extends Effect15.Service()("DevCredentialsStore", {
|
|
1727
|
+
accessors: true,
|
|
1728
|
+
effect: Effect15.gen(function* () {
|
|
1729
|
+
const fs = yield* FileSystem7.FileSystem;
|
|
1730
|
+
const pathService = yield* Path6.Path;
|
|
1731
|
+
const homeDir = os2.homedir();
|
|
1732
|
+
const storeDir = pathService.join(homeDir, ".gigadrive");
|
|
1733
|
+
const storeFile = pathService.join(storeDir, "dev-keys.json");
|
|
1734
|
+
const ensureDirectory = Effect15.gen(function* () {
|
|
1735
|
+
yield* fs.makeDirectory(storeDir, { recursive: true }).pipe(
|
|
1736
|
+
Effect15.mapError(
|
|
1737
|
+
(error) => new DevCredentialsStoreWriteError({ message: "Failed to create ~/.gigadrive", cause: String(error) })
|
|
1738
|
+
)
|
|
1739
|
+
);
|
|
1740
|
+
yield* fs.chmod(storeDir, 448).pipe(
|
|
1741
|
+
Effect15.mapError(
|
|
1742
|
+
(error) => new DevCredentialsStoreWriteError({
|
|
1743
|
+
message: "Failed to set ~/.gigadrive permissions",
|
|
1744
|
+
cause: String(error)
|
|
1745
|
+
})
|
|
1746
|
+
)
|
|
1747
|
+
);
|
|
1748
|
+
});
|
|
1749
|
+
const load = Effect15.gen(function* () {
|
|
1750
|
+
const present = yield* fs.exists(storeFile).pipe(Effect15.mapError(() => new DevCredentialsStoreReadError({ message: "Failed to check dev-keys.json" })));
|
|
1751
|
+
if (!present) return {};
|
|
1752
|
+
const data = yield* fs.readFileString(storeFile, "utf8").pipe(Effect15.mapError(() => new DevCredentialsStoreReadError({ message: "Failed to read dev-keys.json" })));
|
|
1753
|
+
const json = yield* Effect15.try({
|
|
1754
|
+
try: () => JSON.parse(data),
|
|
1755
|
+
catch: () => new DevCredentialsStoreReadError({ message: "dev-keys.json contains invalid JSON" })
|
|
1756
|
+
});
|
|
1757
|
+
return yield* Schema6.decodeUnknown(DevKeyStore)(json).pipe(
|
|
1758
|
+
Effect15.mapError(() => new DevCredentialsStoreReadError({ message: "dev-keys.json has an invalid schema" }))
|
|
1759
|
+
);
|
|
1760
|
+
});
|
|
1761
|
+
const get = Effect15.fn("DevCredentialsStore.get")(function* (applicationId) {
|
|
1762
|
+
const store = yield* load;
|
|
1763
|
+
const entry = store[applicationId];
|
|
1764
|
+
return entry ? Option8.some(entry) : Option8.none();
|
|
1765
|
+
});
|
|
1766
|
+
const set = Effect15.fn("DevCredentialsStore.set")(function* (applicationId, entry) {
|
|
1767
|
+
const store = yield* load;
|
|
1768
|
+
const next = { ...store, [applicationId]: entry };
|
|
1769
|
+
yield* ensureDirectory;
|
|
1770
|
+
yield* fs.writeFileString(storeFile, `${JSON.stringify(next, null, 2)}
|
|
1771
|
+
`, { mode: 384 }).pipe(
|
|
1772
|
+
Effect15.mapError(
|
|
1773
|
+
(error) => new DevCredentialsStoreWriteError({ message: "Failed to write dev-keys.json", cause: String(error) })
|
|
1774
|
+
)
|
|
1775
|
+
);
|
|
1776
|
+
});
|
|
1777
|
+
return { get, set };
|
|
1778
|
+
})
|
|
1779
|
+
}) {
|
|
1780
|
+
};
|
|
1781
|
+
|
|
1782
|
+
// src/services/local-credentials.ts
|
|
1783
|
+
var DEV_KEY_SCOPES = ["network:env_vars:read"];
|
|
1784
|
+
var ApiBaseUrl2 = Config3.string("GIGADRIVE_API_BASE_URL").pipe(Config3.withDefault("https://api.gigadrive.network"));
|
|
1785
|
+
var credentialEntries = (clientId, clientSecret, baseUrl) => [
|
|
1786
|
+
{ key: "GIGADRIVE_CLIENT_ID", value: clientId },
|
|
1787
|
+
{ key: "GIGADRIVE_CLIENT_SECRET", value: clientSecret },
|
|
1788
|
+
{ key: "GIGADRIVE_API_BASE_URL", value: baseUrl }
|
|
1789
|
+
];
|
|
1790
|
+
var LocalCredentialsService = class extends Effect16.Service()("LocalCredentialsService", {
|
|
1791
|
+
accessors: true,
|
|
1792
|
+
dependencies: [ApiClientService.Default, DevCredentialsStore.Default],
|
|
1793
|
+
effect: Effect16.gen(function* () {
|
|
1794
|
+
const apiClient = yield* ApiClientService;
|
|
1795
|
+
const store = yield* DevCredentialsStore;
|
|
1796
|
+
const baseUrl = yield* ApiBaseUrl2;
|
|
1797
|
+
const keyName = (applicationId) => `cli-dev:${os3.hostname()}:${applicationId}`;
|
|
1798
|
+
const provision = Effect16.fn("LocalCredentialsService.provision")(function* (input) {
|
|
1799
|
+
const stored = yield* store.get(input.applicationId);
|
|
1800
|
+
const existing = input.existingContent ? parseEnv(input.existingContent) : {};
|
|
1801
|
+
const existingClientId = existing["GIGADRIVE_CLIENT_ID"];
|
|
1802
|
+
const existingClientSecret = existing["GIGADRIVE_CLIENT_SECRET"];
|
|
1803
|
+
const canReuse = !input.rotate && existingClientId !== void 0 && existingClientSecret !== void 0 && Option9.isSome(stored) && stored.value.apiKeyId === existingClientId;
|
|
1804
|
+
if (canReuse) {
|
|
1805
|
+
return credentialEntries(existingClientId, existingClientSecret, baseUrl);
|
|
1806
|
+
}
|
|
1807
|
+
if (Option9.isSome(stored)) {
|
|
1808
|
+
yield* apiClient.request((client) => client.apiKeys.delete(stored.value.apiKeyId)).pipe(Effect16.catchTag("ApiRequestError", () => Effect16.void));
|
|
1809
|
+
}
|
|
1810
|
+
const created = yield* apiClient.request(
|
|
1811
|
+
(client) => client.apiKeys.create({
|
|
1812
|
+
name: keyName(input.applicationId),
|
|
1813
|
+
applicationId: input.applicationId,
|
|
1814
|
+
scopes: DEV_KEY_SCOPES
|
|
1815
|
+
})
|
|
1816
|
+
);
|
|
1817
|
+
yield* store.set(input.applicationId, { apiKeyId: created.id });
|
|
1818
|
+
return credentialEntries(created.id, created.secret, baseUrl);
|
|
1819
|
+
});
|
|
1820
|
+
return { provision };
|
|
1821
|
+
})
|
|
1822
|
+
}) {
|
|
1823
|
+
};
|
|
1824
|
+
|
|
1825
|
+
// src/commands/env/index.ts
|
|
1544
1826
|
var appOption3 = Options4.text("app").pipe(
|
|
1545
1827
|
Options4.withAlias("a"),
|
|
1546
1828
|
Options4.withDescription("Operate on this application (overrides the linked application)"),
|
|
@@ -1563,21 +1845,40 @@ var keyValueArg = Args3.text({ name: "key=value" }).pipe(
|
|
|
1563
1845
|
Args3.withDescription("Variable assignment, e.g. DATABASE_URL=postgres://user:pass@host/db")
|
|
1564
1846
|
);
|
|
1565
1847
|
var keyOrIdArg = Args3.text({ name: "key-or-id" }).pipe(Args3.withDescription("Variable key or ID to remove"));
|
|
1566
|
-
var
|
|
1567
|
-
|
|
1848
|
+
var fileArg = Args3.text({ name: "file" }).pipe(
|
|
1849
|
+
Args3.withDescription("Output file to write (default: .env.local)"),
|
|
1850
|
+
Args3.optional
|
|
1851
|
+
);
|
|
1852
|
+
var environmentOption = Options4.text("environment").pipe(
|
|
1853
|
+
Options4.withAlias("e"),
|
|
1854
|
+
Options4.withDescription("Environment slug to resolve overrides for (e.g. preview). Omit for the local baseline"),
|
|
1855
|
+
Options4.optional
|
|
1856
|
+
);
|
|
1857
|
+
var yesOption = Options4.boolean("yes").pipe(
|
|
1858
|
+
Options4.withAlias("y"),
|
|
1859
|
+
Options4.withDescription("Overwrite an existing file without confirmation")
|
|
1860
|
+
);
|
|
1861
|
+
var withCredentialsOption = Options4.boolean("with-credentials").pipe(
|
|
1862
|
+
Options4.withDescription("Also provision API-key credentials so the local app can call the API as this application")
|
|
1863
|
+
);
|
|
1864
|
+
var rotateOption = Options4.boolean("rotate").pipe(
|
|
1865
|
+
Options4.withDescription("Force a fresh API key when provisioning credentials, revoking the previous one")
|
|
1866
|
+
);
|
|
1867
|
+
var resolveScope = (app, org) => Effect17.gen(function* () {
|
|
1868
|
+
if (Option10.isSome(org)) {
|
|
1568
1869
|
return { kind: "organization", id: org.value };
|
|
1569
1870
|
}
|
|
1570
1871
|
const projectLink = yield* ProjectLinkService;
|
|
1571
|
-
const id = yield*
|
|
1572
|
-
onSome: (value) =>
|
|
1573
|
-
onNone: () => projectLink.resolve(process.cwd()).pipe(
|
|
1872
|
+
const id = yield* Option10.match(app, {
|
|
1873
|
+
onSome: (value) => Effect17.succeed(value),
|
|
1874
|
+
onNone: () => projectLink.resolve(process.cwd()).pipe(Effect17.map((link) => link.applicationId))
|
|
1574
1875
|
});
|
|
1575
1876
|
return { kind: "application", id };
|
|
1576
1877
|
});
|
|
1577
1878
|
var envListCommand = Command7.make(
|
|
1578
1879
|
"list",
|
|
1579
1880
|
{ app: appOption3, org: orgOption4 },
|
|
1580
|
-
({ app, org }) =>
|
|
1881
|
+
({ app, org }) => Effect17.gen(function* () {
|
|
1581
1882
|
const apiClient = yield* ApiClientService;
|
|
1582
1883
|
const scope = yield* resolveScope(app, org);
|
|
1583
1884
|
const { items } = yield* apiClient.request(
|
|
@@ -1591,23 +1892,23 @@ var envListCommand = Command7.make(
|
|
|
1591
1892
|
yield* Console7.log(`${v.key}=${v.sensitive ? "***" : v.value ?? ""}`);
|
|
1592
1893
|
}
|
|
1593
1894
|
}).pipe(
|
|
1594
|
-
|
|
1895
|
+
Effect17.catchTags({
|
|
1595
1896
|
NotAuthenticatedError: (err) => Console7.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
|
|
1596
|
-
|
|
1897
|
+
Effect17.andThen(Effect17.fail(err))
|
|
1597
1898
|
),
|
|
1598
|
-
ApiRequestError: (err) => Console7.error(`Failed to list environment variables: ${err.message}`).pipe(
|
|
1599
|
-
ProjectNotLinkedError: (err) => Console7.error(err.message).pipe(
|
|
1600
|
-
ProjectLinkReadError: (err) => Console7.error(err.message).pipe(
|
|
1899
|
+
ApiRequestError: (err) => Console7.error(`Failed to list environment variables: ${err.message}`).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
1900
|
+
ProjectNotLinkedError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
1901
|
+
ProjectLinkReadError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err)))
|
|
1601
1902
|
})
|
|
1602
1903
|
)
|
|
1603
1904
|
);
|
|
1604
1905
|
var envSetCommand = Command7.make(
|
|
1605
1906
|
"set",
|
|
1606
1907
|
{ keyValue: keyValueArg, app: appOption3, org: orgOption4, sensitive: sensitiveOption, env: envIdOption },
|
|
1607
|
-
({ keyValue, app, org, sensitive, env }) =>
|
|
1908
|
+
({ keyValue, app, org, sensitive, env }) => Effect17.gen(function* () {
|
|
1608
1909
|
const separator = keyValue.indexOf("=");
|
|
1609
1910
|
if (separator <= 0) {
|
|
1610
|
-
return yield*
|
|
1911
|
+
return yield* Effect17.fail(
|
|
1611
1912
|
new InvalidEnvVarFormatError({
|
|
1612
1913
|
message: 'Invalid format. Use KEY=VALUE, e.g. "gigadrive env set DATABASE_URL=postgres://...".'
|
|
1613
1914
|
})
|
|
@@ -1617,27 +1918,27 @@ var envSetCommand = Command7.make(
|
|
|
1617
1918
|
const value = keyValue.slice(separator + 1);
|
|
1618
1919
|
const apiClient = yield* ApiClientService;
|
|
1619
1920
|
const scope = yield* resolveScope(app, org);
|
|
1620
|
-
const environmentId =
|
|
1921
|
+
const environmentId = Option10.getOrUndefined(env);
|
|
1621
1922
|
const created = yield* apiClient.request(
|
|
1622
1923
|
(client) => scope.kind === "organization" ? client.organizations.envVars.create(scope.id, { key, value, sensitive }) : client.applications.envVars.create(scope.id, { key, value, sensitive, environmentId })
|
|
1623
1924
|
);
|
|
1624
1925
|
yield* Console7.log(`Set ${created.key}.`);
|
|
1625
1926
|
}).pipe(
|
|
1626
|
-
|
|
1627
|
-
InvalidEnvVarFormatError: (err) => Console7.error(err.message).pipe(
|
|
1927
|
+
Effect17.catchTags({
|
|
1928
|
+
InvalidEnvVarFormatError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
1628
1929
|
NotAuthenticatedError: (err) => Console7.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
|
|
1629
|
-
|
|
1930
|
+
Effect17.andThen(Effect17.fail(err))
|
|
1630
1931
|
),
|
|
1631
|
-
ApiRequestError: (err) => Console7.error(`Failed to set environment variable: ${err.message}`).pipe(
|
|
1632
|
-
ProjectNotLinkedError: (err) => Console7.error(err.message).pipe(
|
|
1633
|
-
ProjectLinkReadError: (err) => Console7.error(err.message).pipe(
|
|
1932
|
+
ApiRequestError: (err) => Console7.error(`Failed to set environment variable: ${err.message}`).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
1933
|
+
ProjectNotLinkedError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
1934
|
+
ProjectLinkReadError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err)))
|
|
1634
1935
|
})
|
|
1635
1936
|
)
|
|
1636
1937
|
);
|
|
1637
1938
|
var envRmCommand = Command7.make(
|
|
1638
1939
|
"rm",
|
|
1639
1940
|
{ keyOrId: keyOrIdArg, app: appOption3, org: orgOption4 },
|
|
1640
|
-
({ keyOrId, app, org }) =>
|
|
1941
|
+
({ keyOrId, app, org }) => Effect17.gen(function* () {
|
|
1641
1942
|
const apiClient = yield* ApiClientService;
|
|
1642
1943
|
const scope = yield* resolveScope(app, org);
|
|
1643
1944
|
const { items } = yield* apiClient.request(
|
|
@@ -1645,7 +1946,7 @@ var envRmCommand = Command7.make(
|
|
|
1645
1946
|
);
|
|
1646
1947
|
const target = items.find((v) => v.id === keyOrId || v.key === keyOrId);
|
|
1647
1948
|
if (target === void 0) {
|
|
1648
|
-
return yield*
|
|
1949
|
+
return yield* Effect17.fail(
|
|
1649
1950
|
new EnvVarNotFoundError({ message: `No environment variable matching "${keyOrId}".` })
|
|
1650
1951
|
);
|
|
1651
1952
|
}
|
|
@@ -1654,23 +1955,97 @@ var envRmCommand = Command7.make(
|
|
|
1654
1955
|
);
|
|
1655
1956
|
yield* Console7.log(`Removed ${target.key}.`);
|
|
1656
1957
|
}).pipe(
|
|
1657
|
-
|
|
1658
|
-
EnvVarNotFoundError: (err) => Console7.error(err.message).pipe(
|
|
1958
|
+
Effect17.catchTags({
|
|
1959
|
+
EnvVarNotFoundError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
1960
|
+
NotAuthenticatedError: (err) => Console7.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
|
|
1961
|
+
Effect17.andThen(Effect17.fail(err))
|
|
1962
|
+
),
|
|
1963
|
+
ApiRequestError: (err) => Console7.error(`Failed to remove environment variable: ${err.message}`).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
1964
|
+
ProjectNotLinkedError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
1965
|
+
ProjectLinkReadError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err)))
|
|
1966
|
+
})
|
|
1967
|
+
)
|
|
1968
|
+
);
|
|
1969
|
+
var envPullCommand = Command7.make(
|
|
1970
|
+
"pull",
|
|
1971
|
+
{
|
|
1972
|
+
file: fileArg,
|
|
1973
|
+
app: appOption3,
|
|
1974
|
+
environment: environmentOption,
|
|
1975
|
+
yes: yesOption,
|
|
1976
|
+
withCredentials: withCredentialsOption,
|
|
1977
|
+
rotate: rotateOption
|
|
1978
|
+
},
|
|
1979
|
+
({ file, app, environment, yes, withCredentials, rotate }) => Effect17.gen(function* () {
|
|
1980
|
+
const apiClient = yield* ApiClientService;
|
|
1981
|
+
const projectLink = yield* ProjectLinkService;
|
|
1982
|
+
const envFile = yield* EnvFileService;
|
|
1983
|
+
const applicationId = yield* Option10.match(app, {
|
|
1984
|
+
onSome: (value) => Effect17.succeed(value),
|
|
1985
|
+
onNone: () => projectLink.resolve(process.cwd()).pipe(Effect17.map((link) => link.applicationId))
|
|
1986
|
+
});
|
|
1987
|
+
const target = Option10.getOrElse(file, () => ".env.local");
|
|
1988
|
+
const environmentSlug = Option10.getOrUndefined(environment);
|
|
1989
|
+
const { items, omittedSensitive } = yield* apiClient.request(
|
|
1990
|
+
(client) => client.applications.envVars.pull(applicationId, environmentSlug ? { environment: environmentSlug } : void 0)
|
|
1991
|
+
);
|
|
1992
|
+
const existing = yield* envFile.read(target);
|
|
1993
|
+
if (Option10.isSome(existing) && !yes) {
|
|
1994
|
+
const overwrite = yield* Prompt.run(
|
|
1995
|
+
Prompt.confirm({ message: `${target} already exists. Overwrite?`, initial: false })
|
|
1996
|
+
);
|
|
1997
|
+
if (!overwrite) {
|
|
1998
|
+
return yield* Console7.log("Cancelled.");
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
let credentialEntries2 = [];
|
|
2002
|
+
if (withCredentials) {
|
|
2003
|
+
const localCredentials = yield* LocalCredentialsService;
|
|
2004
|
+
credentialEntries2 = yield* localCredentials.provision({
|
|
2005
|
+
applicationId,
|
|
2006
|
+
rotate,
|
|
2007
|
+
existingContent: Option10.getOrUndefined(existing)
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
const entries = [...items.map((v) => ({ key: v.key, value: v.value })), ...credentialEntries2];
|
|
2011
|
+
yield* envFile.write(target, entries);
|
|
2012
|
+
const gitignored = yield* envFile.ensureGitignored(process.cwd(), target);
|
|
2013
|
+
yield* Console7.log(
|
|
2014
|
+
`Pulled ${items.length} environment variable(s)${withCredentials ? " and API credentials" : ""} to ${target}.`
|
|
2015
|
+
);
|
|
2016
|
+
if (omittedSensitive > 0) {
|
|
2017
|
+
yield* Console7.log(
|
|
2018
|
+
`Skipped ${omittedSensitive} sensitive variable(s); secrets live only in production/preview environments.`
|
|
2019
|
+
);
|
|
2020
|
+
}
|
|
2021
|
+
if (gitignored) {
|
|
2022
|
+
yield* Console7.log(`Added ${target} to .gitignore.`);
|
|
2023
|
+
}
|
|
2024
|
+
}).pipe(
|
|
2025
|
+
Effect17.catchTags({
|
|
2026
|
+
// Ctrl+C during the overwrite prompt is a deliberate cancel — exit 0.
|
|
2027
|
+
QuitException: () => Console7.log("Cancelled."),
|
|
1659
2028
|
NotAuthenticatedError: (err) => Console7.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
|
|
1660
|
-
|
|
2029
|
+
Effect17.andThen(Effect17.fail(err))
|
|
1661
2030
|
),
|
|
1662
|
-
ApiRequestError: (err) => Console7.error(`Failed to
|
|
1663
|
-
ProjectNotLinkedError: (err) => Console7.error(err.message).pipe(
|
|
1664
|
-
ProjectLinkReadError: (err) => Console7.error(err.message).pipe(
|
|
2031
|
+
ApiRequestError: (err) => Console7.error(`Failed to pull environment variables: ${err.message}`).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
2032
|
+
ProjectNotLinkedError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
2033
|
+
ProjectLinkReadError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
2034
|
+
EnvFileReadError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
2035
|
+
EnvFileWriteError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
2036
|
+
DevCredentialsStoreReadError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err))),
|
|
2037
|
+
DevCredentialsStoreWriteError: (err) => Console7.error(err.message).pipe(Effect17.andThen(Effect17.fail(err)))
|
|
1665
2038
|
})
|
|
1666
2039
|
)
|
|
1667
2040
|
);
|
|
1668
|
-
var envBase = Command7.make("env", {}, () =>
|
|
1669
|
-
var envCommand = envBase.pipe(
|
|
2041
|
+
var envBase = Command7.make("env", {}, () => Effect17.void);
|
|
2042
|
+
var envCommand = envBase.pipe(
|
|
2043
|
+
Command7.withSubcommands([envListCommand, envSetCommand, envRmCommand, envPullCommand])
|
|
2044
|
+
);
|
|
1670
2045
|
|
|
1671
2046
|
// src/commands/link/index.ts
|
|
1672
|
-
import { Command as Command8, Options as Options5, Prompt } from "@effect/cli";
|
|
1673
|
-
import { Console as Console8, Effect as
|
|
2047
|
+
import { Command as Command8, Options as Options5, Prompt as Prompt2 } from "@effect/cli";
|
|
2048
|
+
import { Console as Console8, Effect as Effect18, Option as Option11 } from "effect";
|
|
1674
2049
|
var appOption4 = Options5.text("app").pipe(
|
|
1675
2050
|
Options5.withAlias("a"),
|
|
1676
2051
|
Options5.withDescription("Application ID to link (skips the interactive picker)"),
|
|
@@ -1684,26 +2059,26 @@ var orgOption5 = Options5.text("org").pipe(
|
|
|
1684
2059
|
var linkCommand = Command8.make(
|
|
1685
2060
|
"link",
|
|
1686
2061
|
{ app: appOption4, org: orgOption5 },
|
|
1687
|
-
({ app, org }) =>
|
|
2062
|
+
({ app, org }) => Effect18.gen(function* () {
|
|
1688
2063
|
const apiClient = yield* ApiClientService;
|
|
1689
2064
|
const projectLink = yield* ProjectLinkService;
|
|
1690
2065
|
const cwd = process.cwd();
|
|
1691
|
-
const organizationId =
|
|
2066
|
+
const organizationId = Option11.getOrUndefined(org);
|
|
1692
2067
|
const { items: apps } = yield* apiClient.request((client) => client.applications.list({ organizationId }));
|
|
1693
2068
|
if (apps.length === 0) {
|
|
1694
|
-
return yield*
|
|
2069
|
+
return yield* Effect18.fail(
|
|
1695
2070
|
new NoApplicationsFoundError({
|
|
1696
2071
|
message: 'No applications found. Create one in the Gigadrive console, then run "gigadrive link" again.'
|
|
1697
2072
|
})
|
|
1698
2073
|
);
|
|
1699
2074
|
}
|
|
1700
|
-
const explicitAppId =
|
|
2075
|
+
const explicitAppId = Option11.getOrUndefined(app);
|
|
1701
2076
|
let applicationId;
|
|
1702
2077
|
let linkedOrganizationId;
|
|
1703
2078
|
if (explicitAppId !== void 0) {
|
|
1704
2079
|
const match = apps.find((a) => a.id === explicitAppId);
|
|
1705
2080
|
if (match === void 0) {
|
|
1706
|
-
return yield*
|
|
2081
|
+
return yield* Effect18.fail(
|
|
1707
2082
|
new ApplicationNotFoundError({
|
|
1708
2083
|
message: `Application "${explicitAppId}" was not found among your applications.`
|
|
1709
2084
|
})
|
|
@@ -1715,8 +2090,8 @@ var linkCommand = Command8.make(
|
|
|
1715
2090
|
applicationId = apps[0].id;
|
|
1716
2091
|
linkedOrganizationId = apps[0].organization.id;
|
|
1717
2092
|
} else {
|
|
1718
|
-
const chosen = yield*
|
|
1719
|
-
|
|
2093
|
+
const chosen = yield* Prompt2.run(
|
|
2094
|
+
Prompt2.select({
|
|
1720
2095
|
message: "Select an application to link",
|
|
1721
2096
|
choices: apps.map((a) => ({ title: `${a.name} (${a.organization.name})`, value: a }))
|
|
1722
2097
|
})
|
|
@@ -1728,48 +2103,48 @@ var linkCommand = Command8.make(
|
|
|
1728
2103
|
yield* Console8.log(`Linked this directory to application ${applicationId}.`);
|
|
1729
2104
|
yield* Console8.log('Tip: add ".gigadrive/" to your .gitignore.');
|
|
1730
2105
|
}).pipe(
|
|
1731
|
-
|
|
2106
|
+
Effect18.catchTags({
|
|
1732
2107
|
// Ctrl+C during the picker is a deliberate cancel — exit 0.
|
|
1733
2108
|
QuitException: () => Console8.log("Cancelled."),
|
|
1734
2109
|
NotAuthenticatedError: (err) => Console8.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
|
|
1735
|
-
|
|
2110
|
+
Effect18.andThen(Effect18.fail(err))
|
|
1736
2111
|
),
|
|
1737
|
-
ApiRequestError: (err) => Console8.error(`Failed to list applications: ${err.message}`).pipe(
|
|
1738
|
-
NoApplicationsFoundError: (err) => Console8.error(err.message).pipe(
|
|
1739
|
-
ApplicationNotFoundError: (err) => Console8.error(err.message).pipe(
|
|
1740
|
-
ProjectLinkWriteError: (err) => Console8.error(err.message).pipe(
|
|
2112
|
+
ApiRequestError: (err) => Console8.error(`Failed to list applications: ${err.message}`).pipe(Effect18.andThen(Effect18.fail(err))),
|
|
2113
|
+
NoApplicationsFoundError: (err) => Console8.error(err.message).pipe(Effect18.andThen(Effect18.fail(err))),
|
|
2114
|
+
ApplicationNotFoundError: (err) => Console8.error(err.message).pipe(Effect18.andThen(Effect18.fail(err))),
|
|
2115
|
+
ProjectLinkWriteError: (err) => Console8.error(err.message).pipe(Effect18.andThen(Effect18.fail(err)))
|
|
1741
2116
|
})
|
|
1742
2117
|
)
|
|
1743
2118
|
);
|
|
1744
2119
|
var unlinkCommand = Command8.make(
|
|
1745
2120
|
"unlink",
|
|
1746
2121
|
{},
|
|
1747
|
-
() =>
|
|
2122
|
+
() => Effect18.gen(function* () {
|
|
1748
2123
|
const projectLink = yield* ProjectLinkService;
|
|
1749
2124
|
yield* projectLink.remove(process.cwd());
|
|
1750
2125
|
yield* Console8.log("Unlinked this directory.");
|
|
1751
2126
|
}).pipe(
|
|
1752
|
-
|
|
1753
|
-
ProjectLinkWriteError: (err) => Console8.error(err.message).pipe(
|
|
2127
|
+
Effect18.catchTags({
|
|
2128
|
+
ProjectLinkWriteError: (err) => Console8.error(err.message).pipe(Effect18.andThen(Effect18.fail(err)))
|
|
1754
2129
|
})
|
|
1755
2130
|
)
|
|
1756
2131
|
);
|
|
1757
2132
|
|
|
1758
2133
|
// src/commands/login/index.ts
|
|
1759
2134
|
import { Command as Command9 } from "@effect/cli";
|
|
1760
|
-
import { Console as Console9, Effect as
|
|
2135
|
+
import { Console as Console9, Effect as Effect19 } from "effect";
|
|
1761
2136
|
var loginCommand = Command9.make(
|
|
1762
2137
|
"login",
|
|
1763
2138
|
{},
|
|
1764
|
-
() =>
|
|
2139
|
+
() => Effect19.gen(function* () {
|
|
1765
2140
|
yield* Console9.log("Initiating login...");
|
|
1766
2141
|
const auth = yield* AuthService;
|
|
1767
2142
|
yield* auth.login;
|
|
1768
|
-
const info = yield* auth.getUserInfo.pipe(
|
|
2143
|
+
const info = yield* auth.getUserInfo.pipe(Effect19.catchAll(() => Effect19.succeed({})));
|
|
1769
2144
|
const name = auth.inferUserName(info);
|
|
1770
2145
|
yield* Console9.log(`You are now logged in as ${name}.`);
|
|
1771
2146
|
}).pipe(
|
|
1772
|
-
|
|
2147
|
+
Effect19.catchTags({
|
|
1773
2148
|
LoginFlowError: (err) => Console9.error(`Login failed: ${err.message}`),
|
|
1774
2149
|
TokenExchangeError: (err) => Console9.error(`Login failed: ${err.message}`),
|
|
1775
2150
|
OAuthDiscoveryError: (err) => Console9.error(`Login failed: ${err.message}`),
|
|
@@ -1780,51 +2155,51 @@ var loginCommand = Command9.make(
|
|
|
1780
2155
|
|
|
1781
2156
|
// src/commands/logout/index.ts
|
|
1782
2157
|
import { Command as Command10 } from "@effect/cli";
|
|
1783
|
-
import { Console as Console10, Effect as
|
|
2158
|
+
import { Console as Console10, Effect as Effect20 } from "effect";
|
|
1784
2159
|
var logoutCommand = Command10.make(
|
|
1785
2160
|
"logout",
|
|
1786
2161
|
{},
|
|
1787
|
-
() =>
|
|
2162
|
+
() => Effect20.gen(function* () {
|
|
1788
2163
|
const auth = yield* AuthService;
|
|
1789
2164
|
yield* auth.logout;
|
|
1790
2165
|
yield* Console10.log("Logged out.");
|
|
1791
2166
|
}).pipe(
|
|
1792
|
-
|
|
1793
|
-
AuthStorageWriteError: (err) => Console10.error(`Failed to log out: ${err.message}`).pipe(
|
|
2167
|
+
Effect20.catchTags({
|
|
2168
|
+
AuthStorageWriteError: (err) => Console10.error(`Failed to log out: ${err.message}`).pipe(Effect20.andThen(Effect20.fail(err)))
|
|
1794
2169
|
})
|
|
1795
2170
|
)
|
|
1796
2171
|
);
|
|
1797
2172
|
|
|
1798
2173
|
// src/commands/platform/index.ts
|
|
1799
2174
|
import { Command as Command12 } from "@effect/cli";
|
|
1800
|
-
import { Effect as
|
|
2175
|
+
import { Effect as Effect24 } from "effect";
|
|
1801
2176
|
|
|
1802
2177
|
// src/commands/platform/deploy/index.ts
|
|
1803
2178
|
import { Command as Command11, Options as Options6 } from "@effect/cli";
|
|
1804
|
-
import { FileSystem as
|
|
2179
|
+
import { FileSystem as FileSystem9, Path as Path8 } from "@effect/platform";
|
|
1805
2180
|
import { formatFileSize } from "@gigadrive/commons";
|
|
1806
|
-
import { Console as Console11, Duration, Effect as
|
|
1807
|
-
import * as
|
|
2181
|
+
import { Console as Console11, Duration, Effect as Effect23, Option as Option12, Schedule, Stream } from "effect";
|
|
2182
|
+
import * as os4 from "os";
|
|
1808
2183
|
|
|
1809
2184
|
// src/services/archive.ts
|
|
1810
|
-
import { FileSystem as
|
|
2185
|
+
import { FileSystem as FileSystem8, Path as Path7 } from "@effect/platform";
|
|
1811
2186
|
import { getFilesForPattern } from "@gigadrive/build-utils";
|
|
1812
2187
|
import archiver from "archiver";
|
|
1813
|
-
import { Effect as
|
|
2188
|
+
import { Effect as Effect21 } from "effect";
|
|
1814
2189
|
import ignore from "ignore";
|
|
1815
2190
|
import * as nodeFs from "fs";
|
|
1816
2191
|
import * as nodeFsPromises from "fs/promises";
|
|
1817
2192
|
var IGNORE_FILE_NAMES = [".gigadriveignore", ".vercelignore", ".dockerignore", ".nowignore"];
|
|
1818
2193
|
var readIgnoreFile = (fs, ignorePath) => fs.readFileString(ignorePath, "utf8").pipe(
|
|
1819
|
-
|
|
1820
|
-
|
|
2194
|
+
Effect21.map((content) => content.split("\n").filter((line) => line.trim() !== "")),
|
|
2195
|
+
Effect21.catchAll(() => Effect21.succeed([]))
|
|
1821
2196
|
);
|
|
1822
|
-
var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) =>
|
|
1823
|
-
const items = yield* fs.readDirectory(dir).pipe(
|
|
2197
|
+
var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) => Effect21.gen(function* () {
|
|
2198
|
+
const items = yield* fs.readDirectory(dir).pipe(Effect21.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${dir}` })));
|
|
1824
2199
|
for (const item of items) {
|
|
1825
2200
|
const fullPath = pathService.join(dir, item);
|
|
1826
2201
|
const relativePath = pathService.relative(baseDir, fullPath);
|
|
1827
|
-
const stat = yield*
|
|
2202
|
+
const stat = yield* Effect21.tryPromise({
|
|
1828
2203
|
try: () => nodeFsPromises.lstat(fullPath),
|
|
1829
2204
|
catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
|
|
1830
2205
|
});
|
|
@@ -1844,8 +2219,8 @@ var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules) => Effe
|
|
|
1844
2219
|
yield* collectIgnorePatterns(fs, pathService, fullPath, baseDir, ignoreRules);
|
|
1845
2220
|
}
|
|
1846
2221
|
}
|
|
1847
|
-
}).pipe(
|
|
1848
|
-
var initializeIgnoreRules = (fs, pathService, baseDir) =>
|
|
2222
|
+
}).pipe(Effect21.catchAll(() => Effect21.void));
|
|
2223
|
+
var initializeIgnoreRules = (fs, pathService, baseDir) => Effect21.gen(function* () {
|
|
1849
2224
|
const ignoreRules = ignore();
|
|
1850
2225
|
ignoreRules.add([".git/", "**/.DS_Store", "**/Thumbs.db"]);
|
|
1851
2226
|
for (const ignoreFileName of IGNORE_FILE_NAMES) {
|
|
@@ -1860,14 +2235,14 @@ var initializeIgnoreRules = (fs, pathService, baseDir) => Effect18.gen(function*
|
|
|
1860
2235
|
yield* collectIgnorePatterns(fs, pathService, baseDir, baseDir, ignoreRules);
|
|
1861
2236
|
return ignoreRules;
|
|
1862
2237
|
});
|
|
1863
|
-
var getFilesToInclude = (fs, pathService, dir, ignoreRules) =>
|
|
2238
|
+
var getFilesToInclude = (fs, pathService, dir, ignoreRules) => Effect21.gen(function* () {
|
|
1864
2239
|
const filesToInclude = [];
|
|
1865
|
-
const walkDir = (currentDir) =>
|
|
1866
|
-
const items = yield* fs.readDirectory(currentDir).pipe(
|
|
2240
|
+
const walkDir = (currentDir) => Effect21.gen(function* () {
|
|
2241
|
+
const items = yield* fs.readDirectory(currentDir).pipe(Effect21.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${currentDir}` })));
|
|
1867
2242
|
for (const item of items) {
|
|
1868
2243
|
const fullPath = pathService.join(currentDir, item);
|
|
1869
2244
|
const relativePath = pathService.relative(dir, fullPath);
|
|
1870
|
-
const stat = yield*
|
|
2245
|
+
const stat = yield* Effect21.tryPromise({
|
|
1871
2246
|
try: () => nodeFsPromises.lstat(fullPath),
|
|
1872
2247
|
catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
|
|
1873
2248
|
});
|
|
@@ -1881,21 +2256,21 @@ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => Effect18.gen(func
|
|
|
1881
2256
|
}
|
|
1882
2257
|
}
|
|
1883
2258
|
}
|
|
1884
|
-
}).pipe(
|
|
2259
|
+
}).pipe(Effect21.catchAll(() => Effect21.void));
|
|
1885
2260
|
yield* walkDir(dir);
|
|
1886
2261
|
return filesToInclude;
|
|
1887
2262
|
});
|
|
1888
|
-
var ArchiveService = class extends
|
|
2263
|
+
var ArchiveService = class extends Effect21.Service()("ArchiveService", {
|
|
1889
2264
|
accessors: true,
|
|
1890
|
-
effect:
|
|
1891
|
-
const fs = yield*
|
|
1892
|
-
const pathService = yield*
|
|
1893
|
-
const createZipArchive =
|
|
1894
|
-
yield*
|
|
1895
|
-
yield*
|
|
1896
|
-
yield*
|
|
2265
|
+
effect: Effect21.gen(function* () {
|
|
2266
|
+
const fs = yield* FileSystem8.FileSystem;
|
|
2267
|
+
const pathService = yield* Path7.Path;
|
|
2268
|
+
const createZipArchive = Effect21.fn("ArchiveService.createZipArchive")(function* (inputDir, outputFile, options = {}) {
|
|
2269
|
+
yield* Effect21.annotateCurrentSpan("inputDir", inputDir);
|
|
2270
|
+
yield* Effect21.annotateCurrentSpan("outputFile", outputFile);
|
|
2271
|
+
yield* Effect21.log("Creating zip archive", { inputDir, outputFile });
|
|
1897
2272
|
const ignoreRules = options.useIgnoreFiles !== false ? yield* initializeIgnoreRules(fs, pathService, inputDir) : ignore();
|
|
1898
|
-
const filesToInclude = options.whitelist ? yield*
|
|
2273
|
+
const filesToInclude = options.whitelist ? yield* Effect21.tryPromise({
|
|
1899
2274
|
try: () => Promise.all(
|
|
1900
2275
|
options.whitelist.map((file) => getFilesForPattern(file, inputDir, options.excludeFiles ?? []))
|
|
1901
2276
|
),
|
|
@@ -1903,25 +2278,25 @@ var ArchiveService = class extends Effect18.Service()("ArchiveService", {
|
|
|
1903
2278
|
message: "Failed to resolve whitelist patterns",
|
|
1904
2279
|
cause: error instanceof Error ? error.message : String(error)
|
|
1905
2280
|
})
|
|
1906
|
-
}) : yield*
|
|
2281
|
+
}) : yield* Effect21.gen(function* () {
|
|
1907
2282
|
if (options.excludeFiles && options.excludeFiles.length > 0) {
|
|
1908
2283
|
ignoreRules.add(options.excludeFiles);
|
|
1909
2284
|
}
|
|
1910
2285
|
return yield* getFilesToInclude(fs, pathService, inputDir, ignoreRules).pipe(
|
|
1911
|
-
|
|
2286
|
+
Effect21.map((files) => [files])
|
|
1912
2287
|
);
|
|
1913
2288
|
});
|
|
1914
|
-
const archiveExists = yield* fs.exists(outputFile).pipe(
|
|
2289
|
+
const archiveExists = yield* fs.exists(outputFile).pipe(Effect21.catchAll(() => Effect21.succeed(false)));
|
|
1915
2290
|
if (archiveExists) {
|
|
1916
2291
|
yield* fs.remove(outputFile).pipe(
|
|
1917
|
-
|
|
2292
|
+
Effect21.mapError(
|
|
1918
2293
|
(error) => new ArchiveCreateError({
|
|
1919
2294
|
message: `Failed to remove existing archive: ${String(error)}`
|
|
1920
2295
|
})
|
|
1921
2296
|
)
|
|
1922
2297
|
);
|
|
1923
2298
|
}
|
|
1924
|
-
yield*
|
|
2299
|
+
yield* Effect21.tryPromise({
|
|
1925
2300
|
try: () => new Promise((resolve, reject) => {
|
|
1926
2301
|
const output = nodeFs.createWriteStream(outputFile);
|
|
1927
2302
|
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
@@ -1940,14 +2315,14 @@ var ArchiveService = class extends Effect18.Service()("ArchiveService", {
|
|
|
1940
2315
|
})
|
|
1941
2316
|
});
|
|
1942
2317
|
const fileInfo = yield* fs.stat(outputFile).pipe(
|
|
1943
|
-
|
|
2318
|
+
Effect21.mapError(
|
|
1944
2319
|
(error) => new ArchiveCreateError({
|
|
1945
2320
|
message: `Failed to stat archive: ${String(error)}`
|
|
1946
2321
|
})
|
|
1947
2322
|
)
|
|
1948
2323
|
);
|
|
1949
2324
|
const size = Number(fileInfo.size);
|
|
1950
|
-
yield*
|
|
2325
|
+
yield* Effect21.log("Archive created", { size });
|
|
1951
2326
|
return { path: outputFile, size };
|
|
1952
2327
|
});
|
|
1953
2328
|
return { createZipArchive };
|
|
@@ -1956,32 +2331,32 @@ var ArchiveService = class extends Effect18.Service()("ArchiveService", {
|
|
|
1956
2331
|
};
|
|
1957
2332
|
|
|
1958
2333
|
// src/services/deployment-api.ts
|
|
1959
|
-
import { Effect as
|
|
1960
|
-
var DeploymentApiService = class extends
|
|
2334
|
+
import { Effect as Effect22 } from "effect";
|
|
2335
|
+
var DeploymentApiService = class extends Effect22.Service()("DeploymentApiService", {
|
|
1961
2336
|
accessors: true,
|
|
1962
2337
|
dependencies: [ApiClientService.Default],
|
|
1963
|
-
effect:
|
|
2338
|
+
effect: Effect22.gen(function* () {
|
|
1964
2339
|
const apiClient = yield* ApiClientService;
|
|
1965
2340
|
const getClient = apiClient.getClient.pipe(
|
|
1966
|
-
|
|
2341
|
+
Effect22.mapError((err) => new DeploymentAuthError({ message: `Authentication failed: ${err.message}` }))
|
|
1967
2342
|
);
|
|
1968
|
-
const createDeployment =
|
|
1969
|
-
yield*
|
|
1970
|
-
yield*
|
|
2343
|
+
const createDeployment = Effect22.fn("DeploymentApiService.createDeployment")(function* (applicationId) {
|
|
2344
|
+
yield* Effect22.annotateCurrentSpan("applicationId", applicationId);
|
|
2345
|
+
yield* Effect22.log("Creating deployment", { applicationId });
|
|
1971
2346
|
const client = yield* getClient;
|
|
1972
|
-
const deployment = yield*
|
|
2347
|
+
const deployment = yield* Effect22.tryPromise({
|
|
1973
2348
|
try: () => client.deployments.create({ applicationId }),
|
|
1974
2349
|
catch: (error) => new DeploymentCreateError({
|
|
1975
2350
|
message: `Failed to create deployment: ${error instanceof Error ? error.message : String(error)}`
|
|
1976
2351
|
})
|
|
1977
2352
|
});
|
|
1978
|
-
yield*
|
|
2353
|
+
yield* Effect22.log("Deployment created", { deploymentId: deployment.id });
|
|
1979
2354
|
return deployment.id;
|
|
1980
2355
|
});
|
|
1981
|
-
const startMultipartUpload =
|
|
1982
|
-
yield*
|
|
2356
|
+
const startMultipartUpload = Effect22.fn("DeploymentApiService.startMultipartUpload")(function* (deploymentId) {
|
|
2357
|
+
yield* Effect22.annotateCurrentSpan("deploymentId", deploymentId);
|
|
1983
2358
|
const client = yield* getClient;
|
|
1984
|
-
const result = yield*
|
|
2359
|
+
const result = yield* Effect22.tryPromise({
|
|
1985
2360
|
try: () => client.deployments.startUpload(deploymentId),
|
|
1986
2361
|
catch: (error) => new UploadStartError({
|
|
1987
2362
|
message: `Failed to start multipart upload: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1989,9 +2364,9 @@ var DeploymentApiService = class extends Effect19.Service()("DeploymentApiServic
|
|
|
1989
2364
|
});
|
|
1990
2365
|
return result.uploadId;
|
|
1991
2366
|
});
|
|
1992
|
-
const getPresignedUrl =
|
|
2367
|
+
const getPresignedUrl = Effect22.fn("DeploymentApiService.getPresignedUrl")(function* (deploymentId, uploadId, partNumber) {
|
|
1993
2368
|
const client = yield* getClient;
|
|
1994
|
-
const result = yield*
|
|
2369
|
+
const result = yield* Effect22.tryPromise({
|
|
1995
2370
|
try: () => client.deployments.getPresignedUrl(deploymentId, uploadId, partNumber),
|
|
1996
2371
|
catch: (error) => new PresignedUrlError({
|
|
1997
2372
|
message: `Failed to get presigned URL: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -2000,11 +2375,11 @@ var DeploymentApiService = class extends Effect19.Service()("DeploymentApiServic
|
|
|
2000
2375
|
});
|
|
2001
2376
|
return result.url;
|
|
2002
2377
|
});
|
|
2003
|
-
const uploadPart =
|
|
2004
|
-
yield*
|
|
2005
|
-
yield*
|
|
2378
|
+
const uploadPart = Effect22.fn("DeploymentApiService.uploadPart")(function* (presignedUrl, data, partNumber) {
|
|
2379
|
+
yield* Effect22.annotateCurrentSpan("partNumber", partNumber);
|
|
2380
|
+
yield* Effect22.annotateCurrentSpan("size", data.length);
|
|
2006
2381
|
const client = yield* getClient;
|
|
2007
|
-
const result = yield*
|
|
2382
|
+
const result = yield* Effect22.tryPromise({
|
|
2008
2383
|
try: () => client.deployments.uploadPart(presignedUrl, data, partNumber),
|
|
2009
2384
|
catch: (error) => new UploadPartError({
|
|
2010
2385
|
message: `Failed to upload part ${partNumber}: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -2013,21 +2388,21 @@ var DeploymentApiService = class extends Effect19.Service()("DeploymentApiServic
|
|
|
2013
2388
|
});
|
|
2014
2389
|
return result;
|
|
2015
2390
|
});
|
|
2016
|
-
const completeUpload =
|
|
2017
|
-
yield*
|
|
2018
|
-
yield*
|
|
2391
|
+
const completeUpload = Effect22.fn("DeploymentApiService.completeUpload")(function* (deploymentId, uploadId, parts) {
|
|
2392
|
+
yield* Effect22.annotateCurrentSpan("deploymentId", deploymentId);
|
|
2393
|
+
yield* Effect22.annotateCurrentSpan("partsCount", parts.length);
|
|
2019
2394
|
const client = yield* getClient;
|
|
2020
|
-
yield*
|
|
2395
|
+
yield* Effect22.tryPromise({
|
|
2021
2396
|
try: () => client.deployments.completeUpload(deploymentId, uploadId, parts),
|
|
2022
2397
|
catch: (error) => new UploadCompleteError({
|
|
2023
2398
|
message: `Failed to complete upload: ${error instanceof Error ? error.message : String(error)}`
|
|
2024
2399
|
})
|
|
2025
2400
|
});
|
|
2026
|
-
yield*
|
|
2401
|
+
yield* Effect22.log("Upload completed successfully");
|
|
2027
2402
|
});
|
|
2028
|
-
const getDeploymentStatus =
|
|
2403
|
+
const getDeploymentStatus = Effect22.fn("DeploymentApiService.getDeploymentStatus")(function* (deploymentId) {
|
|
2029
2404
|
const client = yield* getClient;
|
|
2030
|
-
const deployment = yield*
|
|
2405
|
+
const deployment = yield* Effect22.tryPromise({
|
|
2031
2406
|
try: () => client.deployments.get(deploymentId),
|
|
2032
2407
|
catch: (error) => new DeploymentStatusError({
|
|
2033
2408
|
message: `Failed to get deployment status: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -2035,9 +2410,9 @@ var DeploymentApiService = class extends Effect19.Service()("DeploymentApiServic
|
|
|
2035
2410
|
});
|
|
2036
2411
|
return deployment.status;
|
|
2037
2412
|
});
|
|
2038
|
-
const getLogs =
|
|
2413
|
+
const getLogs = Effect22.fn("DeploymentApiService.getLogs")(function* (deploymentId, options) {
|
|
2039
2414
|
const client = yield* getClient;
|
|
2040
|
-
const result = yield*
|
|
2415
|
+
const result = yield* Effect22.tryPromise({
|
|
2041
2416
|
try: () => client.deployments.getLogs(deploymentId, options),
|
|
2042
2417
|
catch: (error) => new DeploymentLogsFetchError({
|
|
2043
2418
|
message: `Failed to get logs: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -2045,9 +2420,9 @@ var DeploymentApiService = class extends Effect19.Service()("DeploymentApiServic
|
|
|
2045
2420
|
});
|
|
2046
2421
|
return result;
|
|
2047
2422
|
});
|
|
2048
|
-
const getHostnames =
|
|
2423
|
+
const getHostnames = Effect22.fn("DeploymentApiService.getHostnames")(function* (deploymentId) {
|
|
2049
2424
|
const client = yield* getClient;
|
|
2050
|
-
const result = yield*
|
|
2425
|
+
const result = yield* Effect22.tryPromise({
|
|
2051
2426
|
try: () => client.deployments.getHostnames(deploymentId),
|
|
2052
2427
|
catch: (error) => new DeploymentHostnamesFetchError({
|
|
2053
2428
|
message: `Failed to get hostnames: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -2078,24 +2453,24 @@ var appOption5 = Options6.text("app").pipe(
|
|
|
2078
2453
|
var deployCommand = Command11.make(
|
|
2079
2454
|
"deploy",
|
|
2080
2455
|
{ app: appOption5 },
|
|
2081
|
-
({ app }) =>
|
|
2456
|
+
({ app }) => Effect23.gen(function* () {
|
|
2082
2457
|
const projectConfig = yield* ProjectConfigService;
|
|
2083
2458
|
const deploymentApi = yield* DeploymentApiService;
|
|
2084
2459
|
const archiveService = yield* ArchiveService;
|
|
2085
2460
|
const projectLink = yield* ProjectLinkService;
|
|
2086
|
-
const pathService = yield*
|
|
2461
|
+
const pathService = yield* Path8.Path;
|
|
2087
2462
|
const cwd = process.cwd();
|
|
2088
|
-
const applicationId = yield*
|
|
2089
|
-
onSome: (id) =>
|
|
2090
|
-
onNone: () => projectLink.resolve(cwd).pipe(
|
|
2463
|
+
const applicationId = yield* Option12.match(app, {
|
|
2464
|
+
onSome: (id) => Effect23.succeed(id),
|
|
2465
|
+
onNone: () => projectLink.resolve(cwd).pipe(Effect23.map((link) => link.applicationId))
|
|
2091
2466
|
});
|
|
2092
|
-
yield*
|
|
2467
|
+
yield* Effect23.log("Starting deploy command");
|
|
2093
2468
|
const { config } = yield* projectConfig.resolve(cwd);
|
|
2094
2469
|
yield* Console11.log("Creating deployment...");
|
|
2095
2470
|
const deploymentId = yield* deploymentApi.createDeployment(applicationId);
|
|
2096
2471
|
yield* Console11.log(`Deployment ID: ${deploymentId}`);
|
|
2097
2472
|
yield* Console11.log("Creating archive...");
|
|
2098
|
-
const archivePath = pathService.join(
|
|
2473
|
+
const archivePath = pathService.join(os4.tmpdir(), `project-${Date.now()}.zip`);
|
|
2099
2474
|
const archive = yield* archiveService.createZipArchive(config.userArchive?.rootOverwrite || cwd, archivePath, {
|
|
2100
2475
|
whitelist: config.userArchive?.fileWhitelist,
|
|
2101
2476
|
excludeFiles: config.excludeFiles,
|
|
@@ -2109,55 +2484,55 @@ var deployCommand = Command11.make(
|
|
|
2109
2484
|
yield* Console11.log("The deployment pipeline is now being provisioned. This may take a few seconds.");
|
|
2110
2485
|
yield* pollDeployment(deploymentId);
|
|
2111
2486
|
}).pipe(
|
|
2112
|
-
|
|
2113
|
-
ProjectNotLinkedError: (err) => Console11.error(err.message).pipe(
|
|
2114
|
-
ProjectLinkReadError: (err) => Console11.error(err.message).pipe(
|
|
2115
|
-
ConfigNotFoundError: (err) => Console11.error(err.message).pipe(
|
|
2116
|
-
ConfigParseError: (err) => Console11.error(`Config parse error: ${err.message}`).pipe(
|
|
2117
|
-
ConfigValidationError: (err) =>
|
|
2487
|
+
Effect23.catchTags({
|
|
2488
|
+
ProjectNotLinkedError: (err) => Console11.error(err.message).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2489
|
+
ProjectLinkReadError: (err) => Console11.error(err.message).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2490
|
+
ConfigNotFoundError: (err) => Console11.error(err.message).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2491
|
+
ConfigParseError: (err) => Console11.error(`Config parse error: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2492
|
+
ConfigValidationError: (err) => Effect23.gen(function* () {
|
|
2118
2493
|
for (const e of err.errors) {
|
|
2119
2494
|
yield* Console11.error(e);
|
|
2120
2495
|
}
|
|
2121
2496
|
yield* Console11.error(err.message);
|
|
2122
|
-
}).pipe(
|
|
2123
|
-
DeploymentCreateError: (err) => Console11.error(`Deployment creation failed: ${err.message}`).pipe(
|
|
2124
|
-
ArchiveCreateError: (err) => Console11.error(`Archive creation failed: ${err.message}`).pipe(
|
|
2125
|
-
UploadStartError: (err) => Console11.error(`Upload failed: ${err.message}`).pipe(
|
|
2126
|
-
UploadPartError: (err) => Console11.error(`Upload failed: ${err.message}`).pipe(
|
|
2127
|
-
UploadCompleteError: (err) => Console11.error(`Upload failed: ${err.message}`).pipe(
|
|
2128
|
-
PresignedUrlError: (err) => Console11.error(`Upload failed: ${err.message}`).pipe(
|
|
2129
|
-
DeploymentAuthError: (err) => Console11.error(`Authentication failed: ${err.message}`).pipe(
|
|
2130
|
-
DeploymentFailedError: (err) => Console11.error(err.message).pipe(
|
|
2497
|
+
}).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2498
|
+
DeploymentCreateError: (err) => Console11.error(`Deployment creation failed: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2499
|
+
ArchiveCreateError: (err) => Console11.error(`Archive creation failed: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2500
|
+
UploadStartError: (err) => Console11.error(`Upload failed: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2501
|
+
UploadPartError: (err) => Console11.error(`Upload failed: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2502
|
+
UploadCompleteError: (err) => Console11.error(`Upload failed: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2503
|
+
PresignedUrlError: (err) => Console11.error(`Upload failed: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2504
|
+
DeploymentAuthError: (err) => Console11.error(`Authentication failed: ${err.message}`).pipe(Effect23.andThen(Effect23.fail(err))),
|
|
2505
|
+
DeploymentFailedError: (err) => Console11.error(err.message).pipe(Effect23.andThen(Effect23.fail(err)))
|
|
2131
2506
|
})
|
|
2132
2507
|
)
|
|
2133
2508
|
);
|
|
2134
|
-
var uploadArchive = (deploymentId, archivePath, fileSize) =>
|
|
2509
|
+
var uploadArchive = (deploymentId, archivePath, fileSize) => Effect23.gen(function* () {
|
|
2135
2510
|
const deploymentApi = yield* DeploymentApiService;
|
|
2136
|
-
const fs = yield*
|
|
2511
|
+
const fs = yield* FileSystem9.FileSystem;
|
|
2137
2512
|
const uploadId = yield* deploymentApi.startMultipartUpload(deploymentId);
|
|
2138
2513
|
const FILE_CHUNK_SIZE = 10 * 1024 * 1024;
|
|
2139
2514
|
const numChunks = Math.ceil(fileSize / FILE_CHUNK_SIZE);
|
|
2140
|
-
yield*
|
|
2141
|
-
const parts = yield*
|
|
2515
|
+
yield* Effect23.log("Starting multipart upload", { uploadId, numChunks, fileSize });
|
|
2516
|
+
const parts = yield* Effect23.all(
|
|
2142
2517
|
Array.from({ length: numChunks }, (_, i) => {
|
|
2143
2518
|
const partNumber = i + 1;
|
|
2144
2519
|
const start = i * FILE_CHUNK_SIZE;
|
|
2145
2520
|
const bytesToRead = partNumber < numChunks ? FILE_CHUNK_SIZE : fileSize - start;
|
|
2146
|
-
return
|
|
2521
|
+
return Effect23.gen(function* () {
|
|
2147
2522
|
const presignedUrl = yield* deploymentApi.getPresignedUrl(deploymentId, uploadId, partNumber);
|
|
2148
2523
|
const chunkData = yield* Stream.runCollect(fs.stream(archivePath, { offset: start, bytesToRead })).pipe(
|
|
2149
|
-
|
|
2524
|
+
Effect23.map((chunks) => {
|
|
2150
2525
|
const arrays = Array.from(chunks);
|
|
2151
2526
|
return Buffer.concat(arrays);
|
|
2152
2527
|
}),
|
|
2153
|
-
|
|
2528
|
+
Effect23.mapError(
|
|
2154
2529
|
(error) => new UploadPartError({
|
|
2155
2530
|
message: `Failed to read chunk ${partNumber}: ${String(error)}`,
|
|
2156
2531
|
partNumber
|
|
2157
2532
|
})
|
|
2158
2533
|
)
|
|
2159
2534
|
);
|
|
2160
|
-
yield*
|
|
2535
|
+
yield* Effect23.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
|
|
2161
2536
|
return yield* deploymentApi.uploadPart(presignedUrl, chunkData, partNumber);
|
|
2162
2537
|
});
|
|
2163
2538
|
}),
|
|
@@ -2165,20 +2540,20 @@ var uploadArchive = (deploymentId, archivePath, fileSize) => Effect20.gen(functi
|
|
|
2165
2540
|
);
|
|
2166
2541
|
yield* deploymentApi.completeUpload(deploymentId, uploadId, parts);
|
|
2167
2542
|
});
|
|
2168
|
-
var pollDeployment = (deploymentId) =>
|
|
2543
|
+
var pollDeployment = (deploymentId) => Effect23.gen(function* () {
|
|
2169
2544
|
const deploymentApi = yield* DeploymentApiService;
|
|
2170
2545
|
let lastStatus = "PENDING";
|
|
2171
2546
|
let logOffset = 0;
|
|
2172
2547
|
yield* Console11.log("Status: Pending");
|
|
2173
|
-
yield*
|
|
2174
|
-
|
|
2175
|
-
const newStatus = yield* deploymentApi.getDeploymentStatus(deploymentId).pipe(
|
|
2548
|
+
yield* Effect23.repeat(
|
|
2549
|
+
Effect23.gen(function* () {
|
|
2550
|
+
const newStatus = yield* deploymentApi.getDeploymentStatus(deploymentId).pipe(Effect23.catchTag("DeploymentStatusError", () => Effect23.succeed(lastStatus)));
|
|
2176
2551
|
if (newStatus !== lastStatus) {
|
|
2177
2552
|
lastStatus = newStatus;
|
|
2178
2553
|
const label = newStatus.charAt(0) + newStatus.slice(1).toLowerCase();
|
|
2179
2554
|
yield* Console11.log(`Status: ${label}`);
|
|
2180
2555
|
}
|
|
2181
|
-
const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(
|
|
2556
|
+
const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(Effect23.catchTag("DeploymentLogsFetchError", () => Effect23.succeed(null)));
|
|
2182
2557
|
if (logsPage) {
|
|
2183
2558
|
for (const logEntry of logsPage.items) {
|
|
2184
2559
|
logOffset++;
|
|
@@ -2186,17 +2561,17 @@ var pollDeployment = (deploymentId) => Effect20.gen(function* () {
|
|
|
2186
2561
|
}
|
|
2187
2562
|
}
|
|
2188
2563
|
if (newStatus === "ACTIVE") {
|
|
2189
|
-
const hostnames = yield* deploymentApi.getHostnames(deploymentId).pipe(
|
|
2564
|
+
const hostnames = yield* deploymentApi.getHostnames(deploymentId).pipe(Effect23.catchTag("DeploymentHostnamesFetchError", () => Effect23.succeed([])));
|
|
2190
2565
|
const active = hostnames.find((h) => h.active) ?? hostnames.at(0);
|
|
2191
2566
|
if (active) {
|
|
2192
2567
|
yield* Console11.log(`Deployed to https://${active.hostname}`);
|
|
2193
2568
|
} else {
|
|
2194
2569
|
yield* Console11.log("Deployment is live.");
|
|
2195
2570
|
}
|
|
2196
|
-
return yield*
|
|
2571
|
+
return yield* Effect23.fail("done");
|
|
2197
2572
|
}
|
|
2198
2573
|
if (newStatus === "FAILED") {
|
|
2199
|
-
return yield*
|
|
2574
|
+
return yield* Effect23.fail(
|
|
2200
2575
|
new DeploymentFailedError({
|
|
2201
2576
|
message: "The deployment failed. Please check the logs for more information."
|
|
2202
2577
|
})
|
|
@@ -2205,9 +2580,9 @@ var pollDeployment = (deploymentId) => Effect20.gen(function* () {
|
|
|
2205
2580
|
}),
|
|
2206
2581
|
Schedule.addDelay(Schedule.forever, () => Duration.seconds(1))
|
|
2207
2582
|
).pipe(
|
|
2208
|
-
|
|
2209
|
-
if (err === "done") return
|
|
2210
|
-
return
|
|
2583
|
+
Effect23.catchAll((err) => {
|
|
2584
|
+
if (err === "done") return Effect23.void;
|
|
2585
|
+
return Effect23.fail(err);
|
|
2211
2586
|
})
|
|
2212
2587
|
);
|
|
2213
2588
|
});
|
|
@@ -2223,31 +2598,174 @@ var printLog = (logEntry) => {
|
|
|
2223
2598
|
};
|
|
2224
2599
|
|
|
2225
2600
|
// src/commands/platform/index.ts
|
|
2226
|
-
var platformBase = Command12.make("platform", {}, () =>
|
|
2601
|
+
var platformBase = Command12.make("platform", {}, () => Effect24.void);
|
|
2227
2602
|
var platformCommand = platformBase.pipe(Command12.withSubcommands([deployCommand]));
|
|
2228
2603
|
|
|
2604
|
+
// src/commands/setup/index.ts
|
|
2605
|
+
import { Args as Args4, Command as Command13, Options as Options7, Prompt as Prompt3 } from "@effect/cli";
|
|
2606
|
+
import { Console as Console12, Effect as Effect25, Option as Option13 } from "effect";
|
|
2607
|
+
var fileArg2 = Args4.text({ name: "file" }).pipe(
|
|
2608
|
+
Args4.withDescription("Output file to write (default: .env.local)"),
|
|
2609
|
+
Args4.optional
|
|
2610
|
+
);
|
|
2611
|
+
var appOption6 = Options7.text("app").pipe(
|
|
2612
|
+
Options7.withAlias("a"),
|
|
2613
|
+
Options7.withDescription("Application ID to set up (skips the interactive picker)"),
|
|
2614
|
+
Options7.optional
|
|
2615
|
+
);
|
|
2616
|
+
var orgOption6 = Options7.text("org").pipe(
|
|
2617
|
+
Options7.withAlias("o"),
|
|
2618
|
+
Options7.withDescription("Only list applications belonging to this organization ID when picking"),
|
|
2619
|
+
Options7.optional
|
|
2620
|
+
);
|
|
2621
|
+
var environmentOption2 = Options7.text("environment").pipe(
|
|
2622
|
+
Options7.withAlias("e"),
|
|
2623
|
+
Options7.withDescription("Environment slug to resolve overrides for (e.g. preview)"),
|
|
2624
|
+
Options7.optional
|
|
2625
|
+
);
|
|
2626
|
+
var yesOption2 = Options7.boolean("yes").pipe(
|
|
2627
|
+
Options7.withAlias("y"),
|
|
2628
|
+
Options7.withDescription("Overwrite an existing file without confirmation")
|
|
2629
|
+
);
|
|
2630
|
+
var rotateOption2 = Options7.boolean("rotate").pipe(
|
|
2631
|
+
Options7.withDescription("Force a fresh API key, revoking the previous one")
|
|
2632
|
+
);
|
|
2633
|
+
var setupCommand = Command13.make(
|
|
2634
|
+
"setup",
|
|
2635
|
+
{
|
|
2636
|
+
file: fileArg2,
|
|
2637
|
+
app: appOption6,
|
|
2638
|
+
org: orgOption6,
|
|
2639
|
+
environment: environmentOption2,
|
|
2640
|
+
yes: yesOption2,
|
|
2641
|
+
rotate: rotateOption2
|
|
2642
|
+
},
|
|
2643
|
+
({ file, app, org, environment, yes, rotate }) => Effect25.gen(function* () {
|
|
2644
|
+
const apiClient = yield* ApiClientService;
|
|
2645
|
+
const projectLink = yield* ProjectLinkService;
|
|
2646
|
+
const envFile = yield* EnvFileService;
|
|
2647
|
+
const localCredentials = yield* LocalCredentialsService;
|
|
2648
|
+
const cwd = process.cwd();
|
|
2649
|
+
const existingLink = yield* projectLink.load(cwd);
|
|
2650
|
+
let applicationId;
|
|
2651
|
+
if (Option13.isSome(existingLink)) {
|
|
2652
|
+
applicationId = existingLink.value.applicationId;
|
|
2653
|
+
if (Option13.isSome(app) || Option13.isSome(org)) {
|
|
2654
|
+
yield* Console12.log(
|
|
2655
|
+
`Note: this directory is already linked to ${applicationId}; ignoring --app/--org. Run "gigadrive unlink" first to target a different application.`
|
|
2656
|
+
);
|
|
2657
|
+
}
|
|
2658
|
+
} else {
|
|
2659
|
+
const organizationId = Option13.getOrUndefined(org);
|
|
2660
|
+
const { items: apps } = yield* apiClient.request((client) => client.applications.list({ organizationId }));
|
|
2661
|
+
if (apps.length === 0) {
|
|
2662
|
+
return yield* Effect25.fail(
|
|
2663
|
+
new NoApplicationsFoundError({
|
|
2664
|
+
message: 'No applications found. Create one in the Gigadrive console, then run "gigadrive setup" again.'
|
|
2665
|
+
})
|
|
2666
|
+
);
|
|
2667
|
+
}
|
|
2668
|
+
const explicitAppId = Option13.getOrUndefined(app);
|
|
2669
|
+
let chosen;
|
|
2670
|
+
if (explicitAppId !== void 0) {
|
|
2671
|
+
const match = apps.find((a) => a.id === explicitAppId);
|
|
2672
|
+
if (match === void 0) {
|
|
2673
|
+
return yield* Effect25.fail(
|
|
2674
|
+
new ApplicationNotFoundError({
|
|
2675
|
+
message: `Application "${explicitAppId}" was not found among your applications.`
|
|
2676
|
+
})
|
|
2677
|
+
);
|
|
2678
|
+
}
|
|
2679
|
+
chosen = match;
|
|
2680
|
+
} else if (apps.length === 1) {
|
|
2681
|
+
chosen = apps[0];
|
|
2682
|
+
} else {
|
|
2683
|
+
chosen = yield* Prompt3.run(
|
|
2684
|
+
Prompt3.select({
|
|
2685
|
+
message: "Select an application to set up",
|
|
2686
|
+
choices: apps.map((a) => ({ title: `${a.name} (${a.organization.name})`, value: a }))
|
|
2687
|
+
})
|
|
2688
|
+
);
|
|
2689
|
+
}
|
|
2690
|
+
applicationId = chosen.id;
|
|
2691
|
+
yield* projectLink.save(cwd, { applicationId, organizationId: chosen.organization.id });
|
|
2692
|
+
yield* Console12.log(`Linked this directory to application ${applicationId}.`);
|
|
2693
|
+
}
|
|
2694
|
+
const target = Option13.getOrElse(file, () => ".env.local");
|
|
2695
|
+
const environmentSlug = Option13.getOrUndefined(environment);
|
|
2696
|
+
const { items, omittedSensitive } = yield* apiClient.request(
|
|
2697
|
+
(client) => client.applications.envVars.pull(applicationId, environmentSlug ? { environment: environmentSlug } : void 0)
|
|
2698
|
+
);
|
|
2699
|
+
const existing = yield* envFile.read(target);
|
|
2700
|
+
if (Option13.isSome(existing) && !yes) {
|
|
2701
|
+
const overwrite = yield* Prompt3.run(
|
|
2702
|
+
Prompt3.confirm({ message: `${target} already exists. Overwrite?`, initial: false })
|
|
2703
|
+
);
|
|
2704
|
+
if (!overwrite) {
|
|
2705
|
+
return yield* Console12.log("Cancelled.");
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
const credentialEntries2 = yield* localCredentials.provision({
|
|
2709
|
+
applicationId,
|
|
2710
|
+
rotate,
|
|
2711
|
+
existingContent: Option13.getOrUndefined(existing)
|
|
2712
|
+
});
|
|
2713
|
+
const entries = [...items.map((v) => ({ key: v.key, value: v.value })), ...credentialEntries2];
|
|
2714
|
+
yield* envFile.write(target, entries);
|
|
2715
|
+
const gitignored = yield* envFile.ensureGitignored(cwd, target);
|
|
2716
|
+
yield* Console12.log("\nLocal dev environment ready.");
|
|
2717
|
+
yield* Console12.log(` \u2022 Wrote ${items.length} variable(s) and API credentials to ${target}.`);
|
|
2718
|
+
if (omittedSensitive > 0) {
|
|
2719
|
+
yield* Console12.log(
|
|
2720
|
+
` \u2022 Skipped ${omittedSensitive} sensitive variable(s) (only production/preview hold secrets).`
|
|
2721
|
+
);
|
|
2722
|
+
}
|
|
2723
|
+
if (gitignored) {
|
|
2724
|
+
yield* Console12.log(` \u2022 Added ${target} to .gitignore.`);
|
|
2725
|
+
}
|
|
2726
|
+
yield* Console12.log(" \u2022 Your app can now call the Gigadrive API via the SDK (GIGADRIVE_CLIENT_ID/SECRET).");
|
|
2727
|
+
}).pipe(
|
|
2728
|
+
Effect25.catchTags({
|
|
2729
|
+
// Ctrl+C during a prompt is a deliberate cancel — exit 0.
|
|
2730
|
+
QuitException: () => Console12.log("Cancelled."),
|
|
2731
|
+
NotAuthenticatedError: (err) => Console12.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
|
|
2732
|
+
Effect25.andThen(Effect25.fail(err))
|
|
2733
|
+
),
|
|
2734
|
+
ApiRequestError: (err) => Console12.error(`Setup failed: ${err.message}`).pipe(Effect25.andThen(Effect25.fail(err))),
|
|
2735
|
+
NoApplicationsFoundError: (err) => Console12.error(err.message).pipe(Effect25.andThen(Effect25.fail(err))),
|
|
2736
|
+
ApplicationNotFoundError: (err) => Console12.error(err.message).pipe(Effect25.andThen(Effect25.fail(err))),
|
|
2737
|
+
ProjectLinkReadError: (err) => Console12.error(err.message).pipe(Effect25.andThen(Effect25.fail(err))),
|
|
2738
|
+
ProjectLinkWriteError: (err) => Console12.error(err.message).pipe(Effect25.andThen(Effect25.fail(err))),
|
|
2739
|
+
EnvFileReadError: (err) => Console12.error(err.message).pipe(Effect25.andThen(Effect25.fail(err))),
|
|
2740
|
+
EnvFileWriteError: (err) => Console12.error(err.message).pipe(Effect25.andThen(Effect25.fail(err))),
|
|
2741
|
+
DevCredentialsStoreReadError: (err) => Console12.error(err.message).pipe(Effect25.andThen(Effect25.fail(err))),
|
|
2742
|
+
DevCredentialsStoreWriteError: (err) => Console12.error(err.message).pipe(Effect25.andThen(Effect25.fail(err)))
|
|
2743
|
+
})
|
|
2744
|
+
)
|
|
2745
|
+
);
|
|
2746
|
+
|
|
2229
2747
|
// src/commands/whoami/index.ts
|
|
2230
|
-
import { Command as
|
|
2231
|
-
import { Console as
|
|
2232
|
-
var whoamiCommand =
|
|
2748
|
+
import { Command as Command14 } from "@effect/cli";
|
|
2749
|
+
import { Console as Console13, Effect as Effect26 } from "effect";
|
|
2750
|
+
var whoamiCommand = Command14.make(
|
|
2233
2751
|
"whoami",
|
|
2234
2752
|
{},
|
|
2235
|
-
() =>
|
|
2753
|
+
() => Effect26.gen(function* () {
|
|
2236
2754
|
const auth = yield* AuthService;
|
|
2237
2755
|
const info = yield* auth.getUserInfo;
|
|
2238
|
-
yield*
|
|
2756
|
+
yield* Console13.log(JSON.stringify(info, null, 2));
|
|
2239
2757
|
}).pipe(
|
|
2240
|
-
|
|
2241
|
-
NotAuthenticatedError: () =>
|
|
2242
|
-
UserInfoFetchError: (err) =>
|
|
2243
|
-
OAuthDiscoveryError: (err) =>
|
|
2758
|
+
Effect26.catchTags({
|
|
2759
|
+
NotAuthenticatedError: () => Console13.log('You are not logged in. Run "gigadrive login" to authenticate.'),
|
|
2760
|
+
UserInfoFetchError: (err) => Console13.error(`Failed to retrieve user info: ${err.message}`),
|
|
2761
|
+
OAuthDiscoveryError: (err) => Console13.error(`Failed to retrieve user info: ${err.message}`)
|
|
2244
2762
|
})
|
|
2245
2763
|
)
|
|
2246
2764
|
);
|
|
2247
2765
|
|
|
2248
2766
|
// src/index.ts
|
|
2249
|
-
var gigadrive =
|
|
2250
|
-
|
|
2767
|
+
var gigadrive = Command15.make("gigadrive", {}, () => Effect27.void).pipe(
|
|
2768
|
+
Command15.withSubcommands([
|
|
2251
2769
|
loginCommand,
|
|
2252
2770
|
logoutCommand,
|
|
2253
2771
|
whoamiCommand,
|
|
@@ -2259,10 +2777,11 @@ var gigadrive = Command14.make("gigadrive", {}, () => Effect23.void).pipe(
|
|
|
2259
2777
|
aiCommand,
|
|
2260
2778
|
buildCommand,
|
|
2261
2779
|
debugCommand,
|
|
2262
|
-
platformCommand
|
|
2780
|
+
platformCommand,
|
|
2781
|
+
setupCommand
|
|
2263
2782
|
])
|
|
2264
2783
|
);
|
|
2265
|
-
var cli =
|
|
2784
|
+
var cli = Command15.run(gigadrive, {
|
|
2266
2785
|
name: "Gigadrive CLI",
|
|
2267
2786
|
version: package_default.version
|
|
2268
2787
|
});
|
|
@@ -2273,13 +2792,16 @@ var BaseServices = Layer.mergeAll(
|
|
|
2273
2792
|
ArchiveService.Default,
|
|
2274
2793
|
NetworkConfigLive2,
|
|
2275
2794
|
ProjectConfigService.Default,
|
|
2276
|
-
ProjectLinkService.Default
|
|
2795
|
+
ProjectLinkService.Default,
|
|
2796
|
+
EnvFileService.Default,
|
|
2797
|
+
DevCredentialsStore.Default
|
|
2277
2798
|
).pipe(Layer.provideMerge(AuthService.Default));
|
|
2278
2799
|
var ApiClientLive = Layer.provide(ApiClientService.Default, BaseServices);
|
|
2279
2800
|
var DeploymentApiLive = Layer.provide(DeploymentApiService.Default, BaseServices);
|
|
2280
|
-
var
|
|
2801
|
+
var LocalCredentialsLive = Layer.provide(LocalCredentialsService.Default, BaseServices);
|
|
2802
|
+
var ServicesLive = Layer.mergeAll(BaseServices, ApiClientLive, DeploymentApiLive, LocalCredentialsLive);
|
|
2281
2803
|
var AppLive = Layer.mergeAll(ServicesLive, Logger.minimumLogLevel(LogLevel.Info)).pipe(
|
|
2282
2804
|
Layer.provideMerge(NodeContext.layer)
|
|
2283
2805
|
);
|
|
2284
|
-
NodeRuntime.runMain(
|
|
2806
|
+
NodeRuntime.runMain(Effect27.provide(cli(process.argv), AppLive));
|
|
2285
2807
|
//# sourceMappingURL=index.mjs.map
|