@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,6 +1,7 @@
1
+ import { HushContext } from '../types.js';
1
2
  export interface MigrateOptions {
2
3
  root: string;
3
4
  dryRun: boolean;
4
5
  }
5
- export declare function migrateCommand(options: MigrateOptions): Promise<void>;
6
+ export declare function migrateCommand(ctx: HushContext, options: MigrateOptions): Promise<void>;
6
7
  //# sourceMappingURL=migrate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AA8DD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA0E3E"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AA+DD,wBAAsB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA4E7F"}
@@ -1,8 +1,6 @@
1
- import { existsSync, renameSync, readFileSync, writeFileSync } from 'node:fs';
2
1
  import { join } from 'node:path';
3
2
  import pc from 'picocolors';
4
3
  import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
5
- import { findConfigPath } from '../config/loader.js';
6
4
  const FILE_MIGRATIONS = [
7
5
  { from: '.env.encrypted', to: '.hush.encrypted' },
8
6
  { from: '.env.development.encrypted', to: '.hush.development.encrypted' },
@@ -15,18 +13,19 @@ const SOURCE_MIGRATIONS = {
15
13
  '.env.production': '.hush.production',
16
14
  '.env.local': '.hush.local',
17
15
  };
18
- function getMigrationFiles(root) {
16
+ function getMigrationFiles(ctx, root) {
19
17
  return FILE_MIGRATIONS.map(({ from, to }) => ({
20
18
  from,
21
19
  to,
22
- exists: existsSync(join(root, from)),
20
+ exists: ctx.fs.existsSync(join(root, from)),
23
21
  }));
24
22
  }
25
- function migrateConfig(root, dryRun) {
26
- const configPath = findConfigPath(root);
27
- if (!configPath)
23
+ function migrateConfig(ctx, root, dryRun) {
24
+ const projectInfo = ctx.config.findProjectRoot(root);
25
+ if (!projectInfo)
28
26
  return false;
29
- const content = readFileSync(configPath, 'utf-8');
27
+ const configPath = projectInfo.configPath;
28
+ const content = ctx.fs.readFileSync(configPath, 'utf-8');
30
29
  const config = parseYaml(content);
31
30
  let modified = false;
32
31
  const sources = config.sources;
@@ -45,43 +44,43 @@ function migrateConfig(root, dryRun) {
45
44
  if (modified && !dryRun) {
46
45
  const schemaComment = content.startsWith('#') ? content.split('\n')[0] + '\n' : '';
47
46
  const newContent = schemaComment + stringifyYaml(config, { indent: 2 });
48
- writeFileSync(configPath, newContent, 'utf-8');
47
+ ctx.fs.writeFileSync(configPath, newContent, 'utf-8');
49
48
  }
50
49
  return modified;
51
50
  }
52
- export async function migrateCommand(options) {
51
+ export async function migrateCommand(ctx, options) {
53
52
  const { root, dryRun } = options;
54
- console.log(pc.blue('Hush v4 → v5 Migration\n'));
53
+ ctx.logger.log(pc.blue('Hush v4 → v5 Migration\n'));
55
54
  if (dryRun) {
56
- console.log(pc.yellow('DRY RUN - no changes will be made\n'));
55
+ ctx.logger.log(pc.yellow('DRY RUN - no changes will be made\n'));
57
56
  }
58
- const migrations = getMigrationFiles(root);
57
+ const migrations = getMigrationFiles(ctx, root);
59
58
  const filesToMigrate = migrations.filter(m => m.exists);
60
59
  if (filesToMigrate.length === 0) {
61
- console.log(pc.dim('No v4 encrypted files found (.env.encrypted, etc.)'));
62
- console.log(pc.dim('Already on v5 or no encrypted files exist.\n'));
63
- const configNeedsMigration = migrateConfig(root, true);
60
+ ctx.logger.log(pc.dim('No v4 encrypted files found (.env.encrypted, etc.)'));
61
+ ctx.logger.log(pc.dim('Already on v5 or no encrypted files exist.\n'));
62
+ const configNeedsMigration = migrateConfig(ctx, root, true);
64
63
  if (configNeedsMigration) {
65
- console.log(pc.yellow('hush.yaml contains v4 source paths that need updating.\n'));
64
+ ctx.logger.log(pc.yellow('hush.yaml contains v4 source paths that need updating.\n'));
66
65
  if (!dryRun) {
67
- migrateConfig(root, false);
68
- console.log(pc.green('Updated hush.yaml source paths to v5 format.\n'));
66
+ migrateConfig(ctx, root, false);
67
+ ctx.logger.log(pc.green('Updated hush.yaml source paths to v5 format.\n'));
69
68
  }
70
69
  }
71
70
  return;
72
71
  }
73
- console.log(pc.bold('Files to migrate:'));
72
+ ctx.logger.log(pc.bold('Files to migrate:'));
74
73
  for (const { from, to, exists } of migrations) {
75
74
  if (exists) {
76
- console.log(` ${pc.yellow(from)} → ${pc.green(to)}`);
75
+ ctx.logger.log(` ${pc.yellow(from)} → ${pc.green(to)}`);
77
76
  }
78
77
  else {
79
- console.log(pc.dim(` ${from} (not found, skipping)`));
78
+ ctx.logger.log(pc.dim(` ${from} (not found, skipping)`));
80
79
  }
81
80
  }
82
- console.log('');
81
+ ctx.logger.log('');
83
82
  if (dryRun) {
84
- console.log(pc.dim('Run without --dry-run to apply changes.'));
83
+ ctx.logger.log(pc.dim('Run without --dry-run to apply changes.'));
85
84
  return;
86
85
  }
87
86
  let migratedCount = 0;
@@ -90,27 +89,29 @@ export async function migrateCommand(options) {
90
89
  continue;
91
90
  const fromPath = join(root, from);
92
91
  const toPath = join(root, to);
93
- if (existsSync(toPath)) {
94
- console.log(pc.yellow(` Skipping ${from}: ${to} already exists`));
92
+ if (ctx.fs.existsSync(toPath)) {
93
+ ctx.logger.log(pc.yellow(` Skipping ${from}: ${to} already exists`));
95
94
  continue;
96
95
  }
97
- renameSync(fromPath, toPath);
98
- console.log(pc.green(` Migrated ${from} → ${to}`));
96
+ const content = ctx.fs.readFileSync(fromPath, 'utf-8');
97
+ ctx.fs.writeFileSync(toPath, content);
98
+ ctx.fs.unlinkSync(fromPath);
99
+ ctx.logger.log(pc.green(` Migrated ${from} → ${to}`));
99
100
  migratedCount++;
100
101
  }
101
- const configUpdated = migrateConfig(root, false);
102
+ const configUpdated = migrateConfig(ctx, root, false);
102
103
  if (configUpdated) {
103
- console.log(pc.green(' Updated hush.yaml source paths'));
104
+ ctx.logger.log(pc.green(' Updated hush.yaml source paths'));
104
105
  }
105
- console.log('');
106
+ ctx.logger.log('');
106
107
  if (migratedCount > 0 || configUpdated) {
107
- console.log(pc.green(pc.bold(`Migration complete.`)));
108
- console.log(pc.dim('\nNext steps:'));
109
- console.log(pc.dim(' 1. git add .hush.encrypted .hush.*.encrypted hush.yaml'));
110
- console.log(pc.dim(' 2. git rm .env.encrypted .env.*.encrypted (if tracked)'));
111
- console.log(pc.dim(' 3. git commit -m "chore: migrate to Hush v5 format"'));
108
+ ctx.logger.log(pc.green(pc.bold(`Migration complete.`)));
109
+ ctx.logger.log(pc.dim('\nNext steps:'));
110
+ ctx.logger.log(pc.dim(' 1. git add .hush.encrypted .hush.*.encrypted hush.yaml'));
111
+ ctx.logger.log(pc.dim(' 2. git rm .env.encrypted .env.*.encrypted (if tracked)'));
112
+ ctx.logger.log(pc.dim(' 3. git commit -m "chore: migrate to Hush v5 format"'));
112
113
  }
113
114
  else {
114
- console.log(pc.dim('No changes made.'));
115
+ ctx.logger.log(pc.dim('No changes made.'));
115
116
  }
116
117
  }
@@ -1,3 +1,3 @@
1
- import type { PushOptions } from '../types.js';
2
- export declare function pushCommand(options: PushOptions): Promise<void>;
1
+ import type { PushOptions, HushContext } from '../types.js';
2
+ export declare function pushCommand(ctx: HushContext, options: PushOptions): Promise<void>;
3
3
  //# sourceMappingURL=push.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAU,WAAW,EAAqC,MAAM,aAAa,CAAC;AAgH1F,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA6GrE"}
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAU,WAAW,EAAqC,WAAW,EAAE,MAAM,aAAa,CAAC;AAiHvG,wBAAsB,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA6GvF"}
@@ -1,26 +1,22 @@
1
- import { execSync } from 'node:child_process';
2
- import { existsSync, writeFileSync, unlinkSync } from 'node:fs';
3
1
  import { join } from 'node:path';
4
2
  import pc from 'picocolors';
5
- import { loadConfig } from '../config/loader.js';
6
3
  import { filterVarsForTarget } from '../core/filter.js';
7
4
  import { interpolateVars } 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
- function pushWorkerSecret(key, value, targetDir, dryRun, verbose) {
8
+ function pushWorkerSecret(ctx, key, value, targetDir, dryRun, verbose) {
13
9
  if (dryRun) {
14
10
  if (verbose) {
15
- console.log(pc.green(` + ${key}`));
11
+ ctx.logger.log(pc.green(` + ${key}`));
16
12
  }
17
13
  else {
18
- console.log(pc.dim(` [dry-run] ${key}`));
14
+ ctx.logger.log(pc.dim(` [dry-run] ${key}`));
19
15
  }
20
16
  return true;
21
17
  }
22
18
  try {
23
- execSync(`echo "${value}" | wrangler secret put ${key}`, {
19
+ ctx.exec.execSync(`echo "${value}" | wrangler secret put ${key}`, {
24
20
  cwd: targetDir,
25
21
  stdio: ['pipe', 'pipe', 'pipe'],
26
22
  shell: '/bin/bash',
@@ -29,18 +25,18 @@ function pushWorkerSecret(key, value, targetDir, dryRun, verbose) {
29
25
  }
30
26
  catch (error) {
31
27
  const err = error;
32
- console.error(pc.red(` Failed: ${key} - ${err.stderr || err.message}`));
28
+ ctx.logger.error(pc.red(` Failed: ${key} - ${err.stderr || err.message}`));
33
29
  return false;
34
30
  }
35
31
  }
36
- function pushPagesSecrets(vars, projectName, targetDir, dryRun, verbose) {
32
+ function pushPagesSecrets(ctx, vars, projectName, targetDir, dryRun, verbose) {
37
33
  if (dryRun) {
38
34
  for (const { key } of vars) {
39
35
  if (verbose) {
40
- console.log(pc.green(` + ${key}`));
36
+ ctx.logger.log(pc.green(` + ${key}`));
41
37
  }
42
38
  else {
43
- console.log(pc.dim(` [dry-run] ${key}`));
39
+ ctx.logger.log(pc.dim(` [dry-run] ${key}`));
44
40
  }
45
41
  }
46
42
  return { success: vars.length, failed: 0 };
@@ -51,26 +47,26 @@ function pushPagesSecrets(vars, projectName, targetDir, dryRun, verbose) {
51
47
  }
52
48
  const tempFile = join(targetDir, '.hush-secrets-temp.json');
53
49
  try {
54
- writeFileSync(tempFile, JSON.stringify(secretsJson, null, 2));
55
- execSync(`wrangler pages secret bulk "${tempFile}" --project-name "${projectName}"`, {
50
+ ctx.fs.writeFileSync(tempFile, JSON.stringify(secretsJson, null, 2));
51
+ ctx.exec.execSync(`wrangler pages secret bulk "${tempFile}" --project-name "${projectName}"`, {
56
52
  cwd: targetDir,
57
53
  stdio: ['pipe', 'pipe', 'pipe'],
58
54
  shell: '/bin/bash',
59
55
  });
60
56
  for (const { key } of vars) {
61
- console.log(pc.green(` ${key}`));
57
+ ctx.logger.log(pc.green(` ${key}`));
62
58
  }
63
59
  return { success: vars.length, failed: 0 };
64
60
  }
65
61
  catch (error) {
66
62
  const err = error;
67
63
  const stderrStr = err.stderr instanceof Buffer ? err.stderr.toString() : (err.stderr || err.message || 'Unknown error');
68
- console.error(pc.red(` Failed to push secrets: ${stderrStr}`));
64
+ ctx.logger.error(pc.red(` Failed to push secrets: ${stderrStr}`));
69
65
  return { success: 0, failed: vars.length };
70
66
  }
71
67
  finally {
72
- if (existsSync(tempFile)) {
73
- unlinkSync(tempFile);
68
+ if (ctx.fs.existsSync(tempFile)) {
69
+ ctx.fs.unlinkSync(tempFile);
74
70
  }
75
71
  }
76
72
  }
@@ -103,30 +99,30 @@ function getPagesProject(target) {
103
99
  }
104
100
  throw new Error(`Target "${target.name}" is not configured for Cloudflare Pages`);
105
101
  }
106
- export async function pushCommand(options) {
102
+ export async function pushCommand(ctx, options) {
107
103
  const { root, dryRun, verbose, target: targetFilter } = options;
108
- const config = loadConfig(root);
109
- console.log(pc.blue('Pushing production secrets to Cloudflare...'));
104
+ const config = ctx.config.loadConfig(root);
105
+ ctx.logger.log(pc.blue('Pushing production secrets to Cloudflare...'));
110
106
  if (dryRun) {
111
- console.log(pc.yellow('(dry-run mode)'));
107
+ ctx.logger.log(pc.yellow('(dry-run mode)'));
112
108
  if (verbose) {
113
- console.log(pc.dim('(verbose output enabled)'));
109
+ ctx.logger.log(pc.dim('(verbose output enabled)'));
114
110
  }
115
111
  }
116
112
  const sharedEncrypted = join(root, config.sources.shared + '.encrypted');
117
113
  const prodEncrypted = join(root, config.sources.production + '.encrypted');
118
114
  const varSources = [];
119
- if (existsSync(sharedEncrypted)) {
120
- const content = sopsDecrypt(sharedEncrypted);
115
+ if (ctx.fs.existsSync(sharedEncrypted)) {
116
+ const content = ctx.sops.decrypt(sharedEncrypted);
121
117
  varSources.push(parseEnvContent(content));
122
118
  }
123
- if (existsSync(prodEncrypted)) {
124
- const content = sopsDecrypt(prodEncrypted);
119
+ if (ctx.fs.existsSync(prodEncrypted)) {
120
+ const content = ctx.sops.decrypt(prodEncrypted);
125
121
  varSources.push(parseEnvContent(content));
126
122
  }
127
123
  if (varSources.length === 0) {
128
- console.error(pc.red('No encrypted files found'));
129
- process.exit(1);
124
+ ctx.logger.error(pc.red('No encrypted files found'));
125
+ ctx.process.exit(1);
130
126
  }
131
127
  const merged = mergeVars(...varSources);
132
128
  const interpolated = interpolateVars(merged);
@@ -139,47 +135,47 @@ export async function pushCommand(options) {
139
135
  pushableTargets = getTargetsWithPush(config, targetFilter);
140
136
  }
141
137
  catch (error) {
142
- console.error(pc.red(error.message));
143
- process.exit(1);
138
+ ctx.logger.error(pc.red(error.message));
139
+ ctx.process.exit(1);
144
140
  }
145
141
  if (pushableTargets.length === 0) {
146
- console.error(pc.red('No targets configured for push'));
147
- console.error(pc.dim('Add format: wrangler or push_to: { type: cloudflare-pages, project: ... } to a target'));
148
- process.exit(1);
142
+ ctx.logger.error(pc.red('No targets configured for push'));
143
+ ctx.logger.error(pc.dim('Add format: wrangler or push_to: { type: cloudflare-pages, project: ... } to a target'));
144
+ ctx.process.exit(1);
149
145
  }
150
146
  for (const target of pushableTargets) {
151
147
  const targetDir = join(root, target.path);
152
148
  const pushType = getPushType(target);
153
149
  let filtered = filterVarsForTarget(interpolated, target);
154
- const localTemplate = loadLocalTemplates(targetDir, 'production');
150
+ const localTemplate = loadLocalTemplates(targetDir, 'production', ctx.fs);
155
151
  if (localTemplate.hasTemplate) {
156
- const templateVars = resolveTemplateVars(localTemplate.vars, rootSecretsRecord, { processEnv: process.env });
152
+ const templateVars = resolveTemplateVars(localTemplate.vars, rootSecretsRecord, { processEnv: ctx.process.env });
157
153
  filtered = mergeVars(filtered, templateVars);
158
154
  }
159
155
  if (filtered.length === 0) {
160
- console.log(pc.dim(`\n${target.name} - no matching vars, skipped`));
156
+ ctx.logger.log(pc.dim(`\n${target.name} - no matching vars, skipped`));
161
157
  continue;
162
158
  }
163
159
  const typeLabel = pushType === 'cloudflare-pages' ? 'Pages' : 'Workers';
164
160
  if (dryRun && verbose) {
165
- console.log(pc.blue(`\n[DRY RUN] Would push to ${target.name} (${typeLabel}, ${target.path}/):`));
161
+ ctx.logger.log(pc.blue(`\n[DRY RUN] Would push to ${target.name} (${typeLabel}, ${target.path}/):`));
166
162
  }
167
163
  else {
168
- console.log(pc.blue(`\n${target.name} (${typeLabel}, ${target.path}/)`));
164
+ ctx.logger.log(pc.blue(`\n${target.name} (${typeLabel}, ${target.path}/)`));
169
165
  }
170
166
  let success = 0;
171
167
  let failed = 0;
172
168
  if (pushType === 'cloudflare-pages') {
173
169
  const projectName = getPagesProject(target);
174
- const result = pushPagesSecrets(filtered, projectName, targetDir, dryRun, verbose);
170
+ const result = pushPagesSecrets(ctx, filtered, projectName, targetDir, dryRun, verbose);
175
171
  success = result.success;
176
172
  failed = result.failed;
177
173
  }
178
174
  else {
179
175
  for (const { key, value } of filtered) {
180
- if (pushWorkerSecret(key, value, targetDir, dryRun, verbose)) {
176
+ if (pushWorkerSecret(ctx, key, value, targetDir, dryRun, verbose)) {
181
177
  if (!dryRun)
182
- console.log(pc.green(` ${key}`));
178
+ ctx.logger.log(pc.green(` ${key}`));
183
179
  success++;
184
180
  }
185
181
  else {
@@ -187,12 +183,12 @@ export async function pushCommand(options) {
187
183
  }
188
184
  }
189
185
  }
190
- console.log(pc.dim(` ${success} pushed, ${failed} failed`));
186
+ ctx.logger.log(pc.dim(` ${success} pushed, ${failed} failed`));
191
187
  }
192
188
  if (dryRun) {
193
- console.log(pc.yellow('\n[dry-run] No secrets were pushed'));
189
+ ctx.logger.log(pc.yellow('\n[dry-run] No secrets were pushed'));
194
190
  }
195
191
  else {
196
- console.log(pc.green('\nPush complete'));
192
+ ctx.logger.log(pc.green('\nPush complete'));
197
193
  }
198
194
  }
@@ -1,8 +1,8 @@
1
- import type { Environment } from '../types.js';
1
+ import type { Environment, HushContext } from '../types.js';
2
2
  export interface ResolveOptions {
3
3
  root: string;
4
4
  env: Environment;
5
5
  target: string;
6
6
  }
7
- export declare function resolveCommand(options: ResolveOptions): Promise<void>;
7
+ export declare function resolveCommand(ctx: HushContext, options: ResolveOptions): Promise<void>;
8
8
  //# sourceMappingURL=resolve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/commands/resolve.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAU,WAAW,EAAU,MAAM,aAAa,CAAC;AAG/D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,WAAW,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AA6BD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmI3E"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/commands/resolve.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAU,WAAW,EAAU,WAAW,EAAE,MAAM,aAAa,CAAC;AAG5E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,WAAW,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AA6BD,wBAAsB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmI7F"}
@@ -1,11 +1,8 @@
1
- import { existsSync } from 'node:fs';
2
1
  import { join } from 'node:path';
3
2
  import pc from 'picocolors';
4
- import { loadConfig } from '../config/loader.js';
5
3
  import { interpolateVars } from '../core/interpolate.js';
6
4
  import { mergeVars } from '../core/merge.js';
7
5
  import { parseEnvContent } from '../core/parse.js';
8
- import { decrypt as sopsDecrypt } from '../core/sops.js';
9
6
  import { loadLocalTemplates } from '../core/template.js';
10
7
  import { FORMAT_OUTPUT_FILES } from '../types.js';
11
8
  function matchesPattern(key, pattern) {
@@ -26,14 +23,14 @@ function getOutputFilename(target, env) {
26
23
  }
27
24
  return FORMAT_OUTPUT_FILES[target.format][env];
28
25
  }
29
- export async function resolveCommand(options) {
26
+ export async function resolveCommand(ctx, options) {
30
27
  const { root, env, target: targetName } = options;
31
- const config = loadConfig(root);
28
+ const config = ctx.config.loadConfig(root);
32
29
  const target = config.targets.find(t => t.name === targetName);
33
30
  if (!target) {
34
- console.error(pc.red(`Target not found: ${targetName}`));
35
- console.error(pc.dim('Available targets: ' + config.targets.map(t => t.name).join(', ')));
36
- process.exit(1);
31
+ ctx.logger.error(`Target not found: ${targetName}`);
32
+ ctx.logger.error(pc.dim('Available targets: ' + config.targets.map(t => t.name).join(', ')));
33
+ ctx.process.exit(1);
37
34
  }
38
35
  const sharedEncrypted = join(root, config.sources.shared + '.encrypted');
39
36
  const envEncrypted = join(root, config.sources[env] + '.encrypted');
@@ -41,9 +38,9 @@ export async function resolveCommand(options) {
41
38
  const varsBySource = new Map();
42
39
  const allVars = new Map();
43
40
  const loadSource = (path, sourceName) => {
44
- if (!existsSync(path))
41
+ if (!ctx.fs.existsSync(path))
45
42
  return;
46
- const content = sopsDecrypt(path);
43
+ const content = ctx.sops.decrypt(path);
47
44
  const vars = parseEnvContent(content);
48
45
  const sourceVars = [];
49
46
  for (const v of vars) {
@@ -57,8 +54,8 @@ export async function resolveCommand(options) {
57
54
  loadSource(envEncrypted, config.sources[env]);
58
55
  loadSource(localEncrypted, config.sources.local);
59
56
  if (allVars.size === 0) {
60
- console.error(pc.red('No encrypted files found'));
61
- process.exit(1);
57
+ ctx.logger.error('No encrypted files found');
58
+ ctx.process.exit(1);
62
59
  }
63
60
  const merged = mergeVars(...Array.from(varsBySource.values()).map(sources => sources.map(s => ({ key: s.key, value: s.value }))));
64
61
  const interpolated = interpolateVars(merged);
@@ -84,44 +81,44 @@ export async function resolveCommand(options) {
84
81
  included.push({ key: v.key, source });
85
82
  }
86
83
  const outputFile = getOutputFilename(target, env);
87
- console.log(pc.bold(`\nTarget: ${pc.cyan(target.name)}`));
88
- console.log(`Path: ${pc.dim(target.path + '/')}`);
89
- console.log(`Format: ${pc.dim(target.format)} ${pc.dim(`(${outputFile})`)}`);
90
- console.log(`Environment: ${pc.dim(env)}`);
91
- console.log(pc.green(`\n✅ ROOT SECRETS (Matched Filters) (${included.length}):`));
84
+ ctx.logger.log(pc.bold(`\nTarget: ${pc.cyan(target.name)}`));
85
+ ctx.logger.log(`Path: ${pc.dim(target.path + '/')}`);
86
+ ctx.logger.log(`Format: ${pc.dim(target.format)} ${pc.dim(`(${outputFile})`)}`);
87
+ ctx.logger.log(`Environment: ${pc.dim(env)}`);
88
+ ctx.logger.log(pc.green(`\n✅ ROOT SECRETS (Matched Filters) (${included.length}):`));
92
89
  if (included.length === 0) {
93
- console.log(pc.dim(' (none)'));
90
+ ctx.logger.log(pc.dim(' (none)'));
94
91
  }
95
92
  else {
96
93
  const maxKeyLen = Math.max(...included.map(v => v.key.length));
97
94
  for (const v of included) {
98
- console.log(` ${v.key.padEnd(maxKeyLen)} ${pc.dim(`(source: ${v.source})`)}`);
95
+ ctx.logger.log(` ${v.key.padEnd(maxKeyLen)} ${pc.dim(`(source: ${v.source})`)}`);
99
96
  }
100
97
  }
101
- console.log(pc.red(`\n🚫 EXCLUDED VARIABLES (${excluded.length}):`));
98
+ ctx.logger.log(pc.red(`\n🚫 EXCLUDED VARIABLES (${excluded.length}):`));
102
99
  if (excluded.length === 0) {
103
- console.log(pc.dim(' (none)'));
100
+ ctx.logger.log(pc.dim(' (none)'));
104
101
  }
105
102
  else {
106
103
  const maxKeyLen = Math.max(...excluded.map(v => v.key.length));
107
104
  for (const v of excluded) {
108
- console.log(` ${v.key.padEnd(maxKeyLen)} ${pc.dim(`(matches: ${v.pattern})`)}`);
105
+ ctx.logger.log(` ${v.key.padEnd(maxKeyLen)} ${pc.dim(`(matches: ${v.pattern})`)}`);
109
106
  }
110
107
  }
111
108
  const targetAbsPath = join(root, target.path);
112
- const localTemplate = loadLocalTemplates(targetAbsPath, env);
109
+ const localTemplate = loadLocalTemplates(targetAbsPath, env, ctx.fs);
113
110
  if (localTemplate.hasTemplate) {
114
- console.log(pc.blue(`\n📄 TEMPLATE EXPANSIONS (${pc.dim(join(target.path, '.env'))}):`));
111
+ ctx.logger.log(pc.blue(`\n📄 TEMPLATE EXPANSIONS (${pc.dim(join(target.path, '.hush'))}):`));
115
112
  const maxKeyLen = Math.max(...localTemplate.vars.map(v => v.key.length));
116
113
  for (const v of localTemplate.vars) {
117
- console.log(` ${v.key.padEnd(maxKeyLen)} ${pc.dim(`← ${v.value}`)}`);
114
+ ctx.logger.log(` ${v.key.padEnd(maxKeyLen)} ${pc.dim(`← ${v.value}`)}`);
118
115
  }
119
116
  // Calculate final merged list for clarity
120
117
  const finalKeys = new Set([
121
118
  ...included.map(v => v.key),
122
119
  ...localTemplate.vars.map(v => v.key)
123
120
  ]);
124
- console.log(pc.magenta(`\n📦 FINAL INJECTION (${finalKeys.size} total):`));
121
+ ctx.logger.log(pc.magenta(`\n📦 FINAL INJECTION (${finalKeys.size} total):`));
125
122
  const sortedKeys = Array.from(finalKeys).sort();
126
123
  for (const key of sortedKeys) {
127
124
  const isTemplate = localTemplate.vars.some(v => v.key === key);
@@ -133,8 +130,8 @@ export async function resolveCommand(options) {
133
130
  sourceInfo = pc.dim('(template)');
134
131
  else if (isRoot)
135
132
  sourceInfo = pc.dim('(root)');
136
- console.log(` ${key} ${sourceInfo}`);
133
+ ctx.logger.log(` ${key} ${sourceInfo}`);
137
134
  }
138
135
  }
139
- console.log('');
136
+ ctx.logger.log('');
140
137
  }
@@ -1,3 +1,3 @@
1
- import type { RunOptions } from '../types.js';
2
- export declare function runCommand(options: RunOptions): Promise<void>;
1
+ import type { RunOptions, HushContext } from '../types.js';
2
+ export declare function runCommand(ctx: HushContext, options: RunOptions): Promise<void>;
3
3
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAmC,MAAM,aAAa,CAAC;AAkD/E,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA8GnE"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAmC,WAAW,EAAE,MAAM,aAAa,CAAC;AAkD5F,wBAAsB,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA8GrF"}