slickenv 0.0.1

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.
Files changed (83) hide show
  1. package/bin/run.js +4 -0
  2. package/dist/base-command.d.ts +24 -0
  3. package/dist/base-command.d.ts.map +1 -0
  4. package/dist/base-command.js +74 -0
  5. package/dist/base-command.js.map +1 -0
  6. package/dist/commands/diff.d.ts +22 -0
  7. package/dist/commands/diff.d.ts.map +1 -0
  8. package/dist/commands/diff.js +52 -0
  9. package/dist/commands/diff.js.map +1 -0
  10. package/dist/commands/export.d.ts +13 -0
  11. package/dist/commands/export.d.ts.map +1 -0
  12. package/dist/commands/export.js +61 -0
  13. package/dist/commands/export.js.map +1 -0
  14. package/dist/commands/init.d.ts +17 -0
  15. package/dist/commands/init.d.ts.map +1 -0
  16. package/dist/commands/init.js +90 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/login.d.ts +16 -0
  19. package/dist/commands/login.d.ts.map +1 -0
  20. package/dist/commands/login.js +83 -0
  21. package/dist/commands/login.js.map +1 -0
  22. package/dist/commands/logout.d.ts +14 -0
  23. package/dist/commands/logout.d.ts.map +1 -0
  24. package/dist/commands/logout.js +24 -0
  25. package/dist/commands/logout.js.map +1 -0
  26. package/dist/commands/pull.d.ts +15 -0
  27. package/dist/commands/pull.d.ts.map +1 -0
  28. package/dist/commands/pull.js +100 -0
  29. package/dist/commands/pull.js.map +1 -0
  30. package/dist/commands/push.d.ts +17 -0
  31. package/dist/commands/push.d.ts.map +1 -0
  32. package/dist/commands/push.js +136 -0
  33. package/dist/commands/push.js.map +1 -0
  34. package/dist/commands/rollback.d.ts +19 -0
  35. package/dist/commands/rollback.d.ts.map +1 -0
  36. package/dist/commands/rollback.js +51 -0
  37. package/dist/commands/rollback.js.map +1 -0
  38. package/dist/commands/share.d.ts +14 -0
  39. package/dist/commands/share.d.ts.map +1 -0
  40. package/dist/commands/share.js +79 -0
  41. package/dist/commands/share.js.map +1 -0
  42. package/dist/commands/status.d.ts +12 -0
  43. package/dist/commands/status.d.ts.map +1 -0
  44. package/dist/commands/status.js +93 -0
  45. package/dist/commands/status.js.map +1 -0
  46. package/dist/commands/versions.d.ts +13 -0
  47. package/dist/commands/versions.d.ts.map +1 -0
  48. package/dist/commands/versions.js +42 -0
  49. package/dist/commands/versions.js.map +1 -0
  50. package/dist/lib/api.d.ts +11 -0
  51. package/dist/lib/api.d.ts.map +1 -0
  52. package/dist/lib/api.js +32 -0
  53. package/dist/lib/api.js.map +1 -0
  54. package/dist/lib/auth.d.ts +24 -0
  55. package/dist/lib/auth.d.ts.map +1 -0
  56. package/dist/lib/auth.js +91 -0
  57. package/dist/lib/auth.js.map +1 -0
  58. package/dist/lib/config.d.ts +22 -0
  59. package/dist/lib/config.d.ts.map +1 -0
  60. package/dist/lib/config.js +71 -0
  61. package/dist/lib/config.js.map +1 -0
  62. package/dist/lib/crypto.d.ts +30 -0
  63. package/dist/lib/crypto.d.ts.map +1 -0
  64. package/dist/lib/crypto.js +56 -0
  65. package/dist/lib/crypto.js.map +1 -0
  66. package/dist/lib/errors.d.ts +17 -0
  67. package/dist/lib/errors.d.ts.map +1 -0
  68. package/dist/lib/errors.js +33 -0
  69. package/dist/lib/errors.js.map +1 -0
  70. package/dist/lib/keychain.d.ts +14 -0
  71. package/dist/lib/keychain.d.ts.map +1 -0
  72. package/dist/lib/keychain.js +85 -0
  73. package/dist/lib/keychain.js.map +1 -0
  74. package/dist/lib/output.d.ts +29 -0
  75. package/dist/lib/output.d.ts.map +1 -0
  76. package/dist/lib/output.js +69 -0
  77. package/dist/lib/output.js.map +1 -0
  78. package/dist/lib/parser.d.ts +13 -0
  79. package/dist/lib/parser.d.ts.map +1 -0
  80. package/dist/lib/parser.js +150 -0
  81. package/dist/lib/parser.js.map +1 -0
  82. package/oclif.manifest.json +594 -0
  83. package/package.json +69 -0
@@ -0,0 +1,42 @@
1
+ import { Flags } from "@oclif/core";
2
+ import { BaseCommand } from "../base-command.js";
3
+ import { createApiClient } from "../lib/api.js";
4
+ import { colors } from "../lib/output.js";
5
+ export default class Versions extends BaseCommand {
6
+ static description = "List version history for the current environment";
7
+ static examples = [
8
+ "slickenv versions",
9
+ "slickenv versions --limit 10",
10
+ ];
11
+ static flags = {
12
+ ...BaseCommand.baseFlags,
13
+ limit: Flags.integer({
14
+ description: "Number of versions to show",
15
+ default: 20,
16
+ }),
17
+ };
18
+ async run() {
19
+ const { flags } = await this.parse(Versions);
20
+ const client = createApiClient(this.slickenvConfig.apiUrl, this.authToken);
21
+ const result = await client.query("versions:list", {
22
+ projectId: this.slickenvConfig.projectId,
23
+ label: this.slickenvConfig.defaultEnvironment,
24
+ limit: flags.limit,
25
+ });
26
+ if (!result.versions || result.versions.length === 0) {
27
+ this.info("No versions found. Push first with `slickenv push`.");
28
+ return;
29
+ }
30
+ this.log("");
31
+ this.log(` ${colors.key(this.slickenvConfig.projectName)} / ${this.slickenvConfig.defaultEnvironment}`);
32
+ this.log("");
33
+ for (const v of result.versions) {
34
+ const date = new Date(v.createdAt).toLocaleString();
35
+ const activeMarker = v.isActive ? ` ${colors.success("(active)")}` : "";
36
+ const summary = v.changeSummary ? ` ${colors.info(v.changeSummary)}` : "";
37
+ this.log(` ${colors.version(`v${v.version}`)} ${colors.timestamp(date)} ${v.variableCount} vars${activeMarker}${summary}`);
38
+ }
39
+ this.log("");
40
+ }
41
+ }
42
+ //# sourceMappingURL=versions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versions.js","sourceRoot":"","sources":["../../src/commands/versions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC/C,MAAM,CAAU,WAAW,GAAG,kDAAkD,CAAC;IAEjF,MAAM,CAAU,QAAQ,GAAG;QACzB,mBAAmB;QACnB,8BAA8B;KAC/B,CAAC;IAEF,MAAM,CAAU,KAAK,GAAG;QACtB,GAAG,WAAW,CAAC,SAAS;QACxB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC;YACnB,WAAW,EAAE,4BAA4B;YACzC,OAAO,EAAE,EAAE;SACZ,CAAC;KACH,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAE3E,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,eAAsB,EAAE;YACxD,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,SAAS;YACxC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,kBAAkB;YAC7C,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAQ,CAAC;QAEV,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrD,IAAI,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,EAAE,CAAC,CAAC;QACzG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEb,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,cAAc,EAAE,CAAC;YACpD,MAAM,YAAY,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxE,MAAM,OAAO,GAAG,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,IAAI,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,aAAa,QAAQ,YAAY,GAAG,OAAO,EAAE,CAAC,CAAC;QAChI,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACf,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ /**
3
+ * Create an authenticated Convex HTTP client.
4
+ */
5
+ export declare function createApiClient(apiUrl?: string, token?: string): ConvexHttpClient;
6
+ /**
7
+ * Log an API request with sensitive headers redacted.
8
+ * Only outputs in verbose mode.
9
+ */
10
+ export declare function logRequest(method: string, url: string, headers: Record<string, string>, verbose: boolean): void;
11
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKlD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,gBAAgB,CAelB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,EAAE,OAAO,GACf,IAAI,CASN"}
@@ -0,0 +1,32 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ import { sanitizeForLogging } from "./output.js";
3
+ const DEFAULT_API_URL = process.env.SLICKENV_API_URL ?? "https://apienv.slickspender.com";
4
+ /**
5
+ * Create an authenticated Convex HTTP client.
6
+ */
7
+ export function createApiClient(apiUrl, token) {
8
+ const url = apiUrl ?? DEFAULT_API_URL;
9
+ const isLocalhost = url.startsWith("http://localhost") || url.startsWith("http://127.0.0.1");
10
+ if (!url.startsWith("https://") && !isLocalhost) {
11
+ throw new Error("API URL must use HTTPS.");
12
+ }
13
+ const client = new ConvexHttpClient(url);
14
+ if (token) {
15
+ client.setAuth(token);
16
+ }
17
+ return client;
18
+ }
19
+ /**
20
+ * Log an API request with sensitive headers redacted.
21
+ * Only outputs in verbose mode.
22
+ */
23
+ export function logRequest(method, url, headers, verbose) {
24
+ if (!verbose)
25
+ return;
26
+ const safeHeaders = { ...headers };
27
+ if (safeHeaders["Authorization"]) {
28
+ safeHeaders["Authorization"] = "Bearer [REDACTED]";
29
+ }
30
+ console.debug(`→ ${method} ${url}`, sanitizeForLogging(safeHeaders));
31
+ }
32
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,iCAAiC,CAAC;AAE1F;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAe,EACf,KAAc;IAEd,MAAM,GAAG,GAAG,MAAM,IAAI,eAAe,CAAC;IAEtC,MAAM,WAAW,GAAG,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;IAC7F,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAEzC,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,MAAc,EACd,GAAW,EACX,OAA+B,EAC/B,OAAgB;IAEhB,IAAI,CAAC,OAAO;QAAE,OAAO;IAErB,MAAM,WAAW,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IACnC,IAAI,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC;QACjC,WAAW,CAAC,eAAe,CAAC,GAAG,mBAAmB,CAAC;IACrD,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,KAAK,MAAM,IAAI,GAAG,EAAE,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Decode a JWT payload without verifying the signature.
3
+ * Signature verification is done server-side by Convex.
4
+ */
5
+ export declare function decodeJwt(token: string): Record<string, unknown>;
6
+ /**
7
+ * Check if a JWT is expired.
8
+ */
9
+ export declare function isExpired(token: string): boolean;
10
+ /**
11
+ * Check if a JWT is within 5 minutes of expiry.
12
+ */
13
+ export declare function isNearExpiry(token: string): boolean;
14
+ /**
15
+ * Resolve token from all sources.
16
+ * Priority: SLICKENV_TOKEN env → OS keychain → file fallback.
17
+ */
18
+ export declare function resolveToken(): Promise<string | null>;
19
+ /**
20
+ * Get a valid auth token, refreshing if near expiry.
21
+ * Throws AuthError if no token or token is expired.
22
+ */
23
+ export declare function getValidToken(): Promise<string>;
24
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYhE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIhD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKnD;AAED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ3D;AAmBD;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CA6BrD"}
@@ -0,0 +1,91 @@
1
+ import { getStoredToken, storeToken } from "./keychain.js";
2
+ import { AuthError } from "./errors.js";
3
+ const CLERK_API_URL = "https://api.clerk.dev";
4
+ /**
5
+ * Decode a JWT payload without verifying the signature.
6
+ * Signature verification is done server-side by Convex.
7
+ */
8
+ export function decodeJwt(token) {
9
+ const parts = token.split(".");
10
+ if (parts.length !== 3) {
11
+ throw new AuthError("TOKEN_INVALID", "Authentication token is invalid. Run `slickenv login`.");
12
+ }
13
+ try {
14
+ const payload = Buffer.from(parts[1], "base64url").toString("utf8");
15
+ return JSON.parse(payload);
16
+ }
17
+ catch {
18
+ throw new AuthError("TOKEN_INVALID", "Authentication token is invalid. Run `slickenv login`.");
19
+ }
20
+ }
21
+ /**
22
+ * Check if a JWT is expired.
23
+ */
24
+ export function isExpired(token) {
25
+ const { exp } = decodeJwt(token);
26
+ if (typeof exp !== "number")
27
+ return true;
28
+ return exp < Math.floor(Date.now() / 1000);
29
+ }
30
+ /**
31
+ * Check if a JWT is within 5 minutes of expiry.
32
+ */
33
+ export function isNearExpiry(token) {
34
+ const { exp } = decodeJwt(token);
35
+ if (typeof exp !== "number")
36
+ return true;
37
+ const fiveMinutesFromNow = Math.floor(Date.now() / 1000) + 300;
38
+ return exp < fiveMinutesFromNow;
39
+ }
40
+ /**
41
+ * Resolve token from all sources.
42
+ * Priority: SLICKENV_TOKEN env → OS keychain → file fallback.
43
+ */
44
+ export async function resolveToken() {
45
+ // 1. Environment variable (CI/CD)
46
+ if (process.env.SLICKENV_TOKEN) {
47
+ return process.env.SLICKENV_TOKEN;
48
+ }
49
+ // 2. OS keychain + file fallback
50
+ return getStoredToken();
51
+ }
52
+ /**
53
+ * Attempt to refresh an expiring token via Clerk.
54
+ */
55
+ async function refreshToken(token) {
56
+ const response = await fetch(`${CLERK_API_URL}/tokens/refresh`, {
57
+ method: "POST",
58
+ headers: { Authorization: `Bearer ${token}` },
59
+ });
60
+ if (!response.ok) {
61
+ throw new Error("Refresh failed");
62
+ }
63
+ const data = (await response.json());
64
+ return data.jwt;
65
+ }
66
+ /**
67
+ * Get a valid auth token, refreshing if near expiry.
68
+ * Throws AuthError if no token or token is expired.
69
+ */
70
+ export async function getValidToken() {
71
+ const token = await resolveToken();
72
+ if (!token) {
73
+ throw new AuthError("NOT_AUTHENTICATED", "Not authenticated. Run `slickenv login`.");
74
+ }
75
+ if (isExpired(token)) {
76
+ throw new AuthError("TOKEN_EXPIRED", "Session expired. Run `slickenv login` to reconnect.");
77
+ }
78
+ if (isNearExpiry(token)) {
79
+ try {
80
+ const refreshed = await refreshToken(token);
81
+ await storeToken(refreshed);
82
+ return refreshed;
83
+ }
84
+ catch {
85
+ // Refresh failed but token still valid — proceed
86
+ return token;
87
+ }
88
+ }
89
+ return token;
90
+ }
91
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { SlickEnvConfig, GlobalConfig } from "@slickenv/types";
2
+ export declare const CONFIG_FILENAME = ".slickenv";
3
+ /**
4
+ * Walk up from `startDir` looking for a .slickenv config file.
5
+ * Returns the directory containing it, or null if not found.
6
+ */
7
+ export declare function findConfigDir(startDir?: string): Promise<string | null>;
8
+ /**
9
+ * Load the .slickenv config from the current or parent directory.
10
+ * Throws ConfigError if not found or invalid.
11
+ */
12
+ export declare function loadConfig(startDir?: string): Promise<SlickEnvConfig>;
13
+ /**
14
+ * Write a .slickenv config file to the given directory.
15
+ */
16
+ export declare function writeConfig(config: SlickEnvConfig, dir: string): Promise<void>;
17
+ /**
18
+ * Load global CLI config from ~/.slickenv/config.json.
19
+ * Returns empty config if file doesn't exist.
20
+ */
21
+ export declare function loadGlobalConfig(): Promise<GlobalConfig>;
22
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpE,eAAO,MAAM,eAAe,cAAc,CAAC;AAI3C;;;GAGG;AACH,wBAAsB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAc7E;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA0B3E;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpF;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,YAAY,CAAC,CAO9D"}
@@ -0,0 +1,71 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { join, dirname } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { ConfigError } from "./errors.js";
5
+ export const CONFIG_FILENAME = ".slickenv";
6
+ const GLOBAL_CONFIG_DIR = join(homedir(), ".slickenv");
7
+ const GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, "config.json");
8
+ /**
9
+ * Walk up from `startDir` looking for a .slickenv config file.
10
+ * Returns the directory containing it, or null if not found.
11
+ */
12
+ export async function findConfigDir(startDir) {
13
+ let dir = startDir ?? process.cwd();
14
+ // eslint-disable-next-line no-constant-condition
15
+ while (true) {
16
+ try {
17
+ await readFile(join(dir, CONFIG_FILENAME), "utf8");
18
+ return dir;
19
+ }
20
+ catch {
21
+ const parent = dirname(dir);
22
+ if (parent === dir)
23
+ return null; // reached filesystem root
24
+ dir = parent;
25
+ }
26
+ }
27
+ }
28
+ /**
29
+ * Load the .slickenv config from the current or parent directory.
30
+ * Throws ConfigError if not found or invalid.
31
+ */
32
+ export async function loadConfig(startDir) {
33
+ const configDir = await findConfigDir(startDir);
34
+ if (!configDir) {
35
+ throw new ConfigError("No .slickenv config found.\nRun `slickenv init` to set up this project.");
36
+ }
37
+ try {
38
+ const raw = await readFile(join(configDir, CONFIG_FILENAME), "utf8");
39
+ const parsed = JSON.parse(raw);
40
+ if (!parsed.projectId || !parsed.projectName || !parsed.apiUrl) {
41
+ throw new ConfigError("Your .slickenv config file appears to be corrupted or invalid.\nTo fix: delete .slickenv and run `slickenv init` to re-link this project.");
42
+ }
43
+ return parsed;
44
+ }
45
+ catch (error) {
46
+ if (error instanceof ConfigError)
47
+ throw error;
48
+ throw new ConfigError("Your .slickenv config file appears to be corrupted or invalid.\nTo fix: delete .slickenv and run `slickenv init` to re-link this project.");
49
+ }
50
+ }
51
+ /**
52
+ * Write a .slickenv config file to the given directory.
53
+ */
54
+ export async function writeConfig(config, dir) {
55
+ const content = JSON.stringify(config, null, 2) + "\n";
56
+ await writeFile(join(dir, CONFIG_FILENAME), content, "utf8");
57
+ }
58
+ /**
59
+ * Load global CLI config from ~/.slickenv/config.json.
60
+ * Returns empty config if file doesn't exist.
61
+ */
62
+ export async function loadGlobalConfig() {
63
+ try {
64
+ const raw = await readFile(GLOBAL_CONFIG_FILE, "utf8");
65
+ return JSON.parse(raw);
66
+ }
67
+ catch {
68
+ return {};
69
+ }
70
+ }
71
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAC3C,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACvD,MAAM,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAiB;IACnD,IAAI,GAAG,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAEpC,iDAAiD;IACjD,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC;YACnD,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,MAAM,KAAK,GAAG;gBAAE,OAAO,IAAI,CAAC,CAAC,0BAA0B;YAC3D,GAAG,GAAG,MAAM,CAAC;QACf,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAiB;IAChD,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;IAEhD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,WAAW,CACnB,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;QAEjD,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC/D,MAAM,IAAI,WAAW,CACnB,2IAA2I,CAC5I,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,WAAW;YAAE,MAAM,KAAK,CAAC;QAC9C,MAAM,IAAI,WAAW,CACnB,2IAA2I,CAC5I,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAsB,EAAE,GAAW;IACnE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACvD,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Derive an AES-256 encryption key from user identity and project context.
3
+ *
4
+ * Uses PBKDF2-SHA256 with 100,000 iterations.
5
+ * The derived key is never stored — re-derived on each operation.
6
+ */
7
+ export declare function deriveKey(clerkUserId: string, projectId: string, salt: Buffer): Buffer;
8
+ /**
9
+ * Encrypt a plaintext string using AES-256-GCM.
10
+ *
11
+ * Returns base64-encoded ciphertext (with auth tag appended) and IV.
12
+ * A fresh random 12-byte IV is generated for each call.
13
+ */
14
+ export declare function encrypt(plaintext: string, key: Buffer): {
15
+ ciphertext: string;
16
+ iv: string;
17
+ };
18
+ /**
19
+ * Decrypt an AES-256-GCM ciphertext.
20
+ *
21
+ * Expects base64-encoded ciphertext (with auth tag appended) and IV.
22
+ * Throws if the auth tag is invalid (tampered data).
23
+ */
24
+ export declare function decrypt(ciphertext: string, iv: string, key: Buffer): string;
25
+ /**
26
+ * Generate a random 32-byte salt for project encryption.
27
+ * Returns base64-encoded string for storage in Convex.
28
+ */
29
+ export declare function generateProjectSalt(): string;
30
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/lib/crypto.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACX,MAAM,CAQR;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CACrB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAiBpC;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CACrB,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,GACV,MAAM,CAcR;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C"}
@@ -0,0 +1,56 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, } from "node:crypto";
2
+ /**
3
+ * Derive an AES-256 encryption key from user identity and project context.
4
+ *
5
+ * Uses PBKDF2-SHA256 with 100,000 iterations.
6
+ * The derived key is never stored — re-derived on each operation.
7
+ */
8
+ export function deriveKey(clerkUserId, projectId, salt) {
9
+ return pbkdf2Sync(`${clerkUserId}:${projectId}`, salt, 100_000, 32, // 256 bits
10
+ "sha256");
11
+ }
12
+ /**
13
+ * Encrypt a plaintext string using AES-256-GCM.
14
+ *
15
+ * Returns base64-encoded ciphertext (with auth tag appended) and IV.
16
+ * A fresh random 12-byte IV is generated for each call.
17
+ */
18
+ export function encrypt(plaintext, key) {
19
+ const iv = randomBytes(12); // 96-bit IV as recommended for GCM
20
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
21
+ const encrypted = Buffer.concat([
22
+ cipher.update(plaintext, "utf8"),
23
+ cipher.final(),
24
+ ]);
25
+ const tag = cipher.getAuthTag(); // 128-bit auth tag
26
+ // Append auth tag to ciphertext for storage
27
+ const combined = Buffer.concat([encrypted, tag]);
28
+ return {
29
+ ciphertext: combined.toString("base64"),
30
+ iv: iv.toString("base64"),
31
+ };
32
+ }
33
+ /**
34
+ * Decrypt an AES-256-GCM ciphertext.
35
+ *
36
+ * Expects base64-encoded ciphertext (with auth tag appended) and IV.
37
+ * Throws if the auth tag is invalid (tampered data).
38
+ */
39
+ export function decrypt(ciphertext, iv, key) {
40
+ const combined = Buffer.from(ciphertext, "base64");
41
+ const ivBuf = Buffer.from(iv, "base64");
42
+ // Last 16 bytes are the GCM auth tag
43
+ const tag = combined.subarray(combined.length - 16);
44
+ const encrypted = combined.subarray(0, combined.length - 16);
45
+ const decipher = createDecipheriv("aes-256-gcm", key, ivBuf);
46
+ decipher.setAuthTag(tag);
47
+ return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf8");
48
+ }
49
+ /**
50
+ * Generate a random 32-byte salt for project encryption.
51
+ * Returns base64-encoded string for storage in Convex.
52
+ */
53
+ export function generateProjectSalt() {
54
+ return randomBytes(32).toString("base64");
55
+ }
56
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/lib/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,UAAU,GACX,MAAM,aAAa,CAAC;AAErB;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CACvB,WAAmB,EACnB,SAAiB,EACjB,IAAY;IAEZ,OAAO,UAAU,CACf,GAAG,WAAW,IAAI,SAAS,EAAE,EAC7B,IAAI,EACJ,OAAO,EACP,EAAE,EAAE,WAAW;IACf,QAAQ,CACT,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CACrB,SAAiB,EACjB,GAAW;IAEX,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,mCAAmC;IAC/D,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC;QAChC,MAAM,CAAC,KAAK,EAAE;KACf,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,mBAAmB;IAEpD,4CAA4C;IAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;IAEjD,OAAO;QACL,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACvC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;KAC1B,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CACrB,UAAkB,EAClB,EAAU,EACV,GAAW;IAEX,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAExC,qCAAqC;IACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAE7D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC7D,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAEzB,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAC3E,MAAM,CACP,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,17 @@
1
+ export declare class ConfigError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class AuthError extends Error {
5
+ code: string;
6
+ constructor(code: string, message: string);
7
+ }
8
+ export declare class ParseError extends Error {
9
+ line?: number;
10
+ constructor(message: string, line?: number);
11
+ }
12
+ export declare class ApiRequestError extends Error {
13
+ code: string;
14
+ data?: Record<string, unknown>;
15
+ constructor(code: string, message: string, data?: Record<string, unknown>);
16
+ }
17
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,SAAU,SAAQ,KAAK;IAClC,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;gBAEF,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAK3C;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEnB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAM1E"}
@@ -0,0 +1,33 @@
1
+ export class ConfigError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "ConfigError";
5
+ }
6
+ }
7
+ export class AuthError extends Error {
8
+ code;
9
+ constructor(code, message) {
10
+ super(message);
11
+ this.name = "AuthError";
12
+ this.code = code;
13
+ }
14
+ }
15
+ export class ParseError extends Error {
16
+ line;
17
+ constructor(message, line) {
18
+ super(message);
19
+ this.name = "ParseError";
20
+ this.line = line;
21
+ }
22
+ }
23
+ export class ApiRequestError extends Error {
24
+ code;
25
+ data;
26
+ constructor(code, message, data) {
27
+ super(message);
28
+ this.name = "ApiRequestError";
29
+ this.code = code;
30
+ this.data = data;
31
+ }
32
+ }
33
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,IAAI,CAAS;IAEb,YAAY,IAAY,EAAE,OAAe;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,IAAI,CAAU;IAEd,YAAY,OAAe,EAAE,IAAa;QACxC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,IAAI,CAAS;IACb,IAAI,CAA2B;IAE/B,YAAY,IAAY,EAAE,OAAe,EAAE,IAA8B;QACvE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Store auth token. Tries OS keychain first, falls back to file.
3
+ */
4
+ export declare function storeToken(token: string): Promise<void>;
5
+ /**
6
+ * Retrieve stored auth token.
7
+ * Priority: OS keychain → file fallback.
8
+ */
9
+ export declare function getStoredToken(): Promise<string | null>;
10
+ /**
11
+ * Delete stored auth token from all storage locations.
12
+ */
13
+ export declare function deleteToken(): Promise<void>;
14
+ //# sourceMappingURL=keychain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.d.ts","sourceRoot":"","sources":["../../src/lib/keychain.ts"],"names":[],"mappings":"AA6BA;;GAEG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe7D;AAED;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmB7D;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAgBjD"}
@@ -0,0 +1,85 @@
1
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
2
+ import { join, dirname } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { unlink } from "node:fs/promises";
5
+ const SERVICE = "slickenv-cli";
6
+ const ACCOUNT = "auth-token";
7
+ const TOKEN_FILE = join(homedir(), ".slickenv", "token");
8
+ /**
9
+ * Try to import keytar at runtime. Returns null if unavailable.
10
+ * keytar is a soft dependency — it requires native bindings.
11
+ */
12
+ async function getKeytar() {
13
+ try {
14
+ // Dynamic import with variable to prevent TS from resolving the module
15
+ const moduleName = "keytar";
16
+ return (await import(moduleName));
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ /**
23
+ * Store auth token. Tries OS keychain first, falls back to file.
24
+ */
25
+ export async function storeToken(token) {
26
+ const keytar = await getKeytar();
27
+ if (keytar) {
28
+ try {
29
+ await keytar.setPassword(SERVICE, ACCOUNT, token);
30
+ return;
31
+ }
32
+ catch {
33
+ // Keychain failed — fall through to file
34
+ }
35
+ }
36
+ // File fallback (chmod 600)
37
+ await mkdir(dirname(TOKEN_FILE), { recursive: true });
38
+ await writeFile(TOKEN_FILE, token, { encoding: "utf8", mode: 0o600 });
39
+ }
40
+ /**
41
+ * Retrieve stored auth token.
42
+ * Priority: OS keychain → file fallback.
43
+ */
44
+ export async function getStoredToken() {
45
+ const keytar = await getKeytar();
46
+ if (keytar) {
47
+ try {
48
+ const token = await keytar.getPassword(SERVICE, ACCOUNT);
49
+ if (token)
50
+ return token;
51
+ }
52
+ catch {
53
+ // Keychain failed — fall through to file
54
+ }
55
+ }
56
+ // File fallback
57
+ try {
58
+ const token = await readFile(TOKEN_FILE, "utf8");
59
+ return token.trim() || null;
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ }
65
+ /**
66
+ * Delete stored auth token from all storage locations.
67
+ */
68
+ export async function deleteToken() {
69
+ const keytar = await getKeytar();
70
+ if (keytar) {
71
+ try {
72
+ await keytar.deletePassword(SERVICE, ACCOUNT);
73
+ }
74
+ catch {
75
+ // Ignore — may not exist
76
+ }
77
+ }
78
+ try {
79
+ await unlink(TOKEN_FILE);
80
+ }
81
+ catch {
82
+ // Ignore — file may not exist
83
+ }
84
+ }
85
+ //# sourceMappingURL=keychain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/lib/keychain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,MAAM,OAAO,GAAG,cAAc,CAAC;AAC/B,MAAM,OAAO,GAAG,YAAY,CAAC;AAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAQzD;;;GAGG;AACH,KAAK,UAAU,SAAS;IACtB,IAAI,CAAC;QACH,uEAAuE;QACvE,MAAM,UAAU,GAAG,QAAQ,CAAC;QAC5B,OAAO,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,CAA4B,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAa;IAC5C,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IAEjC,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAAC,MAAM,CAAC;YACP,yCAAyC;QAC3C,CAAC;IACH,CAAC;IAED,4BAA4B;IAC5B,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IAEjC,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzD,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,yCAAyC;QAC3C,CAAC;IACH,CAAC;IAED,gBAAgB;IAChB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACjD,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IAEjC,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,8BAA8B;IAChC,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { Variable } from "@slickenv/types";
2
+ export declare const symbols: {
3
+ readonly success: "✓";
4
+ readonly warning: "⚠";
5
+ readonly error: "✗";
6
+ readonly info: "→";
7
+ readonly unchanged: "·";
8
+ readonly modified: "~";
9
+ readonly added: "+";
10
+ readonly removed: "-";
11
+ };
12
+ export declare const colors: {
13
+ readonly success: import("chalk").ChalkInstance;
14
+ readonly warning: import("chalk").ChalkInstance;
15
+ readonly error: import("chalk").ChalkInstance;
16
+ readonly info: import("chalk").ChalkInstance;
17
+ readonly key: import("chalk").ChalkInstance;
18
+ readonly value: import("chalk").ChalkInstance;
19
+ readonly privateValue: import("chalk").ChalkInstance;
20
+ readonly version: import("chalk").ChalkInstance;
21
+ readonly email: import("chalk").ChalkInstance;
22
+ readonly timestamp: import("chalk").ChalkInstance;
23
+ };
24
+ export declare const isTTY: boolean;
25
+ export declare function mask(): string;
26
+ export declare function displayVariable(variable: Pick<Variable, "key" | "value" | "visibility" | "isEncrypted">): string;
27
+ export declare function sanitizeForLogging(obj: unknown): unknown;
28
+ export declare function confirm(message: string): Promise<boolean>;
29
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhD,eAAO,MAAM,OAAO;;;;;;;;;CASV,CAAC;AAIX,eAAO,MAAM,MAAM;;;;;;;;;;;CAWT,CAAC;AAIX,eAAO,MAAM,KAAK,SAAuD,CAAC;AAI1E,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,GAAG,OAAO,GAAG,YAAY,GAAG,aAAa,CAAC,GAAG,MAAM,CAKhH;AAMD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAWxD;AAID,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgB/D"}