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,100 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { Flags } from "@oclif/core";
4
+ import { BaseCommand } from "../base-command.js";
5
+ import { deriveKey, decrypt } from "../lib/crypto.js";
6
+ import { createApiClient } from "../lib/api.js";
7
+ import { decodeJwt } from "../lib/auth.js";
8
+ import { writeConfig, findConfigDir, loadConfig } from "../lib/config.js";
9
+ import { confirm, colors, displayVariable } from "../lib/output.js";
10
+ export default class Pull extends BaseCommand {
11
+ static description = "Pull the latest version from SlickEnv to local .env";
12
+ static examples = [
13
+ "slickenv pull",
14
+ "slickenv pull --version 2",
15
+ "slickenv pull --dry-run",
16
+ ];
17
+ static flags = {
18
+ ...BaseCommand.baseFlags,
19
+ version: Flags.integer({
20
+ description: "Pull a specific version instead of latest",
21
+ }),
22
+ "dry-run": Flags.boolean({
23
+ description: "Preview without writing to .env",
24
+ default: false,
25
+ }),
26
+ yes: Flags.boolean({
27
+ char: "y",
28
+ description: "Auto-confirm overwrite prompts",
29
+ default: false,
30
+ }),
31
+ };
32
+ async run() {
33
+ const { flags } = await this.parse(Pull);
34
+ const client = createApiClient(this.slickenvConfig.apiUrl, this.authToken);
35
+ // Fetch environment data
36
+ const pullArgs = {
37
+ projectId: this.slickenvConfig.projectId,
38
+ label: this.slickenvConfig.defaultEnvironment,
39
+ };
40
+ if (flags.version !== undefined) {
41
+ pullArgs.version = flags.version;
42
+ }
43
+ const result = await client.query("environments:pull", pullArgs);
44
+ if (!result || !result.variables) {
45
+ this.fail("No environment data found. Push first with `slickenv push`.");
46
+ }
47
+ // Get project for salt
48
+ const project = await client.query("projects:get", {
49
+ projectId: this.slickenvConfig.projectId,
50
+ });
51
+ // Derive decryption key
52
+ const jwt = decodeJwt(this.authToken);
53
+ const clerkUserId = jwt.sub;
54
+ const salt = Buffer.from(project.salt, "base64");
55
+ const key = deriveKey(clerkUserId, this.slickenvConfig.projectId, salt);
56
+ // Decrypt variables
57
+ const lines = [];
58
+ for (const v of result.variables) {
59
+ let value = v.value;
60
+ if (v.isEncrypted && v.iv) {
61
+ value = decrypt(v.value, v.iv, key);
62
+ }
63
+ lines.push(`${v.key}=${value}`);
64
+ }
65
+ const envContent = lines.join("\n") + "\n";
66
+ // Dry run: display and exit
67
+ if (flags["dry-run"]) {
68
+ this.log(`\n ${colors.info(`Preview of v${result.version}:`)} ${this.slickenvConfig.defaultEnvironment}\n`);
69
+ for (const v of result.variables) {
70
+ let value = v.value;
71
+ if (v.isEncrypted && v.iv) {
72
+ value = decrypt(v.value, v.iv, key);
73
+ }
74
+ this.log(` ${displayVariable({ key: v.key, value, visibility: v.visibility, isEncrypted: false })}`);
75
+ }
76
+ this.log("");
77
+ return;
78
+ }
79
+ // Confirm overwrite
80
+ if (!flags.yes) {
81
+ const ok = await confirm(`Overwrite .env with v${result.version} (${result.variables.length} variables)?`);
82
+ if (!ok) {
83
+ this.info("Cancelled.");
84
+ return;
85
+ }
86
+ }
87
+ // Write to .env
88
+ const envPath = join(process.cwd(), ".env");
89
+ await writeFile(envPath, envContent, "utf8");
90
+ // Update local config
91
+ const configDir = await findConfigDir();
92
+ if (configDir) {
93
+ const config = await loadConfig(configDir);
94
+ config.lastSyncedVersion = result.version;
95
+ await writeConfig(config, configDir);
96
+ }
97
+ this.success(`Pulled v${result.version} (${result.variables.length} variable${result.variables.length === 1 ? "" : "s"}).`);
98
+ }
99
+ }
100
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { BaseCommand } from "../base-command.js";
2
+ export default class Push extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ "no-color": import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ };
14
+ run(): Promise<void>;
15
+ private getCurrentVersion;
16
+ }
17
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AASjD,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW;IAC3C,OAAgB,WAAW,SAAyC;IAEpE,OAAgB,QAAQ,WAKtB;IAEF,OAAgB,KAAK;;;;;;;;MAmBnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YAgGZ,iBAAiB;CAWhC"}
@@ -0,0 +1,136 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { Flags } from "@oclif/core";
4
+ import { BaseCommand } from "../base-command.js";
5
+ import { parseEnvFile } from "../lib/parser.js";
6
+ import { deriveKey, encrypt } from "../lib/crypto.js";
7
+ import { createApiClient } from "../lib/api.js";
8
+ import { decodeJwt } from "../lib/auth.js";
9
+ import { writeConfig, findConfigDir, loadConfig } from "../lib/config.js";
10
+ import { confirm, colors } from "../lib/output.js";
11
+ export default class Push extends BaseCommand {
12
+ static description = "Push local .env changes to SlickEnv";
13
+ static examples = [
14
+ "slickenv push",
15
+ 'slickenv push --message "Update Redis URL"',
16
+ "slickenv push --force --yes",
17
+ "slickenv push --file .env.production",
18
+ ];
19
+ static flags = {
20
+ ...BaseCommand.baseFlags,
21
+ file: Flags.string({
22
+ description: "Use a different file instead of .env",
23
+ default: ".env",
24
+ }),
25
+ force: Flags.boolean({
26
+ description: "Skip conflict check and override remote",
27
+ default: false,
28
+ }),
29
+ message: Flags.string({
30
+ char: "m",
31
+ description: "Attach a description to this version",
32
+ }),
33
+ yes: Flags.boolean({
34
+ char: "y",
35
+ description: "Auto-confirm destructive prompts",
36
+ default: false,
37
+ }),
38
+ };
39
+ async run() {
40
+ const { flags } = await this.parse(Push);
41
+ // Read and parse the local .env file
42
+ const envPath = join(process.cwd(), flags.file);
43
+ let content;
44
+ try {
45
+ content = await readFile(envPath, "utf8");
46
+ }
47
+ catch {
48
+ this.fail(`File not found: ${flags.file}`);
49
+ }
50
+ const parsed = parseEnvFile(content);
51
+ if (parsed.length === 0) {
52
+ this.fail(`No variables found in ${flags.file}.`);
53
+ }
54
+ // Get project salt for encryption
55
+ const client = createApiClient(this.slickenvConfig.apiUrl, this.authToken);
56
+ const project = await client.query("projects:get", {
57
+ projectId: this.slickenvConfig.projectId,
58
+ });
59
+ if (!project?.salt) {
60
+ this.fail("Project has no encryption salt. Please re-initialise with `slickenv init`.");
61
+ }
62
+ // Derive encryption key
63
+ const jwt = decodeJwt(this.authToken);
64
+ const clerkUserId = jwt.sub;
65
+ const salt = Buffer.from(project.salt, "base64");
66
+ const key = deriveKey(clerkUserId, this.slickenvConfig.projectId, salt);
67
+ // Encrypt private variables
68
+ const variables = parsed.map((v) => {
69
+ if (v.visibility === "private") {
70
+ const { ciphertext, iv } = encrypt(v.value, key);
71
+ return {
72
+ key: v.key,
73
+ value: ciphertext,
74
+ isEncrypted: true,
75
+ iv,
76
+ visibility: v.visibility,
77
+ type: v.type,
78
+ required: v.required,
79
+ example: v.example,
80
+ };
81
+ }
82
+ return {
83
+ key: v.key,
84
+ value: v.value,
85
+ isEncrypted: false,
86
+ visibility: v.visibility,
87
+ type: v.type,
88
+ required: v.required,
89
+ example: v.example,
90
+ };
91
+ });
92
+ // Determine base version for conflict detection
93
+ const baseVersion = flags.force ? -1 : (this.slickenvConfig.lastSyncedVersion ?? 0);
94
+ // Confirm push
95
+ this.log("");
96
+ this.log(` ${colors.key(this.slickenvConfig.projectName)} / ${this.slickenvConfig.defaultEnvironment}`);
97
+ this.log(` ${parsed.length} variable${parsed.length === 1 ? "" : "s"} to push`);
98
+ this.log("");
99
+ if (!flags.yes) {
100
+ const ok = await confirm("Push these changes?");
101
+ if (!ok) {
102
+ this.info("Cancelled.");
103
+ return;
104
+ }
105
+ }
106
+ // Push to Convex
107
+ const result = await client.mutation("environments:push", {
108
+ projectId: this.slickenvConfig.projectId,
109
+ label: this.slickenvConfig.defaultEnvironment,
110
+ variables,
111
+ changeSummary: flags.message,
112
+ baseVersion: flags.force ? (await this.getCurrentVersion(client)) : baseVersion,
113
+ });
114
+ // Update local config with new version
115
+ const configDir = await findConfigDir();
116
+ if (configDir) {
117
+ const config = await loadConfig(configDir);
118
+ config.lastSyncedVersion = result.newVersion;
119
+ await writeConfig(config, configDir);
120
+ }
121
+ this.success(`Pushed v${result.newVersion} (${parsed.length} variable${parsed.length === 1 ? "" : "s"}).`);
122
+ }
123
+ async getCurrentVersion(client) {
124
+ try {
125
+ const status = await client.query("environments:getStatus", {
126
+ projectId: this.slickenvConfig.projectId,
127
+ label: this.slickenvConfig.defaultEnvironment,
128
+ });
129
+ return status?.version ?? 0;
130
+ }
131
+ catch {
132
+ return 0;
133
+ }
134
+ }
135
+ }
136
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { BaseCommand } from "../base-command.js";
2
+ export default class Rollback extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ version: import("@oclif/core/interfaces").Arg<number, {
7
+ max?: number;
8
+ min?: number;
9
+ }>;
10
+ };
11
+ static flags: {
12
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ "no-color": import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ };
17
+ run(): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=rollback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollback.d.ts","sourceRoot":"","sources":["../../src/commands/rollback.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKjD,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC/C,OAAgB,WAAW,SAA+E;IAE1G,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,IAAI;;;;;MAKlB;IAEF,OAAgB,KAAK;;;;;MAOnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA6B3B"}
@@ -0,0 +1,51 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import { BaseCommand } from "../base-command.js";
3
+ import { createApiClient } from "../lib/api.js";
4
+ import { writeConfig, findConfigDir, loadConfig } from "../lib/config.js";
5
+ import { confirm } from "../lib/output.js";
6
+ export default class Rollback extends BaseCommand {
7
+ static description = "Roll back to a previous version (creates a new version — non-destructive)";
8
+ static examples = [
9
+ "slickenv rollback 3",
10
+ "slickenv rollback 3 --yes",
11
+ ];
12
+ static args = {
13
+ version: Args.integer({
14
+ description: "Version number to roll back to",
15
+ required: true,
16
+ }),
17
+ };
18
+ static flags = {
19
+ ...BaseCommand.baseFlags,
20
+ yes: Flags.boolean({
21
+ char: "y",
22
+ description: "Auto-confirm rollback",
23
+ default: false,
24
+ }),
25
+ };
26
+ async run() {
27
+ const { args, flags } = await this.parse(Rollback);
28
+ if (!flags.yes) {
29
+ const ok = await confirm(`Roll back ${this.slickenvConfig.defaultEnvironment} to v${args.version}? This creates a new version.`);
30
+ if (!ok) {
31
+ this.info("Cancelled.");
32
+ return;
33
+ }
34
+ }
35
+ const client = createApiClient(this.slickenvConfig.apiUrl, this.authToken);
36
+ const result = await client.mutation("versions:rollback", {
37
+ projectId: this.slickenvConfig.projectId,
38
+ label: this.slickenvConfig.defaultEnvironment,
39
+ targetVersion: args.version,
40
+ });
41
+ // Update local config
42
+ const configDir = await findConfigDir();
43
+ if (configDir) {
44
+ const config = await loadConfig(configDir);
45
+ config.lastSyncedVersion = result.newVersion;
46
+ await writeConfig(config, configDir);
47
+ }
48
+ this.success(`Rolled back to v${args.version}. New active version: v${result.newVersion}.`);
49
+ }
50
+ }
51
+ //# sourceMappingURL=rollback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollback.js","sourceRoot":"","sources":["../../src/commands/rollback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC/C,MAAM,CAAU,WAAW,GAAG,2EAA2E,CAAC;IAE1G,MAAM,CAAU,QAAQ,GAAG;QACzB,qBAAqB;QACrB,2BAA2B;KAC5B,CAAC;IAEF,MAAM,CAAU,IAAI,GAAG;QACrB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC;YACpB,WAAW,EAAE,gCAAgC;YAC7C,QAAQ,EAAE,IAAI;SACf,CAAC;KACH,CAAC;IAEF,MAAM,CAAU,KAAK,GAAG;QACtB,GAAG,WAAW,CAAC,SAAS;QACxB,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;YACjB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,uBAAuB;YACpC,OAAO,EAAE,KAAK;SACf,CAAC;KACH,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEnD,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACf,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,aAAa,IAAI,CAAC,cAAc,CAAC,kBAAkB,QAAQ,IAAI,CAAC,OAAO,+BAA+B,CAAC,CAAC;YACjI,IAAI,CAAC,EAAE,EAAE,CAAC;gBACR,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACxB,OAAO;YACT,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAE3E,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,mBAA0B,EAAE;YAC/D,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,SAAS;YACxC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,kBAAkB;YAC7C,aAAa,EAAE,IAAI,CAAC,OAAO;SAC5B,CAAQ,CAAC;QAEV,sBAAsB;QACtB,MAAM,SAAS,GAAG,MAAM,aAAa,EAAE,CAAC;QACxC,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;YAC3C,MAAM,CAAC,iBAAiB,GAAG,MAAM,CAAC,UAAU,CAAC;YAC7C,MAAM,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACvC,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,mBAAmB,IAAI,CAAC,OAAO,0BAA0B,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;IAC9F,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from "../base-command.js";
2
+ export default class Share extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ "public-only": import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ reveal: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ "no-color": import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=share.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share.d.ts","sourceRoot":"","sources":["../../src/commands/share.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAMjD,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,WAAW;IAC5C,OAAgB,WAAW,SAA0D;IAErF,OAAgB,QAAQ,WAItB;IAEF,OAAgB,KAAK;;;;;;MAUnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAkE3B"}
@@ -0,0 +1,79 @@
1
+ import { Flags } from "@oclif/core";
2
+ import { BaseCommand } from "../base-command.js";
3
+ import { createApiClient } from "../lib/api.js";
4
+ import { confirm, colors, mask } from "../lib/output.js";
5
+ import { decodeJwt } from "../lib/auth.js";
6
+ import { deriveKey, decrypt } from "../lib/crypto.js";
7
+ export default class Share extends BaseCommand {
8
+ static description = "Generate a shareable view of the current environment";
9
+ static examples = [
10
+ "slickenv share",
11
+ "slickenv share --public-only",
12
+ "slickenv share --reveal",
13
+ ];
14
+ static flags = {
15
+ ...BaseCommand.baseFlags,
16
+ "public-only": Flags.boolean({
17
+ description: "Show only public variables",
18
+ default: false,
19
+ }),
20
+ reveal: Flags.boolean({
21
+ description: "Show private variable values in plaintext (requires confirmation)",
22
+ default: false,
23
+ }),
24
+ };
25
+ async run() {
26
+ const { flags } = await this.parse(Share);
27
+ if (flags.reveal && !flags["public-only"]) {
28
+ const ok = await confirm("Reveal all private variable values in plaintext?");
29
+ if (!ok) {
30
+ this.info("Cancelled.");
31
+ return;
32
+ }
33
+ }
34
+ const client = createApiClient(this.slickenvConfig.apiUrl, this.authToken);
35
+ // For reveal mode, pull the full encrypted data and decrypt locally
36
+ if (flags.reveal) {
37
+ const result = await client.query("environments:pull", {
38
+ projectId: this.slickenvConfig.projectId,
39
+ label: this.slickenvConfig.defaultEnvironment,
40
+ });
41
+ const project = await client.query("projects:get", {
42
+ projectId: this.slickenvConfig.projectId,
43
+ });
44
+ const jwt = decodeJwt(this.authToken);
45
+ const key = deriveKey(jwt.sub, this.slickenvConfig.projectId, Buffer.from(project.salt, "base64"));
46
+ this.log("");
47
+ this.log(` ${colors.key(project.name)} / ${this.slickenvConfig.defaultEnvironment} v${result.version}`);
48
+ this.log("");
49
+ const vars = flags["public-only"]
50
+ ? result.variables.filter((v) => v.visibility === "public")
51
+ : result.variables;
52
+ for (const v of vars) {
53
+ const value = v.isEncrypted && v.iv ? decrypt(v.value, v.iv, key) : v.value;
54
+ this.log(` ${v.key}=${value}`);
55
+ }
56
+ this.log("");
57
+ this.log(` ${vars.length} variable${vars.length === 1 ? "" : "s"}`);
58
+ this.log("");
59
+ return;
60
+ }
61
+ // Non-reveal mode: use the snapshot endpoint (masks private values)
62
+ const snapshot = await client.query("sharing:createSnapshot", {
63
+ projectId: this.slickenvConfig.projectId,
64
+ label: this.slickenvConfig.defaultEnvironment,
65
+ publicOnly: flags["public-only"],
66
+ });
67
+ this.log("");
68
+ this.log(` ${colors.key(snapshot.project.name)} / ${snapshot.label} v${snapshot.version}`);
69
+ this.log("");
70
+ for (const v of snapshot.variables) {
71
+ const displayValue = v.visibility === "private" ? mask() : v.value;
72
+ this.log(` ${v.key}=${displayValue}`);
73
+ }
74
+ this.log("");
75
+ this.log(` ${snapshot.variables.length} variable${snapshot.variables.length === 1 ? "" : "s"}`);
76
+ this.log("");
77
+ }
78
+ }
79
+ //# sourceMappingURL=share.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share.js","sourceRoot":"","sources":["../../src/commands/share.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from "../base-command.js";
2
+ export default class Status extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ "no-color": import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOjD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW;IAC7C,OAAgB,WAAW,SAAyD;IAEpF,OAAgB,QAAQ,WAEtB;IAEF,OAAgB,KAAK;;;;MAEnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAkF3B"}
@@ -0,0 +1,93 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { BaseCommand } from "../base-command.js";
4
+ import { createApiClient } from "../lib/api.js";
5
+ import { parseEnvFile } from "../lib/parser.js";
6
+ import { decodeJwt } from "../lib/auth.js";
7
+ import { deriveKey, decrypt } from "../lib/crypto.js";
8
+ import { colors, symbols } from "../lib/output.js";
9
+ export default class Status extends BaseCommand {
10
+ static description = "Show what's different between local .env and remote";
11
+ static examples = [
12
+ "slickenv status",
13
+ ];
14
+ static flags = {
15
+ ...BaseCommand.baseFlags,
16
+ };
17
+ async run() {
18
+ const client = createApiClient(this.slickenvConfig.apiUrl, this.authToken);
19
+ // Get remote status
20
+ const status = await client.query("environments:getStatus", {
21
+ projectId: this.slickenvConfig.projectId,
22
+ label: this.slickenvConfig.defaultEnvironment,
23
+ });
24
+ this.log("");
25
+ this.log(` ${colors.key(status.project.name)} / ${this.slickenvConfig.defaultEnvironment}`);
26
+ if (!status.exists) {
27
+ this.log(` ${colors.info("No remote environment yet. Run")} slickenv push ${colors.info("to create one.")}`);
28
+ this.log("");
29
+ return;
30
+ }
31
+ this.log(` Remote: v${status.version} (${status.variableCount} variables)`);
32
+ this.log(` Synced: v${this.slickenvConfig.lastSyncedVersion ?? "none"}`);
33
+ // Parse local .env
34
+ let localVars;
35
+ try {
36
+ const content = await readFile(join(process.cwd(), ".env"), "utf8");
37
+ const parsed = parseEnvFile(content);
38
+ localVars = new Map(parsed.map((v) => [v.key, v.value]));
39
+ }
40
+ catch {
41
+ this.log(` Local: No .env file found`);
42
+ this.log("");
43
+ return;
44
+ }
45
+ this.log(` Local: ${localVars.size} variables`);
46
+ // Fetch remote variables for comparison
47
+ const remote = await client.query("environments:pull", {
48
+ projectId: this.slickenvConfig.projectId,
49
+ label: this.slickenvConfig.defaultEnvironment,
50
+ });
51
+ // Derive key for decryption
52
+ const project = await client.query("projects:get", {
53
+ projectId: this.slickenvConfig.projectId,
54
+ });
55
+ const jwt = decodeJwt(this.authToken);
56
+ const key = deriveKey(jwt.sub, this.slickenvConfig.projectId, Buffer.from(project.salt, "base64"));
57
+ const remoteVars = new Map();
58
+ for (const v of remote.variables) {
59
+ const value = v.isEncrypted && v.iv ? decrypt(v.value, v.iv, key) : v.value;
60
+ remoteVars.set(v.key, value);
61
+ }
62
+ // Compute diff
63
+ const allKeys = new Set([...localVars.keys(), ...remoteVars.keys()]);
64
+ const added = [];
65
+ const removed = [];
66
+ const modified = [];
67
+ for (const k of allKeys) {
68
+ const local = localVars.get(k);
69
+ const remote = remoteVars.get(k);
70
+ if (local !== undefined && remote === undefined)
71
+ added.push(k);
72
+ else if (local === undefined && remote !== undefined)
73
+ removed.push(k);
74
+ else if (local !== remote)
75
+ modified.push(k);
76
+ }
77
+ if (added.length === 0 && removed.length === 0 && modified.length === 0) {
78
+ this.log(`\n ${colors.success(symbols.success)} In sync. No changes.\n`);
79
+ return;
80
+ }
81
+ this.log("");
82
+ for (const k of added)
83
+ this.log(` ${colors.success(symbols.added)} ${k}`);
84
+ for (const k of removed)
85
+ this.log(` ${colors.error(symbols.removed)} ${k}`);
86
+ for (const k of modified)
87
+ this.log(` ${colors.warning(symbols.modified)} ${k}`);
88
+ this.log("");
89
+ this.log(` ${added.length} added, ${removed.length} removed, ${modified.length} modified`);
90
+ this.log("");
91
+ }
92
+ }
93
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from "../base-command.js";
2
+ export default class Versions extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
7
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ "no-color": import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=versions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versions.d.ts","sourceRoot":"","sources":["../../src/commands/versions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIjD,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC/C,OAAgB,WAAW,SAAsD;IAEjF,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,KAAK;;;;;MAMnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA2B3B"}