@id-wispera/core 0.1.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 (154) hide show
  1. package/README.md +268 -0
  2. package/dist/audit.d.ts +68 -0
  3. package/dist/audit.d.ts.map +1 -0
  4. package/dist/audit.js +252 -0
  5. package/dist/audit.js.map +1 -0
  6. package/dist/auth/index.d.ts +8 -0
  7. package/dist/auth/index.d.ts.map +1 -0
  8. package/dist/auth/index.js +8 -0
  9. package/dist/auth/index.js.map +1 -0
  10. package/dist/auth/keychainProvider.d.ts +40 -0
  11. package/dist/auth/keychainProvider.d.ts.map +1 -0
  12. package/dist/auth/keychainProvider.js +98 -0
  13. package/dist/auth/keychainProvider.js.map +1 -0
  14. package/dist/auth/passphraseProvider.d.ts +80 -0
  15. package/dist/auth/passphraseProvider.d.ts.map +1 -0
  16. package/dist/auth/passphraseProvider.js +188 -0
  17. package/dist/auth/passphraseProvider.js.map +1 -0
  18. package/dist/auth/sessionTokenManager.d.ts +106 -0
  19. package/dist/auth/sessionTokenManager.d.ts.map +1 -0
  20. package/dist/auth/sessionTokenManager.js +263 -0
  21. package/dist/auth/sessionTokenManager.js.map +1 -0
  22. package/dist/delegation.d.ts +81 -0
  23. package/dist/delegation.d.ts.map +1 -0
  24. package/dist/delegation.js +299 -0
  25. package/dist/delegation.js.map +1 -0
  26. package/dist/detection.d.ts +35 -0
  27. package/dist/detection.d.ts.map +1 -0
  28. package/dist/detection.js +474 -0
  29. package/dist/detection.js.map +1 -0
  30. package/dist/exec/execManager.d.ts +60 -0
  31. package/dist/exec/execManager.d.ts.map +1 -0
  32. package/dist/exec/execManager.js +226 -0
  33. package/dist/exec/execManager.js.map +1 -0
  34. package/dist/exec/index.d.ts +6 -0
  35. package/dist/exec/index.d.ts.map +1 -0
  36. package/dist/exec/index.js +5 -0
  37. package/dist/exec/index.js.map +1 -0
  38. package/dist/index.d.ts +35 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +98 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/integrations/base.d.ts +64 -0
  43. package/dist/integrations/base.d.ts.map +1 -0
  44. package/dist/integrations/base.js +173 -0
  45. package/dist/integrations/base.js.map +1 -0
  46. package/dist/integrations/envMapping.d.ts +47 -0
  47. package/dist/integrations/envMapping.d.ts.map +1 -0
  48. package/dist/integrations/envMapping.js +174 -0
  49. package/dist/integrations/envMapping.js.map +1 -0
  50. package/dist/integrations/google-a2a.d.ts +48 -0
  51. package/dist/integrations/google-a2a.d.ts.map +1 -0
  52. package/dist/integrations/google-a2a.js +108 -0
  53. package/dist/integrations/google-a2a.js.map +1 -0
  54. package/dist/integrations/index.d.ts +14 -0
  55. package/dist/integrations/index.d.ts.map +1 -0
  56. package/dist/integrations/index.js +14 -0
  57. package/dist/integrations/index.js.map +1 -0
  58. package/dist/integrations/langchain.d.ts +38 -0
  59. package/dist/integrations/langchain.d.ts.map +1 -0
  60. package/dist/integrations/langchain.js +45 -0
  61. package/dist/integrations/langchain.js.map +1 -0
  62. package/dist/integrations/openai-agents.d.ts +76 -0
  63. package/dist/integrations/openai-agents.d.ts.map +1 -0
  64. package/dist/integrations/openai-agents.js +95 -0
  65. package/dist/integrations/openai-agents.js.map +1 -0
  66. package/dist/integrations/slack.d.ts +59 -0
  67. package/dist/integrations/slack.d.ts.map +1 -0
  68. package/dist/integrations/slack.js +113 -0
  69. package/dist/integrations/slack.js.map +1 -0
  70. package/dist/integrations/types.d.ts +107 -0
  71. package/dist/integrations/types.d.ts.map +1 -0
  72. package/dist/integrations/types.js +6 -0
  73. package/dist/integrations/types.js.map +1 -0
  74. package/dist/locations.d.ts +157 -0
  75. package/dist/locations.d.ts.map +1 -0
  76. package/dist/locations.js +733 -0
  77. package/dist/locations.js.map +1 -0
  78. package/dist/passport.d.ts +70 -0
  79. package/dist/passport.d.ts.map +1 -0
  80. package/dist/passport.js +429 -0
  81. package/dist/passport.js.map +1 -0
  82. package/dist/policy.d.ts +80 -0
  83. package/dist/policy.d.ts.map +1 -0
  84. package/dist/policy.js +392 -0
  85. package/dist/policy.js.map +1 -0
  86. package/dist/providers/openclaw.d.ts +80 -0
  87. package/dist/providers/openclaw.d.ts.map +1 -0
  88. package/dist/providers/openclaw.js +712 -0
  89. package/dist/providers/openclaw.js.map +1 -0
  90. package/dist/provisioning/adminPassport.d.ts +51 -0
  91. package/dist/provisioning/adminPassport.d.ts.map +1 -0
  92. package/dist/provisioning/adminPassport.js +101 -0
  93. package/dist/provisioning/adminPassport.js.map +1 -0
  94. package/dist/provisioning/index.d.ts +81 -0
  95. package/dist/provisioning/index.d.ts.map +1 -0
  96. package/dist/provisioning/index.js +141 -0
  97. package/dist/provisioning/index.js.map +1 -0
  98. package/dist/provisioning/provider.d.ts +59 -0
  99. package/dist/provisioning/provider.d.ts.map +1 -0
  100. package/dist/provisioning/provider.js +52 -0
  101. package/dist/provisioning/provider.js.map +1 -0
  102. package/dist/provisioning/providers/anthropic.d.ts +32 -0
  103. package/dist/provisioning/providers/anthropic.d.ts.map +1 -0
  104. package/dist/provisioning/providers/anthropic.js +116 -0
  105. package/dist/provisioning/providers/anthropic.js.map +1 -0
  106. package/dist/provisioning/providers/aws.d.ts +29 -0
  107. package/dist/provisioning/providers/aws.d.ts.map +1 -0
  108. package/dist/provisioning/providers/aws.js +455 -0
  109. package/dist/provisioning/providers/aws.js.map +1 -0
  110. package/dist/provisioning/providers/azure-entra.d.ts +32 -0
  111. package/dist/provisioning/providers/azure-entra.d.ts.map +1 -0
  112. package/dist/provisioning/providers/azure-entra.js +312 -0
  113. package/dist/provisioning/providers/azure-entra.js.map +1 -0
  114. package/dist/provisioning/providers/github.d.ts +24 -0
  115. package/dist/provisioning/providers/github.d.ts.map +1 -0
  116. package/dist/provisioning/providers/github.js +219 -0
  117. package/dist/provisioning/providers/github.js.map +1 -0
  118. package/dist/provisioning/providers/google-cloud.d.ts +34 -0
  119. package/dist/provisioning/providers/google-cloud.d.ts.map +1 -0
  120. package/dist/provisioning/providers/google-cloud.js +366 -0
  121. package/dist/provisioning/providers/google-cloud.js.map +1 -0
  122. package/dist/provisioning/providers/openai.d.ts +29 -0
  123. package/dist/provisioning/providers/openai.d.ts.map +1 -0
  124. package/dist/provisioning/providers/openai.js +263 -0
  125. package/dist/provisioning/providers/openai.js.map +1 -0
  126. package/dist/provisioning/providers/sendgrid.d.ts +27 -0
  127. package/dist/provisioning/providers/sendgrid.d.ts.map +1 -0
  128. package/dist/provisioning/providers/sendgrid.js +186 -0
  129. package/dist/provisioning/providers/sendgrid.js.map +1 -0
  130. package/dist/provisioning/providers/twilio.d.ts +27 -0
  131. package/dist/provisioning/providers/twilio.d.ts.map +1 -0
  132. package/dist/provisioning/providers/twilio.js +194 -0
  133. package/dist/provisioning/providers/twilio.js.map +1 -0
  134. package/dist/provisioning/types.d.ts +274 -0
  135. package/dist/provisioning/types.d.ts.map +1 -0
  136. package/dist/provisioning/types.js +6 -0
  137. package/dist/provisioning/types.js.map +1 -0
  138. package/dist/sharing.d.ts +60 -0
  139. package/dist/sharing.d.ts.map +1 -0
  140. package/dist/sharing.js +305 -0
  141. package/dist/sharing.js.map +1 -0
  142. package/dist/types.d.ts +396 -0
  143. package/dist/types.d.ts.map +1 -0
  144. package/dist/types.js +88 -0
  145. package/dist/types.js.map +1 -0
  146. package/dist/utils.d.ts +45 -0
  147. package/dist/utils.d.ts.map +1 -0
  148. package/dist/utils.js +110 -0
  149. package/dist/utils.js.map +1 -0
  150. package/dist/vault.d.ts +151 -0
  151. package/dist/vault.d.ts.map +1 -0
  152. package/dist/vault.js +499 -0
  153. package/dist/vault.js.map +1 -0
  154. package/package.json +117 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * ID Wispera Keychain Provider
3
+ * Optional OS keychain integration for secure passphrase storage.
4
+ *
5
+ * Uses `keytar` when available (macOS Keychain, Windows Credential Manager,
6
+ * Linux Secret Service). Degrades gracefully — returns null if keytar is
7
+ * not installed or the OS keychain is unavailable.
8
+ */
9
+ const SERVICE_NAME = 'id-wispera';
10
+ const ACCOUNT_NAME = 'vault-passphrase';
11
+ /**
12
+ * Thin wrapper around the OS keychain. Every public method catches import
13
+ * and runtime errors so callers never need to know whether keytar is present.
14
+ */
15
+ export class KeychainProvider {
16
+ keytar = null;
17
+ loadAttempted = false;
18
+ /**
19
+ * Lazily try to load keytar. Called once — result is cached.
20
+ */
21
+ async loadKeytar() {
22
+ if (this.loadAttempted)
23
+ return this.keytar;
24
+ this.loadAttempted = true;
25
+ try {
26
+ // Dynamic import so keytar remains an optional peer dependency
27
+ this.keytar = await import('keytar');
28
+ }
29
+ catch {
30
+ this.keytar = null;
31
+ }
32
+ return this.keytar;
33
+ }
34
+ /**
35
+ * Whether the keychain backend is available on this system.
36
+ */
37
+ async isAvailable() {
38
+ const kt = await this.loadKeytar();
39
+ if (!kt)
40
+ return false;
41
+ // Some environments have keytar installed but no backing store.
42
+ // Smoke-test with a harmless read to verify.
43
+ try {
44
+ await kt.getPassword(SERVICE_NAME, '__probe__');
45
+ return true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ /**
52
+ * Store the vault passphrase in the OS keychain.
53
+ * Returns `true` on success, `false` if the keychain is unavailable.
54
+ */
55
+ async store(passphrase) {
56
+ const kt = await this.loadKeytar();
57
+ if (!kt)
58
+ return false;
59
+ try {
60
+ await kt.setPassword(SERVICE_NAME, ACCOUNT_NAME, passphrase);
61
+ return true;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ /**
68
+ * Retrieve the vault passphrase from the OS keychain.
69
+ * Returns `null` if unavailable or no entry exists.
70
+ */
71
+ async retrieve() {
72
+ const kt = await this.loadKeytar();
73
+ if (!kt)
74
+ return null;
75
+ try {
76
+ return await kt.getPassword(SERVICE_NAME, ACCOUNT_NAME);
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ /**
83
+ * Remove the vault passphrase from the OS keychain.
84
+ * Returns `true` if deleted, `false` if unavailable or nothing to delete.
85
+ */
86
+ async remove() {
87
+ const kt = await this.loadKeytar();
88
+ if (!kt)
89
+ return false;
90
+ try {
91
+ return await kt.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
92
+ }
93
+ catch {
94
+ return false;
95
+ }
96
+ }
97
+ }
98
+ //# sourceMappingURL=keychainProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychainProvider.js","sourceRoot":"","sources":["../../src/auth/keychainProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,YAAY,GAAG,YAAY,CAAC;AAClC,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAExC;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IACnB,MAAM,GAAmC,IAAI,CAAC;IAC9C,aAAa,GAAG,KAAK,CAAC;IAE9B;;OAEG;IACK,KAAK,CAAC,UAAU;QACtB,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAE1B,IAAI,CAAC;YACH,+DAA+D;YAC/D,IAAI,CAAC,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW;QACf,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;QAEtB,gEAAgE;QAChE,6CAA6C;QAC7C,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,WAAW,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;YAChD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,UAAkB;QAC5B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;QAEtB,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,WAAW,CAAC,YAAY,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;YAC7D,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAErB,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,WAAW,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM;QACV,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;QAEtB,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,cAAc,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAC7D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * ID Wispera Passphrase Provider
3
+ * Resolves the vault passphrase through a priority chain of strategies.
4
+ *
5
+ * Resolution order:
6
+ * 1. Session token — IDW_SESSION_TOKEN env var → sidecar decrypt
7
+ * 2. OS keychain — keytar (optional native module)
8
+ * 3. Env var / .env — IDW_PASSPHRASE from environment or .env file
9
+ * 4. Interactive — masked terminal prompt (CLI only)
10
+ *
11
+ * The user explicitly chooses their auth mode:
12
+ * - Passphrase mode (default): keychain, env var, or interactive prompt
13
+ * - Session token mode (opt-in): for CI / headless via `idw auth token create`
14
+ *
15
+ * If IDW_SESSION_TOKEN is set it takes precedence — the user has opted in.
16
+ * Otherwise we try the keychain, then env var / .env, then prompt.
17
+ */
18
+ export interface PassphraseProviderOptions {
19
+ /**
20
+ * Allow interactive terminal prompt (masked input).
21
+ * Set to `false` for headless environments (MCP server, CI).
22
+ * @default false
23
+ */
24
+ allowInteractive?: boolean;
25
+ /**
26
+ * Allow the IDW_PASSPHRASE env var (or .env file).
27
+ * When true, the provider checks the process environment and then
28
+ * .env files ($CWD/.env, ~/.id-wispera/.env) for IDW_PASSPHRASE.
29
+ * @default true
30
+ */
31
+ allowEnvVar?: boolean;
32
+ /**
33
+ * Custom prompt function for interactive mode.
34
+ * Receives a message string and must return the passphrase.
35
+ * If not provided, a default `inquirer` prompt is used.
36
+ */
37
+ promptFn?: (message: string) => Promise<string>;
38
+ /**
39
+ * Custom path for the session token sidecar file.
40
+ * Defaults to ~/.id-wispera/sessions.json
41
+ */
42
+ sidecarPath?: string;
43
+ }
44
+ export type PassphraseSource = 'session-token' | 'keychain' | 'env-var' | 'dotenv-file' | 'interactive';
45
+ export interface PassphraseResult {
46
+ passphrase: string;
47
+ source: PassphraseSource;
48
+ }
49
+ export declare class PassphraseProvider {
50
+ private keychain;
51
+ private tokenManager;
52
+ private options;
53
+ constructor(options?: PassphraseProviderOptions);
54
+ /**
55
+ * Resolve the vault passphrase by walking the strategy chain.
56
+ * @throws Error if no strategy succeeds.
57
+ */
58
+ resolve(): Promise<PassphraseResult>;
59
+ /**
60
+ * Convenience: resolve and return just the passphrase string.
61
+ */
62
+ getPassphrase(): Promise<string>;
63
+ /**
64
+ * Look for IDW_PASSPHRASE in the process environment first, then in .env
65
+ * files ($CWD/.env → ~/.id-wispera/.env). Only the IDW_PASSPHRASE key is
66
+ * read — no other variables are loaded.
67
+ */
68
+ private static resolveFromEnvOrDotenv;
69
+ /**
70
+ * Parse a .env file and return the value of IDW_PASSPHRASE, or null.
71
+ * Lightweight parser: splits on first `=`, ignores comments and blanks.
72
+ */
73
+ private static readPassphraseFromDotenv;
74
+ /**
75
+ * Warn to stderr if a .env file is world-readable.
76
+ */
77
+ private static warnIfWorldReadable;
78
+ private prompt;
79
+ }
80
+ //# sourceMappingURL=passphraseProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passphraseProvider.d.ts","sourceRoot":"","sources":["../../src/auth/passphraseProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA0BH,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAEhD;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,UAAU,GACV,SAAS,GACT,aAAa,GACb,aAAa,CAAC;AAElB,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAMD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,OAAO,CAEiD;gBAEpD,OAAO,GAAE,yBAA8B;IAWnD;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAkC1C;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAStC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAwBrC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;IA2BvC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;YAepB,MAAM;CAyBrB"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * ID Wispera Passphrase Provider
3
+ * Resolves the vault passphrase through a priority chain of strategies.
4
+ *
5
+ * Resolution order:
6
+ * 1. Session token — IDW_SESSION_TOKEN env var → sidecar decrypt
7
+ * 2. OS keychain — keytar (optional native module)
8
+ * 3. Env var / .env — IDW_PASSPHRASE from environment or .env file
9
+ * 4. Interactive — masked terminal prompt (CLI only)
10
+ *
11
+ * The user explicitly chooses their auth mode:
12
+ * - Passphrase mode (default): keychain, env var, or interactive prompt
13
+ * - Session token mode (opt-in): for CI / headless via `idw auth token create`
14
+ *
15
+ * If IDW_SESSION_TOKEN is set it takes precedence — the user has opted in.
16
+ * Otherwise we try the keychain, then env var / .env, then prompt.
17
+ */
18
+ import { readFileSync, statSync } from 'node:fs';
19
+ import { join } from 'node:path';
20
+ import { homedir } from 'node:os';
21
+ import { KeychainProvider } from './keychainProvider.js';
22
+ import { SessionTokenManager, } from './sessionTokenManager.js';
23
+ // ============================================================================
24
+ // Constants
25
+ // ============================================================================
26
+ const PASSPHRASE_ENV_VAR = 'IDW_PASSPHRASE';
27
+ const NO_PASSPHRASE_ERROR = 'No passphrase available. Options:\n' +
28
+ " - Run 'idw auth login' to store in OS keychain\n" +
29
+ " - Run 'idw auth token create' for headless / CI environments\n" +
30
+ ` - Set ${PASSPHRASE_ENV_VAR} in your environment or a .env file`;
31
+ // ============================================================================
32
+ // Provider
33
+ // ============================================================================
34
+ export class PassphraseProvider {
35
+ keychain;
36
+ tokenManager;
37
+ options;
38
+ constructor(options = {}) {
39
+ this.options = {
40
+ allowInteractive: options.allowInteractive ?? false,
41
+ allowEnvVar: options.allowEnvVar ?? true,
42
+ promptFn: options.promptFn,
43
+ sidecarPath: options.sidecarPath,
44
+ };
45
+ this.keychain = new KeychainProvider();
46
+ this.tokenManager = new SessionTokenManager(this.options.sidecarPath);
47
+ }
48
+ /**
49
+ * Resolve the vault passphrase by walking the strategy chain.
50
+ * @throws Error if no strategy succeeds.
51
+ */
52
+ async resolve() {
53
+ // ── 1. Session token ───────────────────────────────────────────────
54
+ const token = SessionTokenManager.getTokenFromEnv();
55
+ if (token) {
56
+ const passphrase = await this.tokenManager.resolve(token);
57
+ return { passphrase, source: 'session-token' };
58
+ }
59
+ // ── 2. OS keychain ─────────────────────────────────────────────────
60
+ const keychainPassphrase = await this.keychain.retrieve();
61
+ if (keychainPassphrase) {
62
+ return { passphrase: keychainPassphrase, source: 'keychain' };
63
+ }
64
+ // ── 3. IDW_PASSPHRASE env var or .env file ────────────────────────
65
+ if (this.options.allowEnvVar) {
66
+ const envResult = PassphraseProvider.resolveFromEnvOrDotenv();
67
+ if (envResult) {
68
+ return envResult;
69
+ }
70
+ }
71
+ // ── 4. Interactive prompt ──────────────────────────────────────────
72
+ if (this.options.allowInteractive) {
73
+ const passphrase = await this.prompt();
74
+ if (passphrase) {
75
+ return { passphrase, source: 'interactive' };
76
+ }
77
+ }
78
+ // ── Nothing worked ─────────────────────────────────────────────────
79
+ throw new Error(NO_PASSPHRASE_ERROR);
80
+ }
81
+ /**
82
+ * Convenience: resolve and return just the passphrase string.
83
+ */
84
+ async getPassphrase() {
85
+ const result = await this.resolve();
86
+ return result.passphrase;
87
+ }
88
+ // --------------------------------------------------------------------------
89
+ // Internal
90
+ // --------------------------------------------------------------------------
91
+ /**
92
+ * Look for IDW_PASSPHRASE in the process environment first, then in .env
93
+ * files ($CWD/.env → ~/.id-wispera/.env). Only the IDW_PASSPHRASE key is
94
+ * read — no other variables are loaded.
95
+ */
96
+ static resolveFromEnvOrDotenv() {
97
+ // Process environment takes precedence
98
+ const envVal = process.env[PASSPHRASE_ENV_VAR];
99
+ if (envVal) {
100
+ return { passphrase: envVal, source: 'env-var' };
101
+ }
102
+ // Try .env files: CWD first, then ~/.id-wispera/
103
+ const candidates = [
104
+ join(process.cwd(), '.env'),
105
+ join(homedir(), '.id-wispera', '.env'),
106
+ ];
107
+ for (const filePath of candidates) {
108
+ const passphrase = PassphraseProvider.readPassphraseFromDotenv(filePath);
109
+ if (passphrase) {
110
+ PassphraseProvider.warnIfWorldReadable(filePath);
111
+ return { passphrase, source: 'dotenv-file' };
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+ /**
117
+ * Parse a .env file and return the value of IDW_PASSPHRASE, or null.
118
+ * Lightweight parser: splits on first `=`, ignores comments and blanks.
119
+ */
120
+ static readPassphraseFromDotenv(filePath) {
121
+ try {
122
+ const content = readFileSync(filePath, 'utf-8');
123
+ for (const line of content.split('\n')) {
124
+ const trimmed = line.trim();
125
+ if (!trimmed || trimmed.startsWith('#'))
126
+ continue;
127
+ const eqIndex = trimmed.indexOf('=');
128
+ if (eqIndex === -1)
129
+ continue;
130
+ const key = trimmed.slice(0, eqIndex).trim();
131
+ if (key === PASSPHRASE_ENV_VAR) {
132
+ let value = trimmed.slice(eqIndex + 1).trim();
133
+ // Strip surrounding quotes if present
134
+ if ((value.startsWith('"') && value.endsWith('"')) ||
135
+ (value.startsWith("'") && value.endsWith("'"))) {
136
+ value = value.slice(1, -1);
137
+ }
138
+ return value || null;
139
+ }
140
+ }
141
+ }
142
+ catch {
143
+ // File doesn't exist or isn't readable — skip
144
+ }
145
+ return null;
146
+ }
147
+ /**
148
+ * Warn to stderr if a .env file is world-readable.
149
+ */
150
+ static warnIfWorldReadable(filePath) {
151
+ try {
152
+ const mode = statSync(filePath).mode;
153
+ // Check "other read" bit (0o004)
154
+ if (mode & 0o004) {
155
+ process.stderr.write(`[WARNING] ${filePath} is world-readable. ` +
156
+ 'Consider running: chmod 600 ' + filePath + '\n');
157
+ }
158
+ }
159
+ catch {
160
+ // stat failed — skip warning
161
+ }
162
+ }
163
+ async prompt() {
164
+ // User-supplied prompt function takes precedence
165
+ if (this.options.promptFn) {
166
+ return this.options.promptFn('Enter vault passphrase:');
167
+ }
168
+ // Fallback to inquirer (dynamic import — optional dep for non-CLI use)
169
+ try {
170
+ const inquirer = await import('inquirer');
171
+ const { passphrase } = await inquirer.default.prompt([
172
+ {
173
+ type: 'password',
174
+ name: 'passphrase',
175
+ message: 'Enter vault passphrase:',
176
+ mask: '*',
177
+ validate: (input) => input.length >= 1 || 'Passphrase is required',
178
+ },
179
+ ]);
180
+ return passphrase || null;
181
+ }
182
+ catch {
183
+ // inquirer not available — skip interactive
184
+ return null;
185
+ }
186
+ }
187
+ }
188
+ //# sourceMappingURL=passphraseProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passphraseProvider.js","sourceRoot":"","sources":["../../src/auth/passphraseProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * ID Wispera Session Token Manager
3
+ * Manages short-lived session tokens for headless / CI environments.
4
+ *
5
+ * Design:
6
+ * - Tokens live in a sidecar file (~/.id-wispera/sessions.json), NOT inside
7
+ * the encrypted vault (avoids the chicken-and-egg problem).
8
+ * - Each sidecar entry stores the vault passphrase encrypted with a key
9
+ * derived from the token itself (PBKDF2 + AES-256-GCM).
10
+ * - The token is the only runtime credential needed — set as
11
+ * IDW_SESSION_TOKEN env var.
12
+ * - If the sidecar is lost or corrupted the user still has their passphrase
13
+ * as the root recovery path.
14
+ *
15
+ * Token format:
16
+ * idw_token_ + 64 random bytes base64url-encoded (~90 chars total)
17
+ *
18
+ * Sidecar schema (cross-SDK compatible — TS, Python, Go all read/write this):
19
+ * {
20
+ * "version": 1,
21
+ * "entries": [
22
+ * {
23
+ * "tokenHash": "<SHA-256 hex>",
24
+ * "salt": "<32 bytes hex>",
25
+ * "iv": "<12 bytes hex>",
26
+ * "encryptedPassphrase": "<AES-256-GCM ciphertext, base64>",
27
+ * "createdAt": "<ISO 8601>",
28
+ * "expiresAt": "<ISO 8601 | null>",
29
+ * "label": "<user-provided label>"
30
+ * }
31
+ * ]
32
+ * }
33
+ */
34
+ declare const ENV_VAR = "IDW_SESSION_TOKEN";
35
+ export interface SidecarEntry {
36
+ tokenHash: string;
37
+ salt: string;
38
+ iv: string;
39
+ encryptedPassphrase: string;
40
+ createdAt: string;
41
+ expiresAt: string | null;
42
+ label: string;
43
+ }
44
+ export interface SidecarFile {
45
+ version: number;
46
+ entries: SidecarEntry[];
47
+ }
48
+ export interface TokenInfo {
49
+ tokenHash: string;
50
+ label: string;
51
+ createdAt: string;
52
+ expiresAt: string | null;
53
+ }
54
+ export declare class SessionTokenManager {
55
+ private sidecarPath;
56
+ constructor(sidecarPath?: string);
57
+ /** Read the sidecar file. Returns an empty structure if missing/corrupt. */
58
+ private readSidecar;
59
+ /** Write the sidecar file, creating parent directories as needed. */
60
+ private writeSidecar;
61
+ /**
62
+ * Generate a new CSPRNG token string.
63
+ */
64
+ generateToken(): string;
65
+ /**
66
+ * Create a session token entry. Encrypts the passphrase with a key
67
+ * derived from the token and stores the entry in the sidecar file.
68
+ *
69
+ * @returns The plaintext token (shown to the user once — never stored).
70
+ */
71
+ create(passphrase: string, options?: {
72
+ label?: string;
73
+ expiresInSeconds?: number;
74
+ }): Promise<string>;
75
+ /**
76
+ * Resolve a session token → vault passphrase.
77
+ *
78
+ * Looks up the sidecar entry by the token's SHA-256 hash, checks expiry,
79
+ * then decrypts the passphrase.
80
+ *
81
+ * @throws Error if the token is invalid, expired, or sidecar is missing.
82
+ */
83
+ resolve(token: string): Promise<string>;
84
+ /**
85
+ * Revoke a session token by its hash prefix (first 8+ hex chars).
86
+ * Returns `true` if an entry was removed.
87
+ */
88
+ revoke(hashPrefix: string): Promise<boolean>;
89
+ /**
90
+ * List all session tokens (metadata only — never returns secrets).
91
+ * Expired entries are included but marked.
92
+ */
93
+ list(): Promise<TokenInfo[]>;
94
+ /**
95
+ * Remove all expired entries from the sidecar.
96
+ * Returns the number of entries removed.
97
+ */
98
+ pruneExpired(): Promise<number>;
99
+ /**
100
+ * Read the token from the environment variable.
101
+ * Returns `null` if not set.
102
+ */
103
+ static getTokenFromEnv(): string | null;
104
+ }
105
+ export { ENV_VAR as SESSION_TOKEN_ENV_VAR };
106
+ //# sourceMappingURL=sessionTokenManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionTokenManager.d.ts","sourceRoot":"","sources":["../../src/auth/sessionTokenManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAcH,QAAA,MAAM,OAAO,sBAAsB,CAAC;AAMpC,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AA+GD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,CAAC,EAAE,MAAM;IAKhC,6EAA6E;YAC/D,WAAW;IAczB,qEAAqE;YACvD,YAAY;IAc1B;;OAEG;IACH,aAAa,IAAI,MAAM;IAKvB;;;;;OAKG;IACG,MAAM,CACV,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAO,GAC1D,OAAO,CAAC,MAAM,CAAC;IA6BlB;;;;;;;OAOG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuC7C;;;OAGG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWlD;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAUlC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAYrC;;;OAGG;IACH,MAAM,CAAC,eAAe,IAAI,MAAM,GAAG,IAAI;CAGxC;AAED,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,CAAC"}