@chriscode/hush 5.0.0 → 5.0.2

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 (77) hide show
  1. package/dist/cli.js +39 -26
  2. package/dist/commands/check.d.ts +3 -3
  3. package/dist/commands/check.d.ts.map +1 -1
  4. package/dist/commands/check.js +27 -31
  5. package/dist/commands/decrypt.d.ts +2 -2
  6. package/dist/commands/decrypt.d.ts.map +1 -1
  7. package/dist/commands/decrypt.js +52 -55
  8. package/dist/commands/edit.d.ts +2 -2
  9. package/dist/commands/edit.d.ts.map +1 -1
  10. package/dist/commands/edit.js +10 -12
  11. package/dist/commands/encrypt.d.ts +2 -2
  12. package/dist/commands/encrypt.d.ts.map +1 -1
  13. package/dist/commands/encrypt.js +27 -29
  14. package/dist/commands/expansions.d.ts +2 -2
  15. package/dist/commands/expansions.d.ts.map +1 -1
  16. package/dist/commands/expansions.js +46 -44
  17. package/dist/commands/has.d.ts +2 -2
  18. package/dist/commands/has.d.ts.map +1 -1
  19. package/dist/commands/has.js +12 -15
  20. package/dist/commands/init.d.ts +2 -2
  21. package/dist/commands/init.d.ts.map +1 -1
  22. package/dist/commands/init.js +92 -100
  23. package/dist/commands/inspect.d.ts +2 -2
  24. package/dist/commands/inspect.d.ts.map +1 -1
  25. package/dist/commands/inspect.js +14 -16
  26. package/dist/commands/keys.d.ts +2 -1
  27. package/dist/commands/keys.d.ts.map +1 -1
  28. package/dist/commands/keys.js +47 -49
  29. package/dist/commands/list.d.ts +2 -2
  30. package/dist/commands/list.d.ts.map +1 -1
  31. package/dist/commands/list.js +11 -14
  32. package/dist/commands/migrate.d.ts +2 -1
  33. package/dist/commands/migrate.d.ts.map +1 -1
  34. package/dist/commands/migrate.js +38 -37
  35. package/dist/commands/push.d.ts +2 -2
  36. package/dist/commands/push.d.ts.map +1 -1
  37. package/dist/commands/push.js +41 -45
  38. package/dist/commands/resolve.d.ts +2 -2
  39. package/dist/commands/resolve.d.ts.map +1 -1
  40. package/dist/commands/resolve.js +25 -28
  41. package/dist/commands/run.d.ts +2 -2
  42. package/dist/commands/run.d.ts.map +1 -1
  43. package/dist/commands/run.js +35 -39
  44. package/dist/commands/set.d.ts +2 -2
  45. package/dist/commands/set.d.ts.map +1 -1
  46. package/dist/commands/set.js +61 -70
  47. package/dist/commands/skill.d.ts +2 -2
  48. package/dist/commands/skill.d.ts.map +1 -1
  49. package/dist/commands/skill.js +149 -459
  50. package/dist/commands/status.d.ts +2 -2
  51. package/dist/commands/status.d.ts.map +1 -1
  52. package/dist/commands/status.js +48 -52
  53. package/dist/commands/template.d.ts +2 -2
  54. package/dist/commands/template.d.ts.map +1 -1
  55. package/dist/commands/template.js +36 -39
  56. package/dist/commands/trace.d.ts +2 -2
  57. package/dist/commands/trace.d.ts.map +1 -1
  58. package/dist/commands/trace.js +16 -19
  59. package/dist/config/loader.js +3 -3
  60. package/dist/context.d.ts +3 -0
  61. package/dist/context.d.ts.map +1 -0
  62. package/dist/context.js +60 -0
  63. package/dist/core/parse.js +3 -3
  64. package/dist/core/sops.js +9 -9
  65. package/dist/core/template.d.ts +2 -2
  66. package/dist/core/template.d.ts.map +1 -1
  67. package/dist/core/template.js +11 -12
  68. package/dist/lib/age.js +9 -9
  69. package/dist/lib/fs.d.ts +25 -0
  70. package/dist/lib/fs.d.ts.map +1 -0
  71. package/dist/lib/fs.js +36 -0
  72. package/dist/lib/onepassword.d.ts.map +1 -1
  73. package/dist/lib/onepassword.js +41 -4
  74. package/dist/types.d.ts +92 -0
  75. package/dist/types.d.ts.map +1 -1
  76. package/dist/utils/version-check.js +5 -5
  77. package/package.json +3 -2
@@ -1,32 +1,28 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { existsSync } from 'node:fs';
3
1
  import { join, resolve } from 'node:path';
4
2
  import pc from 'picocolors';
5
- import { loadConfig, findProjectRoot } from '../config/loader.js';
6
3
  import { filterVarsForTarget } from '../core/filter.js';
7
4
  import { interpolateVars, getUnresolvedVars } from '../core/interpolate.js';
8
5
  import { mergeVars } from '../core/merge.js';
9
6
  import { parseEnvContent } from '../core/parse.js';
10
- import { decrypt as sopsDecrypt } from '../core/sops.js';
11
7
  import { loadLocalTemplates, resolveTemplateVars } from '../core/template.js';
12
8
  function getEncryptedPath(sourcePath) {
13
9
  return sourcePath + '.encrypted';
14
10
  }
15
- function getDecryptedSecrets(projectRoot, env, config) {
11
+ function getDecryptedSecrets(ctx, projectRoot, env, config) {
16
12
  const sharedEncrypted = join(projectRoot, getEncryptedPath(config.sources.shared));
17
13
  const envEncrypted = join(projectRoot, getEncryptedPath(config.sources[env]));
18
14
  const localEncrypted = join(projectRoot, getEncryptedPath(config.sources.local));
19
15
  const varSources = [];
20
- if (existsSync(sharedEncrypted)) {
21
- const content = sopsDecrypt(sharedEncrypted);
16
+ if (ctx.fs.existsSync(sharedEncrypted)) {
17
+ const content = ctx.sops.decrypt(sharedEncrypted);
22
18
  varSources.push(parseEnvContent(content));
23
19
  }
24
- if (existsSync(envEncrypted)) {
25
- const content = sopsDecrypt(envEncrypted);
20
+ if (ctx.fs.existsSync(envEncrypted)) {
21
+ const content = ctx.sops.decrypt(envEncrypted);
26
22
  varSources.push(parseEnvContent(content));
27
23
  }
28
- if (existsSync(localEncrypted)) {
29
- const content = sopsDecrypt(localEncrypted);
24
+ if (ctx.fs.existsSync(localEncrypted)) {
25
+ const content = ctx.sops.decrypt(localEncrypted);
30
26
  varSources.push(parseEnvContent(content));
31
27
  }
32
28
  if (varSources.length === 0) {
@@ -46,31 +42,31 @@ function getRootSecretsAsRecord(vars) {
46
42
  }
47
43
  return record;
48
44
  }
49
- export async function runCommand(options) {
45
+ export async function runCommand(ctx, options) {
50
46
  const { root, env, target, command } = options;
51
47
  if (!command || command.length === 0) {
52
- console.error(pc.red('Usage: hush run -- <command>'));
53
- console.error(pc.dim('Example: hush run -- npm start'));
54
- console.error(pc.dim(' hush run -e production -- npm run build'));
55
- console.error(pc.dim(' hush run --target api -- wrangler dev'));
56
- process.exit(1);
48
+ ctx.logger.error('Usage: hush run -- <command>');
49
+ ctx.logger.error(pc.dim('Example: hush run -- npm start'));
50
+ ctx.logger.error(pc.dim(' hush run -e production -- npm run build'));
51
+ ctx.logger.error(pc.dim(' hush run --target api -- wrangler dev'));
52
+ ctx.process.exit(1);
57
53
  }
58
54
  const contextDir = root;
59
- const projectInfo = findProjectRoot(contextDir);
55
+ const projectInfo = ctx.config.findProjectRoot(contextDir);
60
56
  if (!projectInfo) {
61
- console.error(pc.red('No hush.yaml found in current directory or any parent directory.'));
62
- console.error(pc.dim('Run: npx hush init'));
63
- process.exit(1);
57
+ ctx.logger.error('No hush.yaml found in current directory or any parent directory.');
58
+ ctx.logger.error(pc.dim('Run: npx hush init'));
59
+ ctx.process.exit(1);
64
60
  }
65
61
  const { projectRoot } = projectInfo;
66
- const config = loadConfig(projectRoot);
67
- const rootSecrets = getDecryptedSecrets(projectRoot, env, config);
62
+ const config = ctx.config.loadConfig(projectRoot);
63
+ const rootSecrets = getDecryptedSecrets(ctx, projectRoot, env, config);
68
64
  const rootSecretsRecord = getRootSecretsAsRecord(rootSecrets);
69
- const localTemplate = loadLocalTemplates(contextDir, env);
65
+ const localTemplate = loadLocalTemplates(contextDir, env, ctx.fs);
70
66
  // 1. Resolve Template Vars
71
67
  let templateVars = [];
72
68
  if (localTemplate.hasTemplate) {
73
- templateVars = resolveTemplateVars(localTemplate.vars, rootSecretsRecord, { processEnv: process.env });
69
+ templateVars = resolveTemplateVars(localTemplate.vars, rootSecretsRecord, { processEnv: ctx.process.env });
74
70
  }
75
71
  // 2. Resolve Target Vars
76
72
  let targetVars = [];
@@ -79,9 +75,9 @@ export async function runCommand(options) {
79
75
  ? config.targets.find(t => t.name === target)
80
76
  : config.targets.find(t => resolve(projectRoot, t.path) === resolve(contextDir));
81
77
  if (target && !targetConfig) {
82
- console.error(pc.red(`Target "${target}" not found in hush.yaml`));
83
- console.error(pc.dim(`Available targets: ${config.targets.map(t => t.name).join(', ')}`));
84
- process.exit(1);
78
+ ctx.logger.error(`Target "${target}" not found in hush.yaml`);
79
+ ctx.logger.error(pc.dim(`Available targets: ${config.targets.map(t => t.name).join(', ')}`));
80
+ ctx.process.exit(1);
85
81
  }
86
82
  if (targetConfig) {
87
83
  targetVars = filterVarsForTarget(rootSecrets, targetConfig);
@@ -89,11 +85,11 @@ export async function runCommand(options) {
89
85
  targetVars.push({ key: 'CLOUDFLARE_INCLUDE_PROCESS_ENV', value: 'true' });
90
86
  const devVarsPath = join(targetConfig.path, '.dev.vars');
91
87
  const absDevVarsPath = join(projectRoot, devVarsPath);
92
- if (existsSync(absDevVarsPath)) {
93
- console.warn(pc.yellow('\n⚠️ Wrangler Conflict Detected'));
94
- console.warn(pc.yellow(` Found .dev.vars in ${targetConfig.path}`));
95
- console.warn(pc.yellow(' Wrangler will IGNORE Hush secrets while this file exists.'));
96
- console.warn(pc.bold(` Fix: rm ${devVarsPath}\n`));
88
+ if (ctx.fs.existsSync(absDevVarsPath)) {
89
+ ctx.logger.warn('\n⚠️ Wrangler Conflict Detected');
90
+ ctx.logger.warn(` Found .dev.vars in ${targetConfig.path}`);
91
+ ctx.logger.warn(' Wrangler will IGNORE Hush secrets while this file exists.');
92
+ ctx.logger.warn(pc.bold(` Fix: rm ${devVarsPath}\n`));
97
93
  }
98
94
  }
99
95
  }
@@ -115,22 +111,22 @@ export async function runCommand(options) {
115
111
  }
116
112
  const unresolved = getUnresolvedVars(vars);
117
113
  if (unresolved.length > 0) {
118
- console.warn(pc.yellow(`Warning: ${unresolved.length} vars have unresolved references: ${unresolved.join(', ')}`));
114
+ ctx.logger.warn(`Warning: ${unresolved.length} vars have unresolved references: ${unresolved.join(', ')}`);
119
115
  }
120
116
  const childEnv = {
121
- ...process.env,
117
+ ...ctx.process.env,
122
118
  ...Object.fromEntries(vars.map(v => [v.key, v.value])),
123
119
  };
124
120
  const [cmd, ...args] = command;
125
- const result = spawnSync(cmd, args, {
121
+ const result = ctx.exec.spawnSync(cmd, args, {
126
122
  stdio: 'inherit',
127
123
  env: childEnv,
128
124
  shell: true,
129
125
  cwd: contextDir,
130
126
  });
131
127
  if (result.error) {
132
- console.error(pc.red(`Failed to execute: ${result.error.message}`));
133
- process.exit(1);
128
+ ctx.logger.error(`Failed to execute: ${result.error.message}`);
129
+ ctx.process.exit(1);
134
130
  }
135
- process.exit(result.status ?? 1);
131
+ ctx.process.exit(result.status ?? 1);
136
132
  }
@@ -1,3 +1,3 @@
1
- import type { SetOptions } from '../types.js';
2
- export declare function setCommand(options: SetOptions): Promise<void>;
1
+ import type { HushContext, SetOptions } from '../types.js';
2
+ export declare function setCommand(ctx: HushContext, options: SetOptions): Promise<void>;
3
3
  //# sourceMappingURL=set.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../src/commands/set.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA2M9C,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CnE"}
1
+ {"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../src/commands/set.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoM3D,wBAAsB,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CrF"}
@@ -1,74 +1,65 @@
1
- import { execSync } from 'node:child_process';
2
- import { existsSync, fstatSync } from 'node:fs';
3
1
  import { join } from 'node:path';
4
2
  import { platform } from 'node:os';
5
3
  import pc from 'picocolors';
6
- import { loadConfig } from '../config/loader.js';
7
4
  import { setKey } from '../core/sops.js';
8
- const STDIN_FD = 0;
9
- function hasStdinPipe() {
5
+ function hasStdinPipe(ctx) {
10
6
  try {
11
- if (process.stdin.isTTY) {
12
- return false;
13
- }
14
- const stat = fstatSync(STDIN_FD);
15
- return stat.isFIFO() || stat.isFile();
7
+ return !ctx.process.stdin.isTTY;
16
8
  }
17
9
  catch {
18
10
  return false;
19
11
  }
20
12
  }
21
- function readFromStdinPipe() {
13
+ function readFromStdinPipe(ctx) {
22
14
  return new Promise((resolve, reject) => {
23
15
  let data = '';
24
- process.stdin.setEncoding('utf8');
25
- process.stdin.on('data', (chunk) => {
16
+ ctx.process.stdin.setEncoding('utf8');
17
+ ctx.process.stdin.on('data', (chunk) => {
26
18
  data += chunk;
27
19
  });
28
- process.stdin.on('end', () => {
20
+ ctx.process.stdin.on('end', () => {
29
21
  const trimTrailingNewlines = /\n+$/;
30
22
  resolve(data.replace(trimTrailingNewlines, ''));
31
23
  });
32
- process.stdin.on('error', reject);
33
- process.stdin.resume();
24
+ ctx.process.stdin.on('error', reject);
25
+ ctx.process.stdin.resume();
34
26
  });
35
27
  }
36
- function promptViaMacOSDialog(key) {
28
+ function promptViaMacOSDialog(ctx, key) {
37
29
  try {
38
- const script = `display dialog "Enter value for ${key}:" default answer "" with hidden answer with title "Hush - Set Secret"`;
39
- const result = execSync(`osascript -e '${script}' -e 'text returned of result'`, {
30
+ const script = `display dialog "Enter value for ${key}:" default answer "" with title "Hush - Set Secret"`;
31
+ const result = ctx.exec.execSync(`osascript -e '${script}' -e 'text returned of result'`, {
40
32
  encoding: 'utf-8',
41
33
  stdio: ['pipe', 'pipe', 'pipe'],
42
34
  });
43
- return result.trim();
35
+ return result.toString().trim();
44
36
  }
45
37
  catch {
46
38
  return null;
47
39
  }
48
40
  }
49
- function promptViaWindowsDialog(key) {
41
+ function promptViaWindowsDialog(ctx, key) {
50
42
  try {
51
43
  const psScript = `
52
44
  Add-Type -AssemblyName System.Windows.Forms
53
45
  Add-Type -AssemblyName System.Drawing
54
-
46
+
55
47
  $form = New-Object System.Windows.Forms.Form
56
48
  $form.Text = 'Hush - Set Secret'
57
49
  $form.Size = New-Object System.Drawing.Size(300,150)
58
50
  $form.StartPosition = 'CenterScreen'
59
-
51
+
60
52
  $label = New-Object System.Windows.Forms.Label
61
53
  $label.Location = New-Object System.Drawing.Point(10,20)
62
54
  $label.Size = New-Object System.Drawing.Size(280,20)
63
55
  $label.Text = 'Enter value for ${key}:'
64
56
  $form.Controls.Add($label)
65
-
57
+
66
58
  $textBox = New-Object System.Windows.Forms.TextBox
67
59
  $textBox.Location = New-Object System.Drawing.Point(10,50)
68
60
  $textBox.Size = New-Object System.Drawing.Size(260,20)
69
- $textBox.PasswordChar = '*'
70
61
  $form.Controls.Add($textBox)
71
-
62
+
72
63
  $okButton = New-Object System.Windows.Forms.Button
73
64
  $okButton.Location = New-Object System.Drawing.Point(10,80)
74
65
  $okButton.Size = New-Object System.Drawing.Size(75,23)
@@ -76,11 +67,11 @@ function promptViaWindowsDialog(key) {
76
67
  $okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
77
68
  $form.AcceptButton = $okButton
78
69
  $form.Controls.Add($okButton)
79
-
70
+
80
71
  $form.TopMost = $true
81
-
72
+
82
73
  $result = $form.ShowDialog()
83
-
74
+
84
75
  if ($result -eq [System.Windows.Forms.DialogResult]::OK) {
85
76
  Write-Output $textBox.Text
86
77
  } else {
@@ -88,41 +79,41 @@ function promptViaWindowsDialog(key) {
88
79
  }
89
80
  `;
90
81
  const encodedCommand = Buffer.from(psScript, 'utf16le').toString('base64');
91
- const result = execSync(`powershell -EncodedCommand "${encodedCommand}"`, {
82
+ const result = ctx.exec.execSync(`powershell -EncodedCommand "${encodedCommand}"`, {
92
83
  encoding: 'utf-8',
93
84
  stdio: ['pipe', 'pipe', 'pipe'],
94
85
  });
95
- return result.trim();
86
+ return result.toString().trim();
96
87
  }
97
88
  catch {
98
89
  return null;
99
90
  }
100
91
  }
101
- function promptViaLinuxDialog(key) {
92
+ function promptViaLinuxDialog(ctx, key) {
102
93
  try {
103
- const result = execSync(`zenity --password --title="Hush - Set Secret" --text="Enter value for ${key}:"`, {
94
+ const result = ctx.exec.execSync(`zenity --entry --title="Hush - Set Secret" --text="Enter value for ${key}:"`, {
104
95
  encoding: 'utf-8',
105
96
  stdio: ['pipe', 'pipe', 'pipe'],
106
97
  });
107
- return result.trim();
98
+ return result.toString().trim();
108
99
  }
109
100
  catch {
110
101
  try {
111
- const result = execSync(`kdialog --password "Enter value for ${key}:" --title "Hush - Set Secret"`, {
102
+ const result = ctx.exec.execSync(`kdialog --inputbox "Enter value for ${key}:" --title "Hush - Set Secret"`, {
112
103
  encoding: 'utf-8',
113
104
  stdio: ['pipe', 'pipe', 'pipe'],
114
105
  });
115
- return result.trim();
106
+ return result.toString().trim();
116
107
  }
117
108
  catch {
118
109
  return null;
119
110
  }
120
111
  }
121
112
  }
122
- function promptViaTTY(key) {
113
+ function promptViaTTY(ctx, key) {
123
114
  return new Promise((resolve, reject) => {
124
- process.stdout.write(`Enter value for ${pc.cyan(key)}: `);
125
- const stdin = process.stdin;
115
+ ctx.process.stdout.write(`Enter value for ${pc.cyan(key)}: `);
116
+ const stdin = ctx.process.stdin;
126
117
  stdin.setRawMode(true);
127
118
  stdin.resume();
128
119
  stdin.setEncoding('utf8');
@@ -135,49 +126,49 @@ function promptViaTTY(key) {
135
126
  stdin.setRawMode(false);
136
127
  stdin.pause();
137
128
  stdin.removeListener('data', onData);
138
- process.stdout.write('\n');
129
+ ctx.process.stdout.write('\n');
139
130
  resolve(value);
140
131
  break;
141
132
  case '\u0003':
142
133
  stdin.setRawMode(false);
143
134
  stdin.pause();
144
135
  stdin.removeListener('data', onData);
145
- process.stdout.write('\n');
136
+ ctx.process.stdout.write('\n');
146
137
  reject(new Error('Cancelled'));
147
138
  break;
148
139
  case '\u007F':
149
140
  case '\b':
150
141
  if (value.length > 0) {
151
142
  value = value.slice(0, -1);
152
- process.stdout.write('\b \b');
143
+ ctx.process.stdout.write('\b \b');
153
144
  }
154
145
  break;
155
146
  default:
156
147
  value += char;
157
- process.stdout.write('\u2022');
148
+ ctx.process.stdout.write('\u2022');
158
149
  }
159
150
  };
160
151
  stdin.on('data', onData);
161
152
  });
162
153
  }
163
- async function promptForValue(key, forceGui) {
164
- if (hasStdinPipe()) {
165
- return readFromStdinPipe();
154
+ async function promptForValue(ctx, key, forceGui) {
155
+ if (hasStdinPipe(ctx)) {
156
+ return readFromStdinPipe(ctx);
166
157
  }
167
- if (process.stdin.isTTY && !forceGui) {
168
- return promptViaTTY(key);
158
+ if (ctx.process.stdin.isTTY && !forceGui) {
159
+ return promptViaTTY(ctx, key);
169
160
  }
170
- console.log(pc.dim('Opening dialog for secret input...'));
161
+ ctx.logger.log(pc.dim('Opening dialog for secret input...'));
171
162
  let value = null;
172
163
  switch (platform()) {
173
164
  case 'darwin':
174
- value = promptViaMacOSDialog(key);
165
+ value = promptViaMacOSDialog(ctx, key);
175
166
  break;
176
167
  case 'win32':
177
- value = promptViaWindowsDialog(key);
168
+ value = promptViaWindowsDialog(ctx, key);
178
169
  break;
179
170
  case 'linux':
180
- value = promptViaLinuxDialog(key);
171
+ value = promptViaLinuxDialog(ctx, key);
181
172
  break;
182
173
  }
183
174
  if (value !== null) {
@@ -188,39 +179,39 @@ async function promptForValue(key, forceGui) {
188
179
  }
189
180
  throw new Error('Dialog cancelled or failed. Interactive input requires a terminal (TTY) or a supported GUI environment.');
190
181
  }
191
- export async function setCommand(options) {
192
- const { root, file, key, gui } = options;
193
- const config = loadConfig(root);
182
+ export async function setCommand(ctx, options) {
183
+ const { root, file, key, value: inlineValue, gui } = options;
184
+ const config = ctx.config.loadConfig(root);
194
185
  const fileKey = file ?? 'shared';
195
186
  const sourcePath = config.sources[fileKey];
196
187
  const encryptedPath = join(root, sourcePath + '.encrypted');
197
188
  if (!key) {
198
- console.error(pc.red('Usage: hush set <KEY> [-e environment]'));
199
- console.error(pc.dim('Example: hush set DATABASE_URL'));
200
- console.error(pc.dim(' hush set API_KEY -e production'));
201
- console.error(pc.dim('\nTo edit all secrets in an editor, use: hush edit'));
202
- process.exit(1);
189
+ ctx.logger.error(pc.red('Usage: hush set <KEY> [-e environment]'));
190
+ ctx.logger.error(pc.dim('Example: hush set DATABASE_URL'));
191
+ ctx.logger.error(pc.dim(' hush set API_KEY -e production'));
192
+ ctx.logger.error(pc.dim('\nTo edit all secrets in an editor, use: hush edit'));
193
+ ctx.process.exit(1);
203
194
  }
204
- if (!existsSync(encryptedPath) && !existsSync(join(root, '.sops.yaml'))) {
205
- console.error(pc.red('Hush is not initialized in this directory'));
206
- console.error(pc.dim('Run "hush init" first, then "hush encrypt"'));
207
- process.exit(1);
195
+ if (!ctx.fs.existsSync(encryptedPath) && !ctx.fs.existsSync(join(root, '.sops.yaml'))) {
196
+ ctx.logger.error(pc.red('Hush is not initialized in this directory'));
197
+ ctx.logger.error(pc.dim('Run "hush init" first, then "hush encrypt"'));
198
+ ctx.process.exit(1);
208
199
  }
209
200
  try {
210
- const value = await promptForValue(key, gui ?? false);
201
+ const value = inlineValue ?? await promptForValue(ctx, key, gui ?? false);
211
202
  if (!value) {
212
- console.error(pc.yellow('No value entered, aborting'));
213
- process.exit(1);
203
+ ctx.logger.error(pc.yellow('No value entered, aborting'));
204
+ ctx.process.exit(1);
214
205
  }
215
206
  setKey(encryptedPath, key, value);
216
207
  const envLabel = fileKey === 'shared' ? '' : ` in ${fileKey}`;
217
- console.log(pc.green(`\n${key} set${envLabel} (${value.length} chars, encrypted)`));
208
+ ctx.logger.log(pc.green(`\n${key} set${envLabel} (${value.length} chars, encrypted)`));
218
209
  }
219
210
  catch (error) {
220
211
  const err = error;
221
212
  if (err.message === 'Cancelled') {
222
- console.log(pc.yellow('Cancelled'));
223
- process.exit(1);
213
+ ctx.logger.log(pc.yellow('Cancelled'));
214
+ ctx.process.exit(1);
224
215
  }
225
216
  throw err;
226
217
  }
@@ -1,3 +1,3 @@
1
- import type { SkillOptions } from '../types.js';
2
- export declare function skillCommand(options: SkillOptions): Promise<void>;
1
+ import type { HushContext, SkillOptions } from '../types.js';
2
+ export declare function skillCommand(ctx: HushContext, options: SkillOptions): Promise<void>;
3
3
  //# sourceMappingURL=skill.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../../src/commands/skill.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAuiDhD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CvE"}
1
+ {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../../src/commands/skill.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkvC7D,wBAAsB,YAAY,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CzF"}