secretless-ai 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +156 -161
  2. package/dist/backends/config.d.ts +19 -0
  3. package/dist/backends/config.d.ts.map +1 -0
  4. package/dist/backends/config.js +99 -0
  5. package/dist/backends/config.js.map +1 -0
  6. package/dist/backends/factory.d.ts +34 -0
  7. package/dist/backends/factory.d.ts.map +1 -0
  8. package/dist/backends/factory.js +112 -0
  9. package/dist/backends/factory.js.map +1 -0
  10. package/dist/backends/index.d.ts +8 -0
  11. package/dist/backends/index.d.ts.map +1 -0
  12. package/dist/backends/index.js +19 -0
  13. package/dist/backends/index.js.map +1 -0
  14. package/dist/backends/keychain-linux.d.ts +27 -0
  15. package/dist/backends/keychain-linux.d.ts.map +1 -0
  16. package/dist/backends/keychain-linux.js +155 -0
  17. package/dist/backends/keychain-linux.js.map +1 -0
  18. package/dist/backends/keychain-macos.d.ts +25 -0
  19. package/dist/backends/keychain-macos.d.ts.map +1 -0
  20. package/dist/backends/keychain-macos.js +181 -0
  21. package/dist/backends/keychain-macos.js.map +1 -0
  22. package/dist/backends/local.d.ts +2 -2
  23. package/dist/backends/local.d.ts.map +1 -1
  24. package/dist/backends/migrate.d.ts +30 -0
  25. package/dist/backends/migrate.d.ts.map +1 -0
  26. package/dist/backends/migrate.js +40 -0
  27. package/dist/backends/migrate.js.map +1 -0
  28. package/dist/backends/onepassword.d.ts +62 -0
  29. package/dist/backends/onepassword.d.ts.map +1 -0
  30. package/dist/backends/onepassword.js +254 -0
  31. package/dist/backends/onepassword.js.map +1 -0
  32. package/dist/backends/types.d.ts +12 -2
  33. package/dist/backends/types.d.ts.map +1 -1
  34. package/dist/cli.d.ts +5 -0
  35. package/dist/cli.d.ts.map +1 -1
  36. package/dist/cli.js +590 -4
  37. package/dist/cli.js.map +1 -1
  38. package/dist/env-import.d.ts +37 -0
  39. package/dist/env-import.d.ts.map +1 -0
  40. package/dist/env-import.js +140 -0
  41. package/dist/env-import.js.map +1 -0
  42. package/dist/git-hook.d.ts +28 -0
  43. package/dist/git-hook.d.ts.map +1 -0
  44. package/dist/git-hook.js +115 -0
  45. package/dist/git-hook.js.map +1 -0
  46. package/dist/index.d.ts +8 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +33 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/manifest.d.ts +40 -0
  51. package/dist/manifest.d.ts.map +1 -0
  52. package/dist/manifest.js +125 -0
  53. package/dist/manifest.js.map +1 -0
  54. package/dist/mcp/protect.d.ts +3 -0
  55. package/dist/mcp/protect.d.ts.map +1 -1
  56. package/dist/mcp/protect.js +2 -1
  57. package/dist/mcp/protect.js.map +1 -1
  58. package/dist/mcp/rewrite.d.ts +2 -1
  59. package/dist/mcp/rewrite.d.ts.map +1 -1
  60. package/dist/mcp/rewrite.js +4 -2
  61. package/dist/mcp/rewrite.js.map +1 -1
  62. package/dist/mcp/vault.d.ts +17 -2
  63. package/dist/mcp/vault.d.ts.map +1 -1
  64. package/dist/mcp/vault.js +23 -4
  65. package/dist/mcp/vault.js.map +1 -1
  66. package/dist/mcp-wrapper.js +28 -6
  67. package/dist/mcp-wrapper.js.map +1 -1
  68. package/dist/run.d.ts +18 -0
  69. package/dist/run.d.ts.map +1 -0
  70. package/dist/run.js +54 -0
  71. package/dist/run.js.map +1 -0
  72. package/dist/scan-staged.d.ts +20 -0
  73. package/dist/scan-staged.d.ts.map +1 -0
  74. package/dist/scan-staged.js +99 -0
  75. package/dist/scan-staged.js.map +1 -0
  76. package/dist/secret-store.d.ts +30 -0
  77. package/dist/secret-store.d.ts.map +1 -0
  78. package/dist/secret-store.js +78 -0
  79. package/dist/secret-store.js.map +1 -0
  80. package/dist/setup.d.ts +33 -0
  81. package/dist/setup.d.ts.map +1 -0
  82. package/dist/setup.js +128 -0
  83. package/dist/setup.js.map +1 -0
  84. package/package.json +1 -1
package/README.md CHANGED
@@ -13,6 +13,110 @@ Part of the [OpenA2A](https://opena2a.org) ecosystem — open-source security fo
13
13
  npx secretless-ai init
14
14
  ```
15
15
 
16
+ ## Secret Storage Backends
17
+
18
+ Secretless stores secrets in your choice of backend. Secrets are never in environment variables, shell profiles, or config files — they exist only in the backend and get injected into process memory at runtime.
19
+
20
+ | Backend | Storage | Sync | Auth | Best For |
21
+ |---------|---------|------|------|----------|
22
+ | `local` | AES-256-GCM encrypted file | None (single machine) | Filesystem | Quick start, simple setups |
23
+ | `keychain` | macOS Keychain / Linux Secret Service | Device-local | OS login | Native OS integration |
24
+ | `1password` | 1Password vault | Cross-device | Biometric (Touch ID) / Service Account | Teams, CI/CD, multi-device |
25
+
26
+ ```bash
27
+ npx secretless-ai backend # Show available backends
28
+ npx secretless-ai backend set 1password # Switch to 1Password
29
+ npx secretless-ai backend set keychain # Switch to OS keychain
30
+ npx secretless-ai migrate --from local --to 1password # Migrate existing secrets
31
+ ```
32
+
33
+ ### 1Password Backend
34
+
35
+ Stores secrets in a dedicated "Secretless" vault using the [`op` CLI](https://developer.1password.com/docs/cli). Secrets never touch disk.
36
+
37
+ **Setup:**
38
+
39
+ ```bash
40
+ brew install --cask 1password # Install 1Password desktop app
41
+ brew install --cask 1password-cli # Install op CLI
42
+ ```
43
+
44
+ Then enable CLI integration: **1Password > Settings > Developer > "Integrate with 1Password CLI"**. This allows the CLI to authenticate through the desktop app with biometric unlock (Touch ID / Windows Hello).
45
+
46
+ ```bash
47
+ npx secretless-ai backend set 1password # Switch backend
48
+ ```
49
+
50
+ **CI/CD:** Set `OP_SERVICE_ACCOUNT_TOKEN` — same secrets, no code changes. No desktop app needed.
51
+
52
+ ## Secret Management
53
+
54
+ Store, list, and inject secrets without exposing them to AI tools.
55
+
56
+ ```bash
57
+ npx secretless-ai secret set STRIPE_KEY=sk_live_... # Store a secret
58
+ npx secretless-ai secret set DATABASE_URL # Read value from stdin
59
+ npx secretless-ai secret list # List secret names (never values)
60
+ npx secretless-ai secret rm STRIPE_KEY # Remove a secret
61
+ ```
62
+
63
+ ### Running Commands with Secrets
64
+
65
+ Inject secrets as environment variables into any command. The AI tool sees the command output but never the secret values.
66
+
67
+ ```bash
68
+ npx secretless-ai run -- npm test # Inject all secrets
69
+ npx secretless-ai run --only STRIPE_KEY -- curl -u "$STRIPE_KEY:" https://api.stripe.com/v1/balance
70
+ npx secretless-ai run --only DATABASE_URL -- npm run migrate # Inject specific key
71
+ ```
72
+
73
+ ### AI-Safe by Design
74
+
75
+ When an AI tool tries to read a secret value, secretless blocks it:
76
+
77
+ ```
78
+ $ npx secretless-ai secret get STRIPE_KEY # (run by AI tool)
79
+
80
+ secretless: Blocked -- secret values cannot be read in non-interactive contexts.
81
+ AI tools capture stdout, which would expose the secret in their context.
82
+
83
+ To inject secrets into a command:
84
+ npx secretless-ai run -- <command>
85
+ ```
86
+
87
+ Direct terminal access (human) works normally. The guard detects non-interactive execution (how AI tools run commands) and refuses to output.
88
+
89
+ ### Import from .env Files
90
+
91
+ ```bash
92
+ npx secretless-ai import .env # Import from specific file
93
+ npx secretless-ai import --detect # Auto-find and import all .env files
94
+ ```
95
+
96
+ ### Project Manifests
97
+
98
+ Define required secrets in a `.secretless` file at the project root:
99
+
100
+ ```
101
+ STRIPE_KEY required Stripe API key for payments
102
+ DATABASE_URL required PostgreSQL connection string
103
+ SENTRY_DSN optional Error tracking
104
+ ```
105
+
106
+ ```bash
107
+ npx secretless-ai setup # Interactive setup for missing secrets
108
+ npx secretless-ai setup --check # CI: fail if required secrets are missing
109
+ ```
110
+
111
+ ## Backend Inspection
112
+
113
+ ```bash
114
+ npx secretless-ai backend list # Show all entries grouped by prefix
115
+ npx secretless-ai backend purge # Dry-run: show what would be deleted
116
+ npx secretless-ai backend purge --yes # Delete all entries
117
+ npx secretless-ai backend purge --prefix mcp --yes # Delete only mcp/ entries
118
+ ```
119
+
16
120
  ## MCP Secret Protection
17
121
 
18
122
  Every MCP server config has plaintext API keys sitting in JSON files on your laptop. The LLM sees them. Secretless encrypts them.
@@ -42,48 +146,14 @@ npx secretless-ai protect-mcp
42
146
 
43
147
  1. Scans MCP configs across Claude Desktop, Cursor, Claude Code, VS Code, and Windsurf
44
148
  2. Identifies which env vars are secrets (key name patterns + value regex matching)
45
- 3. Encrypts secrets into a local AES-256-GCM vault (`~/.secretless-ai/mcp-vault/`)
149
+ 3. Stores secrets in your configured backend (local, keychain, or 1Password)
46
150
  4. Rewrites configs to use the `secretless-mcp` wrapper — decrypts at runtime, injects as env vars
47
151
  5. Non-secret env vars (URLs, org names, regions) stay in the config untouched
48
152
 
49
- **Before:**
50
- ```json
51
- {
52
- "mcpServers": {
53
- "github": {
54
- "command": "npx",
55
- "args": ["@github/mcp-server"],
56
- "env": {
57
- "GITHUB_TOKEN": "ghp_plaintext_visible_to_LLM",
58
- "GITHUB_ORG": "my-org"
59
- }
60
- }
61
- }
62
- }
63
- ```
64
-
65
- **After:**
66
- ```json
67
- {
68
- "mcpServers": {
69
- "github": {
70
- "command": "secretless-mcp",
71
- "args": ["--server", "github", "--client", "claude-desktop", "--", "npx", "@github/mcp-server"],
72
- "env": {
73
- "GITHUB_ORG": "my-org"
74
- }
75
- }
76
- }
77
- }
78
- ```
79
-
80
- The secret moves to the encrypted vault. The wrapper decrypts it at startup (<10ms overhead) and passes it to the MCP server as an env var. The LLM never sees it.
81
-
82
- **Other MCP commands:**
83
-
84
153
  ```bash
85
- npx secretless-ai mcp-status # Show which servers are protected/exposed
86
- npx secretless-ai mcp-unprotect # Restore original configs from backup
154
+ npx secretless-ai protect-mcp --backend 1password # Store MCP secrets in 1Password
155
+ npx secretless-ai mcp-status # Show which servers are protected/exposed
156
+ npx secretless-ai mcp-unprotect # Restore original configs from backup
87
157
  ```
88
158
 
89
159
  ---
@@ -117,7 +187,7 @@ npx secretless-ai init
117
187
  Output:
118
188
 
119
189
  ```
120
- Secretless v0.6.0
190
+ Secretless v0.7.1
121
191
  Keeping secrets out of AI
122
192
 
123
193
  Detected:
@@ -153,40 +223,13 @@ Non-interactive subprocesses (Claude Code's Bash tool, CI/CD, Docker) don't sour
153
223
  | Linux | bash | `~/.bashrc` | Sourced by interactive bash; most tools source it explicitly |
154
224
  | Windows | — | System Environment Variables | Use `setx` or Settings > System > Environment Variables |
155
225
 
156
- **Common mistakes Secretless auto-fixes:**
157
- - **macOS:** Adding `export` lines to `~/.zshrc` instead of `~/.zshenv`. Secretless copies them to the correct file during `init`.
158
- - **Linux:** Adding exports to `~/.bash_profile` instead of `~/.bashrc`, or placing them after the interactive guard in `.bashrc`. Secretless inserts them before the guard.
159
- - **Windows:** Setting keys only in PowerShell `$PROFILE` (session-only). Secretless runs `setx` to set persistent user environment variables.
160
-
161
- ```bash
162
- # macOS (zsh) — add to ~/.zshenv
163
- export ANTHROPIC_API_KEY="sk-ant-..."
164
- export OPENAI_API_KEY="sk-proj-..."
165
-
166
- # Linux (bash) — add to ~/.bashrc (before the interactive guard)
167
- export ANTHROPIC_API_KEY="sk-ant-..."
168
- export OPENAI_API_KEY="sk-proj-..."
169
- ```
170
-
171
- ```powershell
172
- # Windows — use setx (or Settings > System > Environment Variables)
173
- setx ANTHROPIC_API_KEY "sk-ant-..."
174
- setx OPENAI_API_KEY "sk-proj-..."
175
- ```
176
-
177
- **Step 2: Remove keys from AI config files**
178
-
179
- Delete any hardcoded keys from `CLAUDE.md`, `.cursorrules`, `.env`, etc.
180
-
181
- **Step 3: Run secretless init**
226
+ **Step 2: Run secretless init**
182
227
 
183
228
  ```bash
184
229
  npx secretless-ai init
185
230
  ```
186
231
 
187
- Secretless detects which env vars are set and adds a reference table to your AI tool's instruction file. The AI knows *which* keys are available and *how* to authenticate — without seeing the actual values.
188
-
189
- **Step 4: Verify**
232
+ **Step 3: Verify**
190
233
 
191
234
  ```bash
192
235
  npx secretless-ai verify
@@ -202,94 +245,52 @@ npx secretless-ai verify
202
245
  PASS: Secrets are accessible via env vars but hidden from AI context.
203
246
  ```
204
247
 
205
- **Before:** Claude sees `ANTHROPIC_API_KEY=sk-ant-api03-abc123...` in CLAUDE.md — the key is in the context window, extractable via prompt injection.
206
-
207
- **After:** Claude sees a table saying `$ANTHROPIC_API_KEY` exists and the auth header is `x-api-key: $ANTHROPIC_API_KEY`. It uses `$ANTHROPIC_API_KEY` in shell commands. The shell resolves it. Claude never sees the actual value.
248
+ ## Git Protection
208
249
 
209
- ## Commands
210
-
211
- ### `npx secretless-ai init`
212
-
213
- Detects AI tools in your project and installs protections. If API keys are set as env vars, includes a reference table with service names and auth header formats so the AI can use them without seeing values. Safe to run multiple times.
214
-
215
- ### `npx secretless-ai scan`
216
-
217
- Scans config files for hardcoded credentials — both project-level and global (`~/.claude/CLAUDE.md`). Detects 49 credential patterns including Anthropic, OpenAI, AWS, GitHub, Slack, Google, Stripe, SendGrid, Supabase, Azure, GitLab, Twilio, Mailgun, and more.
218
-
219
- ```
220
- Found 2 credential(s):
221
-
222
- [CRIT] Anthropic API Key
223
- ~/.claude/CLAUDE.md:286
224
- ANTHROPIC_API_KEY=[Anthropic API Key REDACTED]
225
-
226
- [CRIT] OpenAI Project Key
227
- ~/.claude/CLAUDE.md:284
228
- OPENAI_API_KEY=[OpenAI Project Key REDACTED]
229
- ```
230
-
231
- ### `npx secretless-ai verify`
232
-
233
- Confirms keys are usable but hidden from AI. Checks that env vars are set AND that the actual key values don't appear in any AI context file.
234
-
235
- ```
236
- PASS: Secrets are accessible via env vars but hidden from AI context.
237
- ```
238
-
239
- ### `npx secretless-ai doctor`
240
-
241
- Diagnoses shell profile issues that cause "No API keys found" errors. Detects when keys are in an interactive-only profile (like `~/.zshrc`) that non-interactive subprocesses can't see.
242
-
243
- Use `--fix` to auto-fix: copies export lines from the wrong profile to the correct one (non-destructive, does not modify the original file).
250
+ Prevent secrets from being committed:
244
251
 
245
252
  ```bash
246
- npx secretless-ai doctor # Diagnose
247
- npx secretless-ai doctor --fix # Diagnose and auto-fix
253
+ npx secretless-ai hook install # Install pre-commit secret scanner
254
+ npx secretless-ai hook status # Check hook installation status
255
+ npx secretless-ai hook uninstall # Remove pre-commit hook
248
256
  ```
249
257
 
250
- Note: `init` also runs this auto-fix automatically, so most users never need to run `doctor` separately.
251
-
252
- ```
253
- Secretless Doctor
254
-
255
- Platform: darwin
256
- Shell: zsh
257
-
258
- Shell profiles:
259
- - ~/.zshenv (RECOMMENDED): not found
260
- + ~/.zshrc (interactive-only): 2 key(s)
261
- - ~/.zprofile (login-only): not found
262
-
263
- Auto-fix applied:
264
- Copied 2 export(s) from ~/.zshrc to ~/.zshenv
265
- + ANTHROPIC_API_KEY
266
- + OPENAI_API_KEY
267
- Restart your terminal for changes to take effect.
268
- ```
269
-
270
- ### `npx secretless-ai protect-mcp`
271
-
272
- Scans all MCP configs on your machine, encrypts plaintext secrets into a local vault, and rewrites configs to use the `secretless-mcp` wrapper. Safe to run multiple times — skips already-protected servers.
273
-
274
- ### `npx secretless-ai mcp-status`
275
-
276
- Shows protection status for every MCP server across all clients. Tells you which servers have exposed secrets and which are protected.
277
-
278
- ### `npx secretless-ai mcp-unprotect`
279
-
280
- Restores all MCP configs to their original state from backups. One command to undo everything.
281
-
282
- ### `npx secretless-ai status`
283
-
284
- Shows current protection status.
285
-
286
- ```
287
- Protected: Yes
288
- Tools: Claude Code, Cursor
289
- Hook: Installed
290
- Deny rules: 14
291
- Secrets: 0 found in config files
292
- ```
258
+ ## All Commands
259
+
260
+ | Command | Description |
261
+ |---------|-------------|
262
+ | `init` | Set up protections for your AI tools |
263
+ | `scan` | Scan for hardcoded secrets (49 patterns) |
264
+ | `status` | Show protection status |
265
+ | `verify` | Verify keys are usable but hidden from AI |
266
+ | `doctor [--fix]` | Diagnose and auto-fix shell profile issues |
267
+ | `clean [--dry-run]` | Scan and redact credentials in transcripts |
268
+ | `watch` | Monitor transcripts in real-time |
269
+ | **Secret Management** | |
270
+ | `secret set <NAME[=VALUE]>` | Store a secret |
271
+ | `secret list` | List stored secret names |
272
+ | `secret get <NAME>` | Retrieve a secret value (blocked in non-interactive contexts) |
273
+ | `secret rm <NAME>` | Remove a secret |
274
+ | `run [--only K1,K2] -- <cmd>` | Run command with secrets injected as env vars |
275
+ | `import <file>` | Import secrets from .env file |
276
+ | `import --detect` | Auto-find and import .env files |
277
+ | **Project Setup** | |
278
+ | `setup` | Interactive setup from `.secretless` manifest |
279
+ | `setup --check` | CI: fail if required secrets are missing |
280
+ | **Git Protection** | |
281
+ | `hook install` | Install pre-commit secret scanner |
282
+ | `hook uninstall` | Remove pre-commit hook |
283
+ | `hook status` | Check hook installation status |
284
+ | **MCP Protection** | |
285
+ | `protect-mcp [--backend TYPE]` | Encrypt MCP server secrets |
286
+ | `mcp-status` | Show MCP protection status |
287
+ | `mcp-unprotect` | Restore original MCP configs |
288
+ | **Backend Management** | |
289
+ | `backend` | Show current backend status |
290
+ | `backend set <TYPE>` | Set backend (local, keychain, 1password) |
291
+ | `backend list` | List all stored entries |
292
+ | `backend purge [--prefix] [--yes]` | Delete entries from backend |
293
+ | `migrate --from TYPE --to TYPE` | Migrate secrets between backends |
293
294
 
294
295
  ## What Gets Blocked
295
296
 
@@ -309,19 +310,11 @@ Commands that dump secret files (`cat .env`, `head *.key`) and commands that ech
309
310
 
310
311
  For Claude Code, Secretless installs a PreToolUse hook that intercepts every `Read`, `Grep`, `Glob`, `Bash`, `Write`, and `Edit` tool call. The hook runs *before* the tool executes, so secrets never enter the AI context window.
311
312
 
312
- ```bash
313
- # .claude/hooks/secretless-guard.sh
314
- # Runs before every tool call, checks file paths against block list
315
- # Returns deny decision if a secret file is targeted
316
- ```
317
-
318
- Additionally, Secretless adds `permissions.deny` rules to `.claude/settings.json` as a second layer of defense, and adds instructions to `CLAUDE.md` so Claude understands why certain files are blocked.
319
-
320
313
  ## Development
321
314
 
322
315
  ```bash
323
316
  npm run build # Compile TypeScript to dist/
324
- npm test # Run tests (vitest)
317
+ npm test # Run tests (vitest, 461 tests)
325
318
  npm run dev # Watch mode — recompile on file changes
326
319
  npm run clean # Remove dist/ directory
327
320
  ```
@@ -330,10 +323,12 @@ npm run clean # Remove dist/ directory
330
323
 
331
324
  - Node.js 18+
332
325
  - A project directory with at least one AI tool configured (or Secretless defaults to Claude Code)
326
+ - **Optional:** 1Password CLI (`op`) for 1Password backend
327
+ - **Optional:** macOS Keychain or `secret-tool` (Linux) for keychain backend
333
328
 
334
329
  ## Zero Dependencies
335
330
 
336
- Secretless has zero runtime dependencies. The npm package is 18 KB.
331
+ Secretless has zero runtime dependencies.
337
332
 
338
333
  ## OpenA2A Ecosystem
339
334
 
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Backend configuration manager.
3
+ *
4
+ * Reads and writes the user's backend preference from ~/.secretless-ai/config.json.
5
+ * Resolution priority: explicit CLI flag > config file > default ('local').
6
+ */
7
+ /** Writable backend types that can be selected by the user. */
8
+ export type SelectableBackendType = 'local' | 'keychain' | '1password';
9
+ /** Read the current backend configuration. Returns undefined if no config file exists. */
10
+ export declare function readBackendConfig(): SelectableBackendType | undefined;
11
+ /** Write the backend preference to the config file. */
12
+ export declare function writeBackendConfig(backend: SelectableBackendType): void;
13
+ /**
14
+ * Resolve which backend type to use.
15
+ *
16
+ * Priority: explicit flag > config file > default ('local').
17
+ */
18
+ export declare function resolveBackendType(explicitFlag?: string): SelectableBackendType;
19
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/backends/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,+DAA+D;AAC/D,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC;AAkBvE,0FAA0F;AAC1F,wBAAgB,iBAAiB,IAAI,qBAAqB,GAAG,SAAS,CAWrE;AAED,uDAAuD;AACvD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAiBvE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,qBAAqB,CAK/E"}
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /**
3
+ * Backend configuration manager.
4
+ *
5
+ * Reads and writes the user's backend preference from ~/.secretless-ai/config.json.
6
+ * Resolution priority: explicit CLI flag > config file > default ('local').
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.readBackendConfig = readBackendConfig;
43
+ exports.writeBackendConfig = writeBackendConfig;
44
+ exports.resolveBackendType = resolveBackendType;
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ const CONFIG_FILENAME = 'config.json';
48
+ const DEFAULT_BACKEND = 'local';
49
+ function configDir() {
50
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? '/tmp';
51
+ return path.join(home, '.secretless-ai');
52
+ }
53
+ function configPath() {
54
+ return path.join(configDir(), CONFIG_FILENAME);
55
+ }
56
+ /** Read the current backend configuration. Returns undefined if no config file exists. */
57
+ function readBackendConfig() {
58
+ try {
59
+ const raw = fs.readFileSync(configPath(), 'utf-8');
60
+ const config = JSON.parse(raw);
61
+ if (config.backend === 'local' || config.backend === 'keychain' || config.backend === '1password') {
62
+ return config.backend;
63
+ }
64
+ return undefined;
65
+ }
66
+ catch {
67
+ return undefined;
68
+ }
69
+ }
70
+ /** Write the backend preference to the config file. */
71
+ function writeBackendConfig(backend) {
72
+ const dir = configDir();
73
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
74
+ const fp = configPath();
75
+ let config = {};
76
+ try {
77
+ const raw = fs.readFileSync(fp, 'utf-8');
78
+ config = JSON.parse(raw);
79
+ }
80
+ catch {
81
+ // No existing config or invalid JSON — start fresh
82
+ }
83
+ config.backend = backend;
84
+ const tmpPath = fp + '.tmp';
85
+ fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 });
86
+ fs.renameSync(tmpPath, fp);
87
+ }
88
+ /**
89
+ * Resolve which backend type to use.
90
+ *
91
+ * Priority: explicit flag > config file > default ('local').
92
+ */
93
+ function resolveBackendType(explicitFlag) {
94
+ if (explicitFlag === 'local' || explicitFlag === 'keychain' || explicitFlag === '1password') {
95
+ return explicitFlag;
96
+ }
97
+ return readBackendConfig() ?? DEFAULT_BACKEND;
98
+ }
99
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/backends/config.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BH,8CAWC;AAGD,gDAiBC;AAOD,gDAKC;AAnED,uCAAyB;AACzB,2CAA6B;AAM7B,MAAM,eAAe,GAAG,aAAa,CAAC;AACtC,MAAM,eAAe,GAA0B,OAAO,CAAC;AAMvD,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,MAAM,CAAC;IACnE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,eAAe,CAAC,CAAC;AACjD,CAAC;AAED,0FAA0F;AAC1F,SAAgB,iBAAiB;IAC/B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;QACnD,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,IAAI,MAAM,CAAC,OAAO,KAAK,UAAU,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC;YAClG,OAAO,MAAM,CAAC,OAAO,CAAC;QACxB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,uDAAuD;AACvD,SAAgB,kBAAkB,CAAC,OAA8B;IAC/D,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEpD,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,IAAI,MAAM,GAAqB,EAAE,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACzC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,mDAAmD;IACrD,CAAC;IAED,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,MAAM,OAAO,GAAG,EAAE,GAAG,MAAM,CAAC;IAC5B,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACnF,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,YAAqB;IACtD,IAAI,YAAY,KAAK,OAAO,IAAI,YAAY,KAAK,UAAU,IAAI,YAAY,KAAK,WAAW,EAAE,CAAC;QAC5F,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,iBAAiB,EAAE,IAAI,eAAe,CAAC;AAChD,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Backend factory — creates the appropriate WritableSecretBackend based on type.
3
+ *
4
+ * Dispatches 'keychain' to the platform-specific implementation (macOS or Linux).
5
+ * Dispatches '1password' to the OnePasswordBackend (requires `op` CLI).
6
+ * Falls back to 'local' on unsupported platforms with a console message.
7
+ */
8
+ import type { WritableSecretBackend } from './types';
9
+ import type { SelectableBackendType } from './config';
10
+ /**
11
+ * Create a WritableSecretBackend instance for the given type.
12
+ *
13
+ * @param type - 'local', 'keychain', or '1password'
14
+ * @param config - Backend-specific configuration (e.g. storeDir, key, vault)
15
+ */
16
+ export declare function createBackend(type: SelectableBackendType, config?: Record<string, unknown>): WritableSecretBackend;
17
+ /**
18
+ * Check if the OS keychain is available on the current platform.
19
+ * Returns a description of the keychain status.
20
+ */
21
+ export declare function isKeychainAvailable(): {
22
+ available: boolean;
23
+ platform: string;
24
+ message: string;
25
+ };
26
+ /**
27
+ * Check if the 1Password CLI (`op`) is available and authenticated.
28
+ * Returns availability status and an actionable message.
29
+ */
30
+ export declare function isOnePasswordAvailable(): {
31
+ available: boolean;
32
+ message: string;
33
+ };
34
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/backends/factory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEtD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,qBAAqB,EAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,qBAAqB,CAYvB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAgC/F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAwBhF"}
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ /**
3
+ * Backend factory — creates the appropriate WritableSecretBackend based on type.
4
+ *
5
+ * Dispatches 'keychain' to the platform-specific implementation (macOS or Linux).
6
+ * Dispatches '1password' to the OnePasswordBackend (requires `op` CLI).
7
+ * Falls back to 'local' on unsupported platforms with a console message.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.createBackend = createBackend;
11
+ exports.isKeychainAvailable = isKeychainAvailable;
12
+ exports.isOnePasswordAvailable = isOnePasswordAvailable;
13
+ const local_1 = require("./local");
14
+ const keychain_macos_1 = require("./keychain-macos");
15
+ const keychain_linux_1 = require("./keychain-linux");
16
+ const onepassword_1 = require("./onepassword");
17
+ /**
18
+ * Create a WritableSecretBackend instance for the given type.
19
+ *
20
+ * @param type - 'local', 'keychain', or '1password'
21
+ * @param config - Backend-specific configuration (e.g. storeDir, key, vault)
22
+ */
23
+ function createBackend(type, config) {
24
+ switch (type) {
25
+ case 'keychain':
26
+ return createKeychainBackend(config);
27
+ case '1password':
28
+ return new onepassword_1.OnePasswordBackend(config);
29
+ case 'local':
30
+ default:
31
+ return new local_1.LocalBackend(config);
32
+ }
33
+ }
34
+ /**
35
+ * Check if the OS keychain is available on the current platform.
36
+ * Returns a description of the keychain status.
37
+ */
38
+ function isKeychainAvailable() {
39
+ const platform = process.platform;
40
+ if (platform === 'darwin') {
41
+ try {
42
+ const { execFileSync } = require('child_process');
43
+ execFileSync('security', ['default-keychain'], { stdio: 'pipe' });
44
+ return { available: true, platform: 'macOS', message: 'macOS Keychain is available' };
45
+ }
46
+ catch {
47
+ return { available: false, platform: 'macOS', message: 'macOS Keychain is not accessible' };
48
+ }
49
+ }
50
+ if (platform === 'linux') {
51
+ try {
52
+ const { execFileSync } = require('child_process');
53
+ execFileSync('which', ['secret-tool'], { stdio: 'pipe' });
54
+ return { available: true, platform: 'Linux', message: 'secret-tool is available (Linux Secret Service)' };
55
+ }
56
+ catch {
57
+ return {
58
+ available: false,
59
+ platform: 'Linux',
60
+ message: 'secret-tool not found. Install libsecret-tools (Debian/Ubuntu) or libsecret (Fedora/RHEL).',
61
+ };
62
+ }
63
+ }
64
+ return {
65
+ available: false,
66
+ platform: platform,
67
+ message: `OS keychain is not supported on ${platform}. Using local encrypted backend.`,
68
+ };
69
+ }
70
+ /**
71
+ * Check if the 1Password CLI (`op`) is available and authenticated.
72
+ * Returns availability status and an actionable message.
73
+ */
74
+ function isOnePasswordAvailable() {
75
+ try {
76
+ const { execFileSync } = require('child_process');
77
+ execFileSync('op', ['--version'], { stdio: 'pipe' });
78
+ }
79
+ catch {
80
+ return {
81
+ available: false,
82
+ message: '1Password CLI (op) not found. Install from https://developer.1password.com/docs/cli',
83
+ };
84
+ }
85
+ try {
86
+ const { execFileSync } = require('child_process');
87
+ execFileSync('op', ['account', 'get', '--format', 'json'], { stdio: 'pipe' });
88
+ return {
89
+ available: true,
90
+ message: '1Password CLI installed and authenticated',
91
+ };
92
+ }
93
+ catch {
94
+ return {
95
+ available: false,
96
+ message: '1Password CLI installed but not signed in. Run `op signin` or set OP_SERVICE_ACCOUNT_TOKEN.',
97
+ };
98
+ }
99
+ }
100
+ function createKeychainBackend(config) {
101
+ const platform = process.platform;
102
+ if (platform === 'darwin') {
103
+ return new keychain_macos_1.MacOSKeychainBackend(config);
104
+ }
105
+ if (platform === 'linux') {
106
+ return new keychain_linux_1.LinuxKeychainBackend(config);
107
+ }
108
+ // Unsupported platform — fall back to local with a message
109
+ console.error(`secretless: OS keychain not supported on ${platform}. Falling back to local encrypted backend.`);
110
+ return new local_1.LocalBackend(config);
111
+ }
112
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/backends/factory.ts"],"names":[],"mappings":"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"}