@skillrecordings/cli 0.2.3 → 0.4.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.
@@ -53,7 +53,7 @@ import {
53
53
  storeDraftSuccess,
54
54
  validate,
55
55
  validateSync
56
- } from "./chunk-5QCJIGF3.js";
56
+ } from "./chunk-TFLLCHUX.js";
57
57
  import "./chunk-KEV3QKXP.js";
58
58
  import "./chunk-HK3PEWFD.js";
59
59
  import "./chunk-WYKL32C3.js";
@@ -117,4 +117,4 @@ export {
117
117
  validate,
118
118
  validateSync
119
119
  };
120
- //# sourceMappingURL=pipeline-JELLPZAE.js.map
120
+ //# sourceMappingURL=pipeline-DRKFIH73.js.map
package/dist/preload.js CHANGED
@@ -1,6 +1,3 @@
1
- import {
2
- decrypt
3
- } from "./chunk-MNFDM56Q.js";
4
1
  import {
5
2
  init_esm_shims
6
3
  } from "./chunk-WFANXVQG.js";
@@ -11,12 +8,21 @@ import path2 from "path";
11
8
 
12
9
  // src/lib/env-loader.ts
13
10
  init_esm_shims();
14
- import { execSync } from "child_process";
15
11
  import fs from "fs/promises";
16
- import os from "os";
17
12
  import path from "path";
18
13
  import { config } from "dotenv-flow";
19
- var OP_AGE_KEY_REF = "op://Support/skill-cli-age-key/private_key";
14
+
15
+ // src/lib/crypto.ts
16
+ init_esm_shims();
17
+ import { Decrypter } from "age-encryption";
18
+ async function decrypt(encrypted, privateKey) {
19
+ const decrypter = new Decrypter();
20
+ decrypter.addIdentity(privateKey);
21
+ const input = encrypted instanceof Buffer ? new Uint8Array(encrypted) : encrypted;
22
+ return decrypter.decrypt(input, "text");
23
+ }
24
+
25
+ // src/lib/env-loader.ts
20
26
  async function loadSecrets(cliDirOverride) {
21
27
  const cliDir2 = cliDirOverride ?? path.resolve(import.meta.dirname, "../..");
22
28
  const localEnvExists = await checkLocalEnv(cliDir2);
@@ -25,15 +31,11 @@ async function loadSecrets(cliDirOverride) {
25
31
  return;
26
32
  }
27
33
  const encryptedPath = path.join(cliDir2, ".env.encrypted");
28
- const encryptedExists = await fileExists(encryptedPath);
29
- if (encryptedExists) {
30
- let privateKey = process.env.AGE_SECRET_KEY;
31
- if (!privateKey) {
32
- privateKey = await tryLoadKeyFrom1Password();
33
- }
34
+ if (await fileExists(encryptedPath)) {
35
+ const privateKey = process.env.AGE_SECRET_KEY;
34
36
  if (!privateKey) {
35
37
  throw new Error(
36
- "Found .env.encrypted but cannot decrypt.\n\nOptions:\n1. Set AGE_SECRET_KEY env var with the private key\n2. Create ~/.op-token with 1Password service account token\n\nSee docs/CLI-AUTH.md for setup instructions."
38
+ 'Found .env.encrypted but AGE_SECRET_KEY is not set.\n\nAdd to your shell profile (~/.zshrc):\n export AGE_SECRET_KEY="AGE-SECRET-KEY-1..."\n\nOr create .env.local in the CLI package directory to bypass encryption.'
37
39
  );
38
40
  }
39
41
  try {
@@ -44,56 +46,14 @@ async function loadSecrets(cliDirOverride) {
44
46
  } catch (error) {
45
47
  throw new Error(
46
48
  `Failed to decrypt .env.encrypted: ${error instanceof Error ? error.message : String(error)}
47
- Check that the decryption key matches the encryption key.`
49
+ Check that AGE_SECRET_KEY matches the encryption key.`
48
50
  );
49
51
  }
50
52
  }
51
53
  throw new Error(
52
- "No environment configuration found.\n\nOptions:\n1. Create .env.local in packages/cli/ with your secrets\n2. Set AGE_SECRET_KEY and use encrypted .env.encrypted\n3. Create ~/.op-token for automatic 1Password-based decryption\n\nSee docs/CLI-AUTH.md for setup instructions."
54
+ "No environment configuration found.\n\nOptions:\n1. Set AGE_SECRET_KEY in your shell profile (for .env.encrypted)\n2. Create .env.local in the CLI package directory\n\nSee docs/CLI-AUTH.md for setup instructions."
53
55
  );
54
56
  }
55
- async function tryLoadKeyFrom1Password() {
56
- const opTokenPath = path.join(os.homedir(), ".op-token");
57
- if (!await fileExists(opTokenPath)) {
58
- return void 0;
59
- }
60
- try {
61
- const tokenFileContent = await fs.readFile(opTokenPath, "utf-8");
62
- const token = parseOpTokenFile(tokenFileContent);
63
- if (!token) {
64
- return void 0;
65
- }
66
- process.env.OP_SERVICE_ACCOUNT_TOKEN = token;
67
- const privateKey = execSync(`op read "${OP_AGE_KEY_REF}"`, {
68
- encoding: "utf-8",
69
- stdio: ["ignore", "pipe", "pipe"],
70
- env: process.env
71
- // Explicit env pass required for token to be visible
72
- }).trim();
73
- return privateKey || void 0;
74
- } catch {
75
- return void 0;
76
- }
77
- }
78
- function parseOpTokenFile(content) {
79
- const lines = content.split("\n");
80
- for (const line of lines) {
81
- const trimmed = line.trim();
82
- const match = trimmed.match(
83
- /^export\s+OP_SERVICE_ACCOUNT_TOKEN\s*=\s*["']?([^"'\s]+)["']?/
84
- );
85
- if (match?.[1]) {
86
- return match[1];
87
- }
88
- const bareMatch = trimmed.match(
89
- /^OP_SERVICE_ACCOUNT_TOKEN\s*=\s*["']?([^"'\s]+)["']?/
90
- );
91
- if (bareMatch?.[1]) {
92
- return bareMatch[1];
93
- }
94
- }
95
- return void 0;
96
- }
97
57
  async function checkLocalEnv(dir) {
98
58
  const candidates = [".env.local", ".env"];
99
59
  for (const file of candidates) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../preload.ts","../src/lib/env-loader.ts"],"sourcesContent":["import path from 'node:path'\nimport { loadSecrets } from './src/lib/env-loader.js'\n\n// Calculate CLI package root - works for both dev (preload.ts) and bundled (dist/preload.js)\n// In dev: dirname is packages/cli, so '..' goes to packages, '../..' to root - wrong, we want '.'\n// In bundle: dirname is packages/cli/dist, so '..' goes to packages/cli - correct\n// Solution: check if we're in dist/ and adjust accordingly\nconst dirname = import.meta.dirname\nconst cliDir =\n dirname.endsWith('/dist') || dirname.endsWith('\\\\dist')\n ? path.resolve(dirname, '..')\n : dirname\n\n// Don't crash for commands that don't need secrets (--help, auth, etc.)\nconst noSecretsNeeded =\n process.argv.length <= 2 ||\n process.argv.some((a) =>\n ['--help', '-h', '--version', '-V', 'auth'].includes(a)\n )\n\ntry {\n await loadSecrets(cliDir)\n} catch (err) {\n if (noSecretsNeeded) {\n // Skip env validation so index.js can load without DATABASE_URL\n process.env.SKIP_ENV_VALIDATION = '1'\n } else {\n console.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n}\n","import { execSync } from 'node:child_process'\nimport fs from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { config } from 'dotenv-flow'\nimport { decrypt } from './crypto.js'\n\n/** 1Password reference for the age private key */\nconst OP_AGE_KEY_REF = 'op://Support/skill-cli-age-key/private_key'\n\n/**\n * Load secrets from layered fallback:\n * 1. Local .env/.env.local files\n * 2. Encrypted .env.encrypted file (auto-fetches key from 1Password if ~/.op-token exists)\n * 3. Fail with clear instructions\n *\n * Injects secrets into process.env\n *\n * @param cliDirOverride - Optional path to CLI package root (use when calling from bundled code)\n */\nexport async function loadSecrets(cliDirOverride?: string): Promise<void> {\n const cliDir = cliDirOverride ?? path.resolve(import.meta.dirname, '../..')\n\n // Layer 1: Check for local .env files\n const localEnvExists = await checkLocalEnv(cliDir)\n if (localEnvExists) {\n config({ path: cliDir, silent: true })\n return\n }\n\n // Layer 2: Try encrypted fallback\n const encryptedPath = path.join(cliDir, '.env.encrypted')\n const encryptedExists = await fileExists(encryptedPath)\n\n if (encryptedExists) {\n // Try to get the private key - either from env or via 1Password\n let privateKey = process.env.AGE_SECRET_KEY\n\n if (!privateKey) {\n // Attempt to load from 1Password via ~/.op-token\n privateKey = await tryLoadKeyFrom1Password()\n }\n\n if (!privateKey) {\n throw new Error(\n 'Found .env.encrypted but cannot decrypt.\\n\\n' +\n 'Options:\\n' +\n '1. Set AGE_SECRET_KEY env var with the private key\\n' +\n '2. Create ~/.op-token with 1Password service account token\\n\\n' +\n 'See docs/CLI-AUTH.md for setup instructions.'\n )\n }\n\n try {\n const encryptedData = await fs.readFile(encryptedPath)\n const decrypted = await decrypt(encryptedData, privateKey)\n\n // Parse decrypted env file format (KEY=VALUE lines)\n parseAndInjectEnv(decrypted)\n return\n } catch (error) {\n throw new Error(\n `Failed to decrypt .env.encrypted: ${error instanceof Error ? error.message : String(error)}\\n` +\n 'Check that the decryption key matches the encryption key.'\n )\n }\n }\n\n // Layer 3: Nothing found - fail with instructions\n throw new Error(\n 'No environment configuration found.\\n\\n' +\n 'Options:\\n' +\n '1. Create .env.local in packages/cli/ with your secrets\\n' +\n '2. Set AGE_SECRET_KEY and use encrypted .env.encrypted\\n' +\n '3. Create ~/.op-token for automatic 1Password-based decryption\\n\\n' +\n 'See docs/CLI-AUTH.md for setup instructions.'\n )\n}\n\n/**\n * Try to load the age private key from 1Password using ~/.op-token\n * Returns the key if successful, undefined otherwise\n */\nasync function tryLoadKeyFrom1Password(): Promise<string | undefined> {\n const opTokenPath = path.join(os.homedir(), '.op-token')\n\n // Check if ~/.op-token exists\n if (!(await fileExists(opTokenPath))) {\n return undefined\n }\n\n try {\n // Read and parse ~/.op-token (format: export VAR=\"value\")\n const tokenFileContent = await fs.readFile(opTokenPath, 'utf-8')\n const token = parseOpTokenFile(tokenFileContent)\n\n if (!token) {\n return undefined\n }\n\n // Set the token in env for op CLI to use\n process.env.OP_SERVICE_ACCOUNT_TOKEN = token\n\n // Fetch the age private key from 1Password\n const privateKey = execSync(`op read \"${OP_AGE_KEY_REF}\"`, {\n encoding: 'utf-8',\n stdio: ['ignore', 'pipe', 'pipe'],\n env: process.env, // Explicit env pass required for token to be visible\n }).trim()\n\n return privateKey || undefined\n } catch {\n // Silently fail - caller will show appropriate error\n return undefined\n }\n}\n\n/**\n * Parse ~/.op-token file format: export OP_SERVICE_ACCOUNT_TOKEN=\"value\"\n */\nfunction parseOpTokenFile(content: string): string | undefined {\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Match: export OP_SERVICE_ACCOUNT_TOKEN=\"value\" or =value\n const match = trimmed.match(\n /^export\\s+OP_SERVICE_ACCOUNT_TOKEN\\s*=\\s*[\"']?([^\"'\\s]+)[\"']?/\n )\n if (match?.[1]) {\n return match[1]\n }\n\n // Also support bare assignment: OP_SERVICE_ACCOUNT_TOKEN=value\n const bareMatch = trimmed.match(\n /^OP_SERVICE_ACCOUNT_TOKEN\\s*=\\s*[\"']?([^\"'\\s]+)[\"']?/\n )\n if (bareMatch?.[1]) {\n return bareMatch[1]\n }\n }\n\n return undefined\n}\n\n/**\n * Check if any local .env files exist\n */\nasync function checkLocalEnv(dir: string): Promise<boolean> {\n const candidates = ['.env.local', '.env']\n for (const file of candidates) {\n if (await fileExists(path.join(dir, file))) {\n return true\n }\n }\n return false\n}\n\n/**\n * Check if a file exists\n */\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parse env file format (KEY=VALUE) and inject into process.env\n * Handles quoted values, comments, and empty lines\n */\nfunction parseAndInjectEnv(content: string): void {\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Skip comments and empty lines\n if (!trimmed || trimmed.startsWith('#')) {\n continue\n }\n\n const eqIndex = trimmed.indexOf('=')\n if (eqIndex === -1) {\n continue\n }\n\n const key = trimmed.slice(0, eqIndex).trim()\n let value = trimmed.slice(eqIndex + 1).trim()\n\n // Remove quotes if present\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1)\n }\n\n // Only set if not already defined (respect existing env vars)\n if (key && process.env[key] === undefined) {\n process.env[key] = value\n }\n }\n}\n"],"mappings":"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","names":["path","cliDir","path"]}
1
+ {"version":3,"sources":["../preload.ts","../src/lib/env-loader.ts","../src/lib/crypto.ts"],"sourcesContent":["import path from 'node:path'\nimport { loadSecrets } from './src/lib/env-loader.js'\n\n// Calculate CLI package root - works for both dev (preload.ts) and bundled (dist/preload.js)\n// In dev: dirname is packages/cli, so '..' goes to packages, '../..' to root - wrong, we want '.'\n// In bundle: dirname is packages/cli/dist, so '..' goes to packages/cli - correct\n// Solution: check if we're in dist/ and adjust accordingly\nconst dirname = import.meta.dirname\nconst cliDir =\n dirname.endsWith('/dist') || dirname.endsWith('\\\\dist')\n ? path.resolve(dirname, '..')\n : dirname\n\n// Don't crash for commands that don't need secrets (--help, auth, etc.)\nconst noSecretsNeeded =\n process.argv.length <= 2 ||\n process.argv.some((a) =>\n ['--help', '-h', '--version', '-V', 'auth'].includes(a)\n )\n\ntry {\n await loadSecrets(cliDir)\n} catch (err) {\n if (noSecretsNeeded) {\n // Skip env validation so index.js can load without DATABASE_URL\n process.env.SKIP_ENV_VALIDATION = '1'\n } else {\n console.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { config } from 'dotenv-flow'\nimport { decrypt } from './crypto.js'\n\n/**\n * Load secrets from layered fallback:\n * 1. Local .env/.env.local files (dev override)\n * 2. Encrypted .env.encrypted + AGE_SECRET_KEY (npm installs)\n * 3. Fail with clear instructions\n *\n * Injects secrets into process.env\n *\n * @param cliDirOverride - Optional path to CLI package root (use when calling from bundled code)\n */\nexport async function loadSecrets(cliDirOverride?: string): Promise<void> {\n const cliDir = cliDirOverride ?? path.resolve(import.meta.dirname, '../..')\n\n // Layer 1: Check for local .env files (takes priority — dev workflow)\n const localEnvExists = await checkLocalEnv(cliDir)\n if (localEnvExists) {\n config({ path: cliDir, silent: true })\n return\n }\n\n // Layer 2: Decrypt .env.encrypted with AGE_SECRET_KEY\n const encryptedPath = path.join(cliDir, '.env.encrypted')\n if (await fileExists(encryptedPath)) {\n const privateKey = process.env.AGE_SECRET_KEY\n if (!privateKey) {\n throw new Error(\n 'Found .env.encrypted but AGE_SECRET_KEY is not set.\\n\\n' +\n 'Add to your shell profile (~/.zshrc):\\n' +\n ' export AGE_SECRET_KEY=\"AGE-SECRET-KEY-1...\"\\n\\n' +\n 'Or create .env.local in the CLI package directory to bypass encryption.'\n )\n }\n\n try {\n const encryptedData = await fs.readFile(encryptedPath)\n const decrypted = await decrypt(encryptedData, privateKey)\n parseAndInjectEnv(decrypted)\n return\n } catch (error) {\n throw new Error(\n `Failed to decrypt .env.encrypted: ${error instanceof Error ? error.message : String(error)}\\n` +\n 'Check that AGE_SECRET_KEY matches the encryption key.'\n )\n }\n }\n\n // Layer 3: Nothing found\n throw new Error(\n 'No environment configuration found.\\n\\n' +\n 'Options:\\n' +\n '1. Set AGE_SECRET_KEY in your shell profile (for .env.encrypted)\\n' +\n '2. Create .env.local in the CLI package directory\\n\\n' +\n 'See docs/CLI-AUTH.md for setup instructions.'\n )\n}\n\n/**\n * Check if any local .env files exist\n */\nasync function checkLocalEnv(dir: string): Promise<boolean> {\n const candidates = ['.env.local', '.env']\n for (const file of candidates) {\n if (await fileExists(path.join(dir, file))) {\n return true\n }\n }\n return false\n}\n\n/**\n * Check if a file exists\n */\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parse env file format (KEY=VALUE) and inject into process.env\n * Handles quoted values, comments, and empty lines\n */\nfunction parseAndInjectEnv(content: string): void {\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Skip comments and empty lines\n if (!trimmed || trimmed.startsWith('#')) {\n continue\n }\n\n const eqIndex = trimmed.indexOf('=')\n if (eqIndex === -1) {\n continue\n }\n\n const key = trimmed.slice(0, eqIndex).trim()\n let value = trimmed.slice(eqIndex + 1).trim()\n\n // Remove quotes if present\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1)\n }\n\n // Only set if not already defined (respect existing env vars)\n if (key && process.env[key] === undefined) {\n process.env[key] = value\n }\n }\n}\n","import { Decrypter } from 'age-encryption'\n\n/**\n * Decrypt data with an age private key\n * @param encrypted - Encrypted data as Uint8Array or Buffer\n * @param privateKey - age private key (AGE-SECRET-KEY-1...)\n * @returns Decrypted data as string\n */\nexport async function decrypt(\n encrypted: Uint8Array | Buffer,\n privateKey: string\n): Promise<string> {\n const decrypter = new Decrypter()\n decrypter.addIdentity(privateKey)\n\n const input =\n encrypted instanceof Buffer ? new Uint8Array(encrypted) : encrypted\n return decrypter.decrypt(input, 'text')\n}\n"],"mappings":"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","names":["path","cliDir","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillrecordings/cli",
3
- "version": "0.2.3",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "skill": "./bin/skill.mjs"