narai-primitives 2.0.0 → 2.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 (141) hide show
  1. package/dist/config/index.d.ts +1 -0
  2. package/dist/config/index.d.ts.map +1 -1
  3. package/dist/config/index.js +1 -0
  4. package/dist/config/index.js.map +1 -1
  5. package/dist/config/policy_validation.d.ts +68 -0
  6. package/dist/config/policy_validation.d.ts.map +1 -0
  7. package/dist/config/policy_validation.js +58 -0
  8. package/dist/config/policy_validation.js.map +1 -0
  9. package/dist/config/types.d.ts +37 -19
  10. package/dist/config/types.d.ts.map +1 -1
  11. package/dist/connectors/aws/lib/aws_client.js +1 -1
  12. package/dist/connectors/aws/lib/aws_client.js.map +1 -1
  13. package/dist/connectors/confluence/index.js +1 -1
  14. package/dist/connectors/confluence/index.js.map +1 -1
  15. package/dist/connectors/confluence/lib/confluence_client.js +1 -1
  16. package/dist/connectors/confluence/lib/confluence_client.js.map +1 -1
  17. package/dist/connectors/db/connector.d.ts.map +1 -1
  18. package/dist/connectors/db/connector.js +4 -0
  19. package/dist/connectors/db/connector.js.map +1 -1
  20. package/dist/connectors/db/index.d.ts +1 -1
  21. package/dist/connectors/db/index.d.ts.map +1 -1
  22. package/dist/connectors/db/index.js +1 -1
  23. package/dist/connectors/db/index.js.map +1 -1
  24. package/dist/connectors/db/lib/connection.js +1 -1
  25. package/dist/connectors/db/lib/connection.js.map +1 -1
  26. package/dist/connectors/db/lib/credentials.d.ts +1 -1
  27. package/dist/connectors/db/lib/credentials.js +4 -4
  28. package/dist/connectors/db/lib/credentials.js.map +1 -1
  29. package/dist/connectors/db/lib/plugin_config.d.ts +28 -1
  30. package/dist/connectors/db/lib/plugin_config.d.ts.map +1 -1
  31. package/dist/connectors/db/lib/plugin_config.js +7 -1
  32. package/dist/connectors/db/lib/plugin_config.js.map +1 -1
  33. package/dist/connectors/db/lib/policy.d.ts +10 -2
  34. package/dist/connectors/db/lib/policy.d.ts.map +1 -1
  35. package/dist/connectors/db/lib/policy.js.map +1 -1
  36. package/dist/connectors/github/index.js +1 -1
  37. package/dist/connectors/github/index.js.map +1 -1
  38. package/dist/connectors/github/lib/github_client.js +1 -1
  39. package/dist/connectors/github/lib/github_client.js.map +1 -1
  40. package/dist/connectors/jira/index.js +1 -1
  41. package/dist/connectors/jira/index.js.map +1 -1
  42. package/dist/connectors/jira/lib/jira_client.js +1 -1
  43. package/dist/connectors/jira/lib/jira_client.js.map +1 -1
  44. package/dist/connectors/notion/index.js +1 -1
  45. package/dist/connectors/notion/index.js.map +1 -1
  46. package/dist/connectors/notion/lib/notion_client.js +1 -1
  47. package/dist/connectors/notion/lib/notion_client.js.map +1 -1
  48. package/dist/credentials/cloud_secrets.d.ts +57 -0
  49. package/dist/credentials/cloud_secrets.d.ts.map +1 -0
  50. package/dist/credentials/cloud_secrets.js +202 -0
  51. package/dist/credentials/cloud_secrets.js.map +1 -0
  52. package/dist/credentials/env_var.d.ts +24 -0
  53. package/dist/credentials/env_var.d.ts.map +1 -0
  54. package/dist/credentials/env_var.js +30 -0
  55. package/dist/credentials/env_var.js.map +1 -0
  56. package/dist/credentials/file.d.ts +73 -0
  57. package/dist/credentials/file.d.ts.map +1 -0
  58. package/dist/credentials/file.js +159 -0
  59. package/dist/credentials/file.js.map +1 -0
  60. package/dist/credentials/index.d.ts +114 -0
  61. package/dist/credentials/index.d.ts.map +1 -0
  62. package/dist/credentials/index.js +174 -0
  63. package/dist/credentials/index.js.map +1 -0
  64. package/dist/credentials/keychain.d.ts +31 -0
  65. package/dist/credentials/keychain.d.ts.map +1 -0
  66. package/dist/credentials/keychain.js +124 -0
  67. package/dist/credentials/keychain.js.map +1 -0
  68. package/dist/credentials/parse_ref.d.ts +57 -0
  69. package/dist/credentials/parse_ref.d.ts.map +1 -0
  70. package/dist/credentials/parse_ref.js +128 -0
  71. package/dist/credentials/parse_ref.js.map +1 -0
  72. package/dist/credentials/redact.d.ts +30 -0
  73. package/dist/credentials/redact.d.ts.map +1 -0
  74. package/dist/credentials/redact.js +51 -0
  75. package/dist/credentials/redact.js.map +1 -0
  76. package/dist/toolkit/connector.d.ts +11 -0
  77. package/dist/toolkit/connector.d.ts.map +1 -1
  78. package/dist/toolkit/connector.js.map +1 -1
  79. package/dist/toolkit/index.d.ts +1 -1
  80. package/dist/toolkit/index.js +1 -1
  81. package/dist/toolkit/policy/config.d.ts.map +1 -1
  82. package/dist/toolkit/policy/config.js +1 -2
  83. package/dist/toolkit/policy/config.js.map +1 -1
  84. package/dist/toolkit/policy/gate.d.ts +7 -5
  85. package/dist/toolkit/policy/gate.d.ts.map +1 -1
  86. package/dist/toolkit/policy/gate.js +2 -14
  87. package/dist/toolkit/policy/gate.js.map +1 -1
  88. package/dist/toolkit/policy/types.d.ts +30 -8
  89. package/dist/toolkit/policy/types.d.ts.map +1 -1
  90. package/dist/toolkit/policy/types.js +8 -2
  91. package/dist/toolkit/policy/types.js.map +1 -1
  92. package/package.json +21 -3
  93. package/plugins/aws-agent/.claude-plugin/plugin.json +2 -2
  94. package/plugins/aws-agent/README.md +2 -2
  95. package/plugins/aws-agent/bin/aws-agent +2 -2
  96. package/plugins/aws-agent/hooks/hooks.json +5 -5
  97. package/plugins/aws-agent/hooks/reminder.mjs +1 -1
  98. package/plugins/aws-agent/package.json +1 -1
  99. package/plugins/aws-agent/skills/aws-agent/SKILL.md +1 -1
  100. package/plugins/confluence-agent/.claude-plugin/plugin.json +2 -2
  101. package/plugins/confluence-agent/README.md +1 -1
  102. package/plugins/confluence-agent/bin/confluence-agent +1 -1
  103. package/plugins/confluence-agent/hooks/hooks.json +5 -5
  104. package/plugins/confluence-agent/hooks/reminder.mjs +1 -1
  105. package/plugins/confluence-agent/package.json +1 -1
  106. package/plugins/confluence-agent/skills/confluence-agent/SKILL.md +1 -1
  107. package/plugins/db-agent/.claude-plugin/plugin.json +4 -4
  108. package/plugins/db-agent/README.md +2 -2
  109. package/plugins/db-agent/bin/db-agent +1 -1
  110. package/plugins/db-agent/hooks/hooks.json +6 -6
  111. package/plugins/db-agent/hooks/reminder.mjs +1 -1
  112. package/plugins/db-agent/package.json +1 -1
  113. package/plugins/db-agent/skills/db-agent/SKILL.md +1 -1
  114. package/plugins/gcp-agent/.claude-plugin/plugin.json +2 -2
  115. package/plugins/gcp-agent/README.md +2 -2
  116. package/plugins/gcp-agent/bin/gcp-agent +2 -2
  117. package/plugins/gcp-agent/hooks/hooks.json +5 -5
  118. package/plugins/gcp-agent/hooks/reminder.mjs +1 -1
  119. package/plugins/gcp-agent/package.json +1 -1
  120. package/plugins/gcp-agent/skills/gcp-agent/SKILL.md +1 -1
  121. package/plugins/github-agent/.claude-plugin/plugin.json +2 -2
  122. package/plugins/github-agent/README.md +1 -1
  123. package/plugins/github-agent/bin/github-agent +1 -1
  124. package/plugins/github-agent/hooks/hooks.json +5 -5
  125. package/plugins/github-agent/hooks/reminder.mjs +1 -1
  126. package/plugins/github-agent/package.json +1 -1
  127. package/plugins/github-agent/skills/github-agent/SKILL.md +1 -1
  128. package/plugins/jira-agent/.claude-plugin/plugin.json +2 -2
  129. package/plugins/jira-agent/README.md +1 -1
  130. package/plugins/jira-agent/bin/jira-agent +1 -1
  131. package/plugins/jira-agent/hooks/hooks.json +5 -5
  132. package/plugins/jira-agent/hooks/reminder.mjs +1 -1
  133. package/plugins/jira-agent/package.json +1 -1
  134. package/plugins/jira-agent/skills/jira-agent/SKILL.md +1 -1
  135. package/plugins/notion-agent/.claude-plugin/plugin.json +2 -2
  136. package/plugins/notion-agent/README.md +2 -2
  137. package/plugins/notion-agent/bin/notion-agent +1 -1
  138. package/plugins/notion-agent/hooks/hooks.json +5 -5
  139. package/plugins/notion-agent/hooks/reminder.mjs +1 -1
  140. package/plugins/notion-agent/package.json +1 -1
  141. package/plugins/notion-agent/skills/notion-agent/SKILL.md +1 -1
@@ -0,0 +1,159 @@
1
+ /**
2
+ * file.ts — Plaintext secrets file credential provider.
3
+ *
4
+ * Reads a JSON file shaped like `{ "<secret-name>": "<value>", ... }`.
5
+ * Emits a single `console.warn` on first use to flag that secrets are
6
+ * stored in cleartext. Callers that need at-rest encryption should switch
7
+ * to the `cloud_secrets` or `keychain` provider.
8
+ *
9
+ * Future enhancement: support age/sops-encrypted files. For now the
10
+ * warning makes the trade-off explicit.
11
+ */
12
+ import * as fs from "node:fs";
13
+ export class FileProvider {
14
+ _path;
15
+ _suppressWarning;
16
+ _allowLoosePermissions;
17
+ _cacheTtlMs;
18
+ _warned = false;
19
+ _cache = null;
20
+ _cacheExpiresAt = null;
21
+ constructor(opts) {
22
+ this._path = opts.path;
23
+ this._suppressWarning = opts.suppressWarning ?? false;
24
+ this._allowLoosePermissions = opts.allowLoosePermissions ?? false;
25
+ this._cacheTtlMs = opts.cacheTtlMs ?? Infinity;
26
+ }
27
+ /**
28
+ * Discard the cached JSON so the next `getSecret` re-reads from disk.
29
+ * Useful when callers want to invalidate the cache on demand — e.g. after
30
+ * rotating the file — independent of the configured TTL.
31
+ */
32
+ clearCache() {
33
+ this._cache = null;
34
+ this._cacheExpiresAt = null;
35
+ }
36
+ /**
37
+ * Return the value for `name`. Two lookup strategies, in order:
38
+ * 1. Literal top-level key — preserves the original flat
39
+ * `{ "<name>": "<value>" }` contract and lets users have
40
+ * key names that contain dots.
41
+ * 2. Dot-path traversal — if the literal key doesn't resolve to a
42
+ * string, treat `name` as a path (e.g. `db-prod.username`) and
43
+ * walk the nested JSON. Returns null if any segment is missing
44
+ * or the leaf is not a string. Lets nested credential layouts
45
+ * reuse the same file/mode/warning machinery.
46
+ */
47
+ async getSecret(name) {
48
+ return this.getSecretSync(name);
49
+ }
50
+ getSecretSync(name) {
51
+ this._warnOnce();
52
+ const data = this._load();
53
+ if (data === null)
54
+ return null;
55
+ const literal = data[name];
56
+ if (typeof literal === "string")
57
+ return literal;
58
+ if (name.includes(".")) {
59
+ const parts = name.split(".");
60
+ let cur = data;
61
+ for (const part of parts) {
62
+ if (cur === null || typeof cur !== "object" || Array.isArray(cur)) {
63
+ return null;
64
+ }
65
+ cur = cur[part];
66
+ }
67
+ if (typeof cur === "string")
68
+ return cur;
69
+ }
70
+ return null;
71
+ }
72
+ async describeSecret(name) {
73
+ const value = this.getSecretSync(name);
74
+ let lastModified;
75
+ try {
76
+ lastModified = fs.statSync(this._path).mtime;
77
+ }
78
+ catch {
79
+ // file absent or unreadable — skip
80
+ }
81
+ const out = { exists: value !== null, provider: "file" };
82
+ if (lastModified !== undefined)
83
+ out.lastModified = lastModified;
84
+ return out;
85
+ }
86
+ _warnOnce() {
87
+ if (this._warned || this._suppressWarning)
88
+ return;
89
+ this._warned = true;
90
+ console.warn(`[credential_providers/file] reading secrets from plaintext file ${this._path}; ` +
91
+ "consider using the keychain or cloud_secrets provider instead.");
92
+ }
93
+ /**
94
+ * Refuse to read the file if its mode is group- or world-accessible
95
+ * on POSIX systems (matches the OpenSSH posture for private keys).
96
+ * Skipped on Windows where stat().mode does not carry Unix permission
97
+ * bits in a portable way.
98
+ */
99
+ _checkFileMode() {
100
+ if (process.platform === "win32")
101
+ return;
102
+ if (this._allowLoosePermissions)
103
+ return;
104
+ const st = fs.statSync(this._path);
105
+ const insecureBits = st.mode & 0o077;
106
+ if (insecureBits !== 0) {
107
+ const octal = (st.mode & 0o777).toString(8).padStart(3, "0");
108
+ throw new Error(`[credential_providers/file] refusing to read ${this._path}: ` +
109
+ `file mode ${octal} is group- or world-accessible. ` +
110
+ `Run 'chmod 600 ${this._path}' to restrict to the owner.`);
111
+ }
112
+ }
113
+ /**
114
+ * Parse the credentials JSON. Stores the raw nested structure so
115
+ * dot-path traversal can find values inside subobjects (e.g. a
116
+ * `db-<env>: {username, password}` layout); literal-key lookups still
117
+ * filter to strings in `getSecret`.
118
+ *
119
+ * Cache lifecycle: by default held forever (fast path). When `cacheTtlMs`
120
+ * is finite, the mode check and file read re-run after the TTL elapses —
121
+ * that way rotated files with new bad permissions still get caught.
122
+ */
123
+ _load() {
124
+ if (this._cache !== null) {
125
+ if (this._cacheTtlMs === Infinity)
126
+ return this._cache;
127
+ if (this._cacheExpiresAt !== null &&
128
+ Date.now() < this._cacheExpiresAt) {
129
+ return this._cache;
130
+ }
131
+ }
132
+ if (!fs.existsSync(this._path))
133
+ return null;
134
+ this._checkFileMode();
135
+ const raw = fs.readFileSync(this._path, "utf-8");
136
+ let parsed;
137
+ try {
138
+ parsed = JSON.parse(raw);
139
+ }
140
+ catch (err) {
141
+ this._cache = null;
142
+ this._cacheExpiresAt = null;
143
+ throw err;
144
+ }
145
+ if (parsed === null ||
146
+ typeof parsed !== "object" ||
147
+ Array.isArray(parsed)) {
148
+ this._cache = null;
149
+ this._cacheExpiresAt = null;
150
+ throw new Error(`file provider: ${this._path} did not contain a JSON object`);
151
+ }
152
+ this._cache = parsed;
153
+ if (this._cacheTtlMs !== Infinity) {
154
+ this._cacheExpiresAt = Date.now() + this._cacheTtlMs;
155
+ }
156
+ return this._cache;
157
+ }
158
+ }
159
+ //# sourceMappingURL=file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/credentials/file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ /** Minimal interface every secret backend satisfies. */
2
+ export interface CredentialProvider {
3
+ /** Look up a secret by logical name. Returns `null` on miss. */
4
+ getSecret(name: string): Promise<string | null>;
5
+ /**
6
+ * Optional synchronous lookup, for callers that cannot await (module-level
7
+ * config resolution, legacy synchronous dispatchers). Providers backed by
8
+ * sync-capable stores (`process.env`, `fs.readFileSync`) implement this;
9
+ * keychain and cloud providers do not because their backing APIs are
10
+ * async-only. Callers that need broad backend coverage should prefer
11
+ * `getSecret`.
12
+ */
13
+ getSecretSync?(name: string): string | null;
14
+ /**
15
+ * Optional metadata lookup. Default built-in providers fall back to
16
+ * calling getSecret and reporting `{exists, provider}` only. Cloud
17
+ * providers may override to surface real version / lastModified data.
18
+ */
19
+ describeSecret?(name: string): Promise<SecretMetadata | null>;
20
+ }
21
+ export interface SecretMetadata {
22
+ /** True when the secret exists in this backend. */
23
+ exists: boolean;
24
+ /** Backend-reported version identifier, if available. */
25
+ version?: string;
26
+ /** When the secret was last modified, if the backend tracks it. */
27
+ lastModified?: Date;
28
+ /** Which registered provider name produced this record. */
29
+ provider: string;
30
+ }
31
+ export interface ResolveSecretOptions {
32
+ /** Primary provider name. If unset, uses the first registered provider. */
33
+ provider?: string;
34
+ /**
35
+ * Ordered fallback provider names. Tried in order after the primary
36
+ * returns `null` or throws.
37
+ */
38
+ fallback?: string[];
39
+ }
40
+ export interface ResolveSecretsOptions {
41
+ /** When true, any null result (miss) causes the batch to reject. */
42
+ strict?: boolean;
43
+ }
44
+ /**
45
+ * A named registry of {@link CredentialProvider} instances plus the
46
+ * `resolveSecret` chain. Instantiate your own resolver for test isolation,
47
+ * multi-tenant setups, or any case where the module-level {@link defaultResolver}
48
+ * is too coarse.
49
+ */
50
+ export declare class CredentialResolver {
51
+ private readonly _registry;
52
+ /** Register a provider under a short name (`keychain`, `env_var`, …). */
53
+ register(name: string, provider: CredentialProvider): void;
54
+ /** Look up a provider previously registered via {@link register}. */
55
+ get(name: string): CredentialProvider | undefined;
56
+ /** Drop all registered providers. */
57
+ clear(): void;
58
+ /** Return the list of currently registered provider names. */
59
+ list(): string[];
60
+ /**
61
+ * Resolve a secret through a primary provider and optional fallback chain.
62
+ *
63
+ * Returns `null` if no provider produces a value while at least one
64
+ * provider runs to completion (hit or miss). If *every* provider throws,
65
+ * an `AggregateError` is raised whose `.errors` array preserves each
66
+ * original error in the order providers were tried.
67
+ */
68
+ resolveSecret(name: string, options?: ResolveSecretOptions): Promise<string | null>;
69
+ /**
70
+ * Resolve multiple credential references in parallel.
71
+ *
72
+ * Each entry in `specs` maps an alias to a reference string (e.g.
73
+ * `{db: "env:PGPASSWORD", token: "keychain:gh"}`). Every reference is
74
+ * parsed up front with `strict: true` so typos in config surface at
75
+ * batch time rather than silently returning null.
76
+ *
77
+ * Per-alias errors are collected and re-thrown as an `AggregateError`
78
+ * — individual errors are wrapped to include their alias name so the
79
+ * `.errors` array is self-describing. With `strict: true`, any null
80
+ * result (miss) also causes the batch to reject.
81
+ */
82
+ resolveSecrets(specs: Record<string, string>, options?: ResolveSecretsOptions): Promise<Record<string, string | null>>;
83
+ }
84
+ /**
85
+ * Built-in provider short names. Matches the reference-string aliases
86
+ * recognized by `parseCredentialRef` (`env` → `env_var`, `cloud` →
87
+ * `cloud_secrets`). Consumers can iterate or narrow against this literal;
88
+ * the tuple is frozen so runtime mutation is rejected in strict mode.
89
+ */
90
+ export declare const KNOWN_PROVIDERS: readonly ["env", "keychain", "file", "cloud"];
91
+ export type KnownProvider = (typeof KNOWN_PROVIDERS)[number];
92
+ /** Shared resolver used by the module-level delegators below. */
93
+ export declare const defaultResolver: CredentialResolver;
94
+ /** Register a provider on the {@link defaultResolver}. */
95
+ export declare const registerProvider: (name: string, provider: CredentialProvider) => void;
96
+ /** Look up a provider on the {@link defaultResolver}. */
97
+ export declare const getProvider: (name: string) => CredentialProvider | undefined;
98
+ /** Drop all providers from the {@link defaultResolver} (test helper). */
99
+ export declare const clearProviders: () => void;
100
+ /** List provider names registered on the {@link defaultResolver}. */
101
+ export declare const listProviders: () => string[];
102
+ /** Resolve a secret via the {@link defaultResolver}. */
103
+ export declare const resolveSecret: (name: string, options?: ResolveSecretOptions) => Promise<string | null>;
104
+ /** Resolve a batch of credential references via the {@link defaultResolver}. */
105
+ export declare const resolveSecrets: (specs: Record<string, string>, options?: ResolveSecretsOptions) => Promise<Record<string, string | null>>;
106
+ export { FileProvider } from "./file.js";
107
+ export { EnvVarProvider } from "./env_var.js";
108
+ export { KeychainProvider } from "./keychain.js";
109
+ export { CloudSecretsProvider } from "./cloud_secrets.js";
110
+ export type { CloudSecretsConfig, CloudSubProvider } from "./cloud_secrets.js";
111
+ export { parseCredentialRef } from "./parse_ref.js";
112
+ export type { CredentialRef, ParseCredentialRefOptions, } from "./parse_ref.js";
113
+ export { redact, redactAll } from "./redact.js";
114
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * credential_providers — pluggable secret-backend layer.
3
+ *
4
+ * Each provider implements {@link CredentialProvider}. A {@link CredentialResolver}
5
+ * instance keeps a named registry of providers and exposes `resolveSecret`,
6
+ * which chains providers in the given fallback order and returns the first
7
+ * non-null hit.
8
+ *
9
+ * A module-level {@link defaultResolver} plus thin delegators
10
+ * (`registerProvider`, `resolveSecret`, …) are provided so callers that only
11
+ * need a single global registry can keep their imports flat.
12
+ */
13
+ import { parseCredentialRef } from "./parse_ref.js";
14
+ /**
15
+ * A named registry of {@link CredentialProvider} instances plus the
16
+ * `resolveSecret` chain. Instantiate your own resolver for test isolation,
17
+ * multi-tenant setups, or any case where the module-level {@link defaultResolver}
18
+ * is too coarse.
19
+ */
20
+ export class CredentialResolver {
21
+ _registry = new Map();
22
+ /** Register a provider under a short name (`keychain`, `env_var`, …). */
23
+ register(name, provider) {
24
+ this._registry.set(name, provider);
25
+ }
26
+ /** Look up a provider previously registered via {@link register}. */
27
+ get(name) {
28
+ return this._registry.get(name);
29
+ }
30
+ /** Drop all registered providers. */
31
+ clear() {
32
+ this._registry.clear();
33
+ }
34
+ /** Return the list of currently registered provider names. */
35
+ list() {
36
+ return [...this._registry.keys()];
37
+ }
38
+ /**
39
+ * Resolve a secret through a primary provider and optional fallback chain.
40
+ *
41
+ * Returns `null` if no provider produces a value while at least one
42
+ * provider runs to completion (hit or miss). If *every* provider throws,
43
+ * an `AggregateError` is raised whose `.errors` array preserves each
44
+ * original error in the order providers were tried.
45
+ */
46
+ async resolveSecret(name, options = {}) {
47
+ const order = [];
48
+ if (options.provider)
49
+ order.push(options.provider);
50
+ if (options.fallback)
51
+ order.push(...options.fallback);
52
+ if (order.length === 0) {
53
+ // Default: iterate whatever is in the registry, insertion order.
54
+ order.push(...this._registry.keys());
55
+ }
56
+ const errors = [];
57
+ let anySuccess = false;
58
+ for (const providerName of order) {
59
+ const provider = this._registry.get(providerName);
60
+ if (!provider)
61
+ continue;
62
+ try {
63
+ const value = await provider.getSecret(name);
64
+ anySuccess = true;
65
+ if (value !== null)
66
+ return value;
67
+ }
68
+ catch (err) {
69
+ errors.push(err);
70
+ }
71
+ }
72
+ if (!anySuccess && errors.length > 0) {
73
+ throw new AggregateError(errors, `resolveSecret('${name}') failed: all ${errors.length} provider(s) threw`);
74
+ }
75
+ return null;
76
+ }
77
+ /**
78
+ * Resolve multiple credential references in parallel.
79
+ *
80
+ * Each entry in `specs` maps an alias to a reference string (e.g.
81
+ * `{db: "env:PGPASSWORD", token: "keychain:gh"}`). Every reference is
82
+ * parsed up front with `strict: true` so typos in config surface at
83
+ * batch time rather than silently returning null.
84
+ *
85
+ * Per-alias errors are collected and re-thrown as an `AggregateError`
86
+ * — individual errors are wrapped to include their alias name so the
87
+ * `.errors` array is self-describing. With `strict: true`, any null
88
+ * result (miss) also causes the batch to reject.
89
+ */
90
+ async resolveSecrets(specs, options = {}) {
91
+ const aliases = Object.keys(specs);
92
+ const parsed = aliases.map((alias) => ({
93
+ alias,
94
+ ref: parseCredentialRef(specs[alias], {
95
+ strict: true,
96
+ resolver: this,
97
+ }),
98
+ }));
99
+ const settled = await Promise.allSettled(parsed.map(({ ref }) => {
100
+ if (ref === null)
101
+ return Promise.resolve(null);
102
+ return this.resolveSecret(ref.key, { provider: ref.provider });
103
+ }));
104
+ const errors = [];
105
+ const failedAliases = [];
106
+ const out = {};
107
+ for (let i = 0; i < aliases.length; i++) {
108
+ const alias = aliases[i];
109
+ const result = settled[i];
110
+ if (result === undefined)
111
+ continue;
112
+ if (result.status === "rejected") {
113
+ const original = result.reason;
114
+ const origMsg = original instanceof Error
115
+ ? original.message
116
+ : String(original);
117
+ const wrapped = new Error(`resolveSecrets: alias "${alias}" failed: ${origMsg}`);
118
+ errors.push(wrapped);
119
+ failedAliases.push(alias);
120
+ }
121
+ else {
122
+ out[alias] = result.value;
123
+ }
124
+ }
125
+ if (errors.length > 0) {
126
+ throw new AggregateError(errors, `resolveSecrets failed for: ${failedAliases.join(", ")}`);
127
+ }
128
+ if (options.strict) {
129
+ const missingAliases = aliases.filter((a) => out[a] === null);
130
+ if (missingAliases.length > 0) {
131
+ throw new Error(`resolveSecrets strict: aliases returned null: ${missingAliases.join(", ")}`);
132
+ }
133
+ }
134
+ return out;
135
+ }
136
+ }
137
+ /**
138
+ * Built-in provider short names. Matches the reference-string aliases
139
+ * recognized by `parseCredentialRef` (`env` → `env_var`, `cloud` →
140
+ * `cloud_secrets`). Consumers can iterate or narrow against this literal;
141
+ * the tuple is frozen so runtime mutation is rejected in strict mode.
142
+ */
143
+ export const KNOWN_PROVIDERS = Object.freeze([
144
+ "env",
145
+ "keychain",
146
+ "file",
147
+ "cloud",
148
+ ]);
149
+ /** Shared resolver used by the module-level delegators below. */
150
+ export const defaultResolver = new CredentialResolver();
151
+ /** Register a provider on the {@link defaultResolver}. */
152
+ export const registerProvider = defaultResolver.register.bind(defaultResolver);
153
+ /** Look up a provider on the {@link defaultResolver}. */
154
+ export const getProvider = defaultResolver.get.bind(defaultResolver);
155
+ /** Drop all providers from the {@link defaultResolver} (test helper). */
156
+ export const clearProviders = defaultResolver.clear.bind(defaultResolver);
157
+ /** List provider names registered on the {@link defaultResolver}. */
158
+ export const listProviders = defaultResolver.list.bind(defaultResolver);
159
+ /** Resolve a secret via the {@link defaultResolver}. */
160
+ export const resolveSecret = defaultResolver.resolveSecret.bind(defaultResolver);
161
+ /** Resolve a batch of credential references via the {@link defaultResolver}. */
162
+ export const resolveSecrets = defaultResolver.resolveSecrets.bind(defaultResolver);
163
+ // Re-export provider implementations so callers can import everything from
164
+ // `narai-primitives/credentials` directly.
165
+ export { FileProvider } from "./file.js";
166
+ export { EnvVarProvider } from "./env_var.js";
167
+ export { KeychainProvider } from "./keychain.js";
168
+ export { CloudSecretsProvider } from "./cloud_secrets.js";
169
+ // Reference-string grammar. Config consumers call `parseCredentialRef` on
170
+ // each string value to decide whether it's a literal or a reference into
171
+ // one of the registered providers. See `parse_ref.ts` for the grammar.
172
+ export { parseCredentialRef } from "./parse_ref.js";
173
+ export { redact, redactAll } from "./redact.js";
174
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { CredentialProvider, SecretMetadata } from "./index.js";
2
+ export interface KeychainProviderOptions {
3
+ /**
4
+ * Override the platform detection (for tests). Accepts any Node
5
+ * `process.platform` value.
6
+ */
7
+ platform?: NodeJS.Platform;
8
+ /**
9
+ * macOS Keychain "account" field (optional). If given, passed as
10
+ * `-a <account>` to `security find-generic-password`.
11
+ */
12
+ account?: string;
13
+ /**
14
+ * macOS Keychain "service" prefix. If set, the lookup name is
15
+ * `<servicePrefix>.<name>` so an application's secrets can be grouped
16
+ * under a single service name (e.g. `com.example.myapp`).
17
+ */
18
+ servicePrefix?: string;
19
+ }
20
+ export declare class KeychainProvider implements CredentialProvider {
21
+ private readonly _platform;
22
+ private readonly _account;
23
+ private readonly _servicePrefix;
24
+ constructor(opts?: KeychainProviderOptions);
25
+ getSecret(name: string): Promise<string | null>;
26
+ describeSecret(name: string): Promise<SecretMetadata>;
27
+ private _macos;
28
+ private _linux;
29
+ private _windows;
30
+ }
31
+ //# sourceMappingURL=keychain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.d.ts","sourceRoot":"","sources":["../../src/credentials/keychain.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,gBAAiB,YAAW,kBAAkB;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkB;IAC5C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,IAAI,GAAE,uBAA4B;IAMxC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmB/C,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAK3D,OAAO,CAAC,MAAM;IAoBd,OAAO,CAAC,MAAM;YAoBA,QAAQ;CAqBvB"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * keychain.ts — OS-native keychain provider.
3
+ *
4
+ * Backends (selected by platform):
5
+ * - darwin → `security find-generic-password -s "<name>" -w`
6
+ * - linux → `secret-tool lookup name "<name>"` (libsecret).
7
+ * If `secret-tool` is missing, throws a clear error.
8
+ * - win32 → `@napi-rs/keyring` (Windows Credential Manager via N-API).
9
+ * Lazy-imported; if the module is not installed, throws a
10
+ * clear error with the install command.
11
+ */
12
+ import { execFileSync } from "node:child_process";
13
+ export class KeychainProvider {
14
+ _platform;
15
+ _account;
16
+ _servicePrefix;
17
+ constructor(opts = {}) {
18
+ this._platform = opts.platform ?? process.platform;
19
+ this._account = opts.account;
20
+ this._servicePrefix = opts.servicePrefix ?? "";
21
+ }
22
+ async getSecret(name) {
23
+ const service = this._servicePrefix
24
+ ? `${this._servicePrefix}.${name}`
25
+ : name;
26
+ switch (this._platform) {
27
+ case "darwin":
28
+ return this._macos(service);
29
+ case "linux":
30
+ return this._linux(service);
31
+ case "win32":
32
+ return this._windows(service);
33
+ default:
34
+ throw new Error(`keychain provider unsupported on platform '${this._platform}'`);
35
+ }
36
+ }
37
+ async describeSecret(name) {
38
+ const value = await this.getSecret(name);
39
+ return { exists: value !== null, provider: "keychain" };
40
+ }
41
+ _macos(service) {
42
+ const args = ["find-generic-password", "-s", service, "-w"];
43
+ if (this._account) {
44
+ args.splice(1, 0, "-a", this._account);
45
+ }
46
+ try {
47
+ const out = execFileSync("security", args, {
48
+ encoding: "utf-8",
49
+ stdio: ["ignore", "pipe", "pipe"],
50
+ });
51
+ const trimmed = out.replace(/\n$/, "");
52
+ return trimmed === "" ? null : trimmed;
53
+ }
54
+ catch (err) {
55
+ // `security` exits non-zero if the item is not found. Surface a
56
+ // null-miss rather than an error so the fallback chain can proceed.
57
+ if (_isMissingKeychainItem(err))
58
+ return null;
59
+ throw _wrapKeychainError(err, "security");
60
+ }
61
+ }
62
+ _linux(service) {
63
+ try {
64
+ const out = execFileSync("secret-tool", ["lookup", "name", service], {
65
+ encoding: "utf-8",
66
+ stdio: ["ignore", "pipe", "pipe"],
67
+ });
68
+ const trimmed = out.replace(/\n$/, "");
69
+ return trimmed === "" ? null : trimmed;
70
+ }
71
+ catch (err) {
72
+ if (_isCommandNotFound(err)) {
73
+ throw new Error("keychain provider on Linux requires `secret-tool` (libsecret). " +
74
+ "Install with `apt install libsecret-tools` or equivalent.");
75
+ }
76
+ if (_isMissingKeychainItem(err))
77
+ return null;
78
+ throw _wrapKeychainError(err, "secret-tool");
79
+ }
80
+ }
81
+ async _windows(service) {
82
+ const mod = await _loadOptional("@napi-rs/keyring", "npm install --save-dev @napi-rs/keyring");
83
+ const { Entry } = mod;
84
+ const entry = new Entry(service, this._account ?? "default");
85
+ try {
86
+ const pw = entry.getPassword();
87
+ return pw === "" || pw === null ? null : pw;
88
+ }
89
+ catch (err) {
90
+ const msg = err.message ?? "";
91
+ if (/not (found|exist)|no.*entry/i.test(msg))
92
+ return null;
93
+ throw new Error(`keychain provider on Windows: ${msg}`);
94
+ }
95
+ }
96
+ }
97
+ function _isMissingKeychainItem(err) {
98
+ const e = err;
99
+ // macOS `security` returns 44 for "item not found"; libsecret's
100
+ // `secret-tool lookup` returns 1 with empty stdout when missing.
101
+ return e.status === 44 || e.status === 1;
102
+ }
103
+ function _isCommandNotFound(err) {
104
+ const e = err;
105
+ return e.code === "ENOENT";
106
+ }
107
+ function _wrapKeychainError(err, command) {
108
+ const e = err;
109
+ const stderr = e.stderr instanceof Buffer ? e.stderr.toString("utf-8") : e.stderr ?? "";
110
+ return new Error(`keychain provider: ${command} failed (status=${e.status}): ${stderr || e.message}`);
111
+ }
112
+ async function _loadOptional(pkg, install) {
113
+ try {
114
+ return (await import(pkg));
115
+ }
116
+ catch (err) {
117
+ const e = err;
118
+ if (e.code === "ERR_MODULE_NOT_FOUND" || e.code === "MODULE_NOT_FOUND") {
119
+ throw new Error(`keychain provider on Windows requires '${pkg}'. Run: ${install}`);
120
+ }
121
+ throw err;
122
+ }
123
+ }
124
+ //# sourceMappingURL=keychain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/credentials/keychain.ts"],"names":[],"mappings":"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"}