@executor-js/plugin-keychain 1.4.33 → 1.5.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.
- package/dist/{chunk-Y2ES5ICS.js → chunk-Y62YAXEQ.js} +28 -38
- package/dist/chunk-Y62YAXEQ.js.map +1 -0
- package/dist/core.js +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/promise.d.ts +2 -2
- package/dist/provider.d.ts +2 -3
- package/package.json +7 -4
- package/dist/chunk-Y2ES5ICS.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { Effect as Effect3 } from "effect";
|
|
3
|
-
import { definePlugin } from "@executor-js/sdk
|
|
3
|
+
import { definePlugin } from "@executor-js/sdk";
|
|
4
4
|
|
|
5
5
|
// src/keyring.ts
|
|
6
6
|
import { createRequire } from "module";
|
|
@@ -81,66 +81,56 @@ var deletePassword = (serviceName, account) => Effect.flatMap(
|
|
|
81
81
|
|
|
82
82
|
// src/provider.ts
|
|
83
83
|
import { Effect as Effect2 } from "effect";
|
|
84
|
-
import {
|
|
84
|
+
import {
|
|
85
|
+
StorageError,
|
|
86
|
+
ProviderKey
|
|
87
|
+
} from "@executor-js/sdk";
|
|
85
88
|
var toStorageError = (cause) => {
|
|
86
89
|
const { cause: underlyingCause } = cause;
|
|
87
90
|
return new StorageError({ message: cause.message, cause: underlyingCause ?? cause });
|
|
88
91
|
};
|
|
89
|
-
var
|
|
90
|
-
var makeKeychainProvider = (
|
|
91
|
-
key:
|
|
92
|
+
var KEYCHAIN_PROVIDER_KEY = ProviderKey.make("keychain");
|
|
93
|
+
var makeKeychainProvider = (serviceName) => ({
|
|
94
|
+
key: KEYCHAIN_PROVIDER_KEY,
|
|
92
95
|
writable: true,
|
|
93
|
-
get: (
|
|
96
|
+
get: (id) => getPassword(serviceName, id).pipe(Effect2.mapError(toStorageError)),
|
|
97
|
+
has: (id) => getPassword(serviceName, id).pipe(
|
|
98
|
+
Effect2.map((value) => value !== null),
|
|
94
99
|
Effect2.mapError(toStorageError)
|
|
95
100
|
),
|
|
96
|
-
set: (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
delete: (secretId, scope) => deletePassword(scopedKeychainServiceName(baseServiceName, scope), secretId).pipe(
|
|
100
|
-
Effect2.mapError(toStorageError)
|
|
101
|
-
),
|
|
102
|
-
// Keychain doesn't support enumerating — you need to know the account name
|
|
101
|
+
set: (id, value) => setPassword(serviceName, id, value).pipe(Effect2.mapError(toStorageError)),
|
|
102
|
+
delete: (id) => deletePassword(serviceName, id).pipe(Effect2.asVoid, Effect2.mapError(toStorageError)),
|
|
103
|
+
// Keychain doesn't support enumerating — you need to know the account name.
|
|
103
104
|
list: void 0
|
|
104
105
|
});
|
|
105
106
|
|
|
106
107
|
// src/index.ts
|
|
107
108
|
var PROBE_VALUE = "probe";
|
|
108
109
|
var probeAccount = () => `__executor_keychain_probe__:${process.pid}:${Date.now()}:${Math.random().toString(36).slice(2)}`;
|
|
109
|
-
var makeKeychainExtension = (
|
|
110
|
-
const
|
|
110
|
+
var makeKeychainExtension = (_ctx, options) => {
|
|
111
|
+
const serviceName = resolveServiceName(options?.serviceName);
|
|
111
112
|
return {
|
|
112
113
|
/** Human-readable name for the keychain on this platform */
|
|
113
114
|
displayName: displayName(),
|
|
114
115
|
/** Whether the current platform supports system keychain */
|
|
115
116
|
isSupported: isSupportedPlatform(),
|
|
116
|
-
/** Check if a secret exists in the system keychain
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
).pipe(
|
|
123
|
-
Effect3.map((v) => v !== null),
|
|
124
|
-
Effect3.orElseSucceed(() => false)
|
|
125
|
-
);
|
|
126
|
-
if (exists) return true;
|
|
127
|
-
}
|
|
128
|
-
return false;
|
|
129
|
-
})
|
|
117
|
+
/** Check if a secret exists in the system keychain. `id` is the opaque
|
|
118
|
+
* provider item id (the keychain account); v2 has no scope partitioning. */
|
|
119
|
+
has: (id) => getPassword(serviceName, id).pipe(
|
|
120
|
+
Effect3.map((value) => value !== null),
|
|
121
|
+
Effect3.orElseSucceed(() => false)
|
|
122
|
+
)
|
|
130
123
|
};
|
|
131
124
|
};
|
|
132
125
|
var keychainPlugin = definePlugin((options) => ({
|
|
133
126
|
id: "keychain",
|
|
134
127
|
storage: () => ({}),
|
|
135
128
|
extension: (ctx) => makeKeychainExtension(ctx, options),
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
const probeServiceName = scopedKeychainServiceName(baseServiceName, ctx.scopes[0].id);
|
|
129
|
+
credentialProviders: () => Effect3.gen(function* () {
|
|
130
|
+
const serviceName = resolveServiceName(options?.serviceName);
|
|
139
131
|
const account = probeAccount();
|
|
140
|
-
const reachable = yield* setPassword(
|
|
141
|
-
Effect3.andThen(
|
|
142
|
-
deletePassword(probeServiceName, account).pipe(Effect3.catch(() => Effect3.void))
|
|
143
|
-
),
|
|
132
|
+
const reachable = yield* setPassword(serviceName, account, PROBE_VALUE).pipe(
|
|
133
|
+
Effect3.andThen(deletePassword(serviceName, account).pipe(Effect3.catch(() => Effect3.void))),
|
|
144
134
|
Effect3.as(true),
|
|
145
135
|
Effect3.catch(
|
|
146
136
|
() => Effect3.logWarning("keychain unavailable, skipping provider registration").pipe(
|
|
@@ -148,7 +138,7 @@ var keychainPlugin = definePlugin((options) => ({
|
|
|
148
138
|
)
|
|
149
139
|
)
|
|
150
140
|
);
|
|
151
|
-
return reachable ? [makeKeychainProvider(
|
|
141
|
+
return reachable ? [makeKeychainProvider(serviceName)] : [];
|
|
152
142
|
})
|
|
153
143
|
}));
|
|
154
144
|
|
|
@@ -159,4 +149,4 @@ export {
|
|
|
159
149
|
makeKeychainProvider,
|
|
160
150
|
keychainPlugin
|
|
161
151
|
};
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
152
|
+
//# sourceMappingURL=chunk-Y62YAXEQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/keyring.ts","../src/errors.ts","../src/provider.ts"],"sourcesContent":["import { Effect } from \"effect\";\n\nimport { definePlugin, type CredentialProvider, type PluginCtx } from \"@executor-js/sdk\";\n\nimport {\n deletePassword,\n displayName,\n getPassword,\n isSupportedPlatform,\n resolveServiceName,\n setPassword,\n} from \"./keyring\";\nimport { makeKeychainProvider } from \"./provider\";\n\n// Probe the keychain by writing and then deleting a sentinel entry. A\n// read-only probe isn't enough — on some Linux environments (WSL2,\n// headless CI) `getPassword` for a missing key returns null without\n// error, but `setPassword` fails because the secret-service backend\n// isn't actually reachable. Writing is the capability the executor\n// cares about, so test it directly.\nconst PROBE_VALUE = \"probe\";\nconst probeAccount = (): string =>\n `__executor_keychain_probe__:${process.pid}:${Date.now()}:${Math.random().toString(36).slice(2)}`;\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport { KeychainError } from \"./errors\";\nexport { makeKeychainProvider } from \"./provider\";\nexport { isSupportedPlatform, displayName } from \"./keyring\";\n\n// ---------------------------------------------------------------------------\n// Plugin config\n// ---------------------------------------------------------------------------\n\nexport interface KeychainPluginConfig {\n /** Override the keychain service name (default: \"executor\") */\n readonly serviceName?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Plugin extension — public API on executor.keychain\n// ---------------------------------------------------------------------------\n\nexport type KeychainExtension = ReturnType<typeof makeKeychainExtension>;\n\n// ---------------------------------------------------------------------------\n// Plugin definition\n// ---------------------------------------------------------------------------\n\nconst makeKeychainExtension = (\n _ctx: PluginCtx<unknown>,\n options: KeychainPluginConfig | undefined,\n) => {\n const serviceName = resolveServiceName(options?.serviceName);\n return {\n /** Human-readable name for the keychain on this platform */\n displayName: displayName(),\n\n /** Whether the current platform supports system keychain */\n isSupported: isSupportedPlatform(),\n\n /** Check if a secret exists in the system keychain. `id` is the opaque\n * provider item id (the keychain account); v2 has no scope partitioning. */\n has: (id: string) =>\n getPassword(serviceName, id).pipe(\n Effect.map((value: string | null) => value !== null),\n Effect.orElseSucceed(() => false),\n ),\n };\n};\n\nexport const keychainPlugin = definePlugin((options?: KeychainPluginConfig) => ({\n id: \"keychain\" as const,\n storage: () => ({}),\n\n extension: (ctx: PluginCtx<unknown>): KeychainExtension => makeKeychainExtension(ctx, options),\n\n credentialProviders: (): Effect.Effect<readonly CredentialProvider[]> =>\n Effect.gen(function* () {\n const serviceName = resolveServiceName(options?.serviceName);\n const account = probeAccount();\n const reachable = yield* setPassword(serviceName, account, PROBE_VALUE).pipe(\n Effect.andThen(deletePassword(serviceName, account).pipe(Effect.catch(() => Effect.void))),\n Effect.as(true),\n Effect.catch(() =>\n Effect.logWarning(\"keychain unavailable, skipping provider registration\").pipe(\n Effect.as(false),\n ),\n ),\n );\n return reachable ? [makeKeychainProvider(serviceName)] : [];\n }),\n}));\n","import { createRequire } from \"node:module\";\n\nimport { Effect } from \"effect\";\n\nimport { KeychainError } from \"./errors\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_SERVICE_NAME = \"executor\";\nconst SERVICE_NAME_ENV = \"EXECUTOR_KEYCHAIN_SERVICE_NAME\";\n\n// ---------------------------------------------------------------------------\n// Platform helpers\n// ---------------------------------------------------------------------------\n\nexport const isSupportedPlatform = () =>\n process.platform === \"darwin\" || process.platform === \"linux\" || process.platform === \"win32\";\n\nexport const displayName = () =>\n process.platform === \"darwin\"\n ? \"macOS Keychain\"\n : process.platform === \"win32\"\n ? \"Windows Credential Manager\"\n : \"Desktop Keyring\";\n\nexport const resolveServiceName = (explicit?: string): string =>\n explicit?.trim() || process.env[SERVICE_NAME_ENV]?.trim() || DEFAULT_SERVICE_NAME;\n\n// ---------------------------------------------------------------------------\n// Lazy-load @napi-rs/keyring (native module)\n// ---------------------------------------------------------------------------\n\ntype EntryConstructor = (typeof import(\"@napi-rs/keyring\"))[\"Entry\"];\n\nlet entryCtorPromise: Promise<EntryConstructor> | null = null;\n\n// In compiled bun binaries (`bun build --compile`) `.node` modules aren't\n// included in bunfs and there's no node_modules at runtime, so\n// @napi-rs/keyring's loader can't find its platform-specific binding.\n// `apps/cli/src/build.ts` copies the .node next to the executor and\n// `apps/cli/src/main.ts` exports its absolute path here. We load it\n// directly because @napi-rs/keyring@1.2.0's NAPI_RS_NATIVE_LIBRARY_PATH\n// branch is buggy (assigns to a local that gets overwritten before return).\nconst loadEntryCtor = async (): Promise<EntryConstructor> => {\n const directPath = process.env.EXECUTOR_KEYRING_NATIVE_PATH;\n if (directPath) {\n const req = createRequire(import.meta.url);\n return (req(directPath) as { Entry: EntryConstructor }).Entry;\n }\n const { Entry } = await import(\"@napi-rs/keyring\");\n return Entry;\n};\n\nconst loadEntry = (): Effect.Effect<EntryConstructor, KeychainError> =>\n isSupportedPlatform()\n ? Effect.tryPromise({\n try: async () => {\n entryCtorPromise ??= loadEntryCtor();\n return await entryCtorPromise;\n },\n catch: (cause) =>\n new KeychainError({\n message: \"Failed loading native keyring\",\n cause,\n }),\n })\n : Effect.fail(\n new KeychainError({\n message: `Failed loading native keyring: unsupported platform '${process.platform}'`,\n }),\n );\n\nconst createEntry = (serviceName: string, account: string) =>\n Effect.flatMap(loadEntry(), (Entry) =>\n Effect.try({\n try: () => new Entry(serviceName, account),\n catch: (cause) =>\n new KeychainError({\n message: \"Failed creating keyring entry\",\n cause,\n }),\n }),\n );\n\n// ---------------------------------------------------------------------------\n// Low-level keychain operations\n// ---------------------------------------------------------------------------\n\nexport const getPassword = (\n serviceName: string,\n account: string,\n): Effect.Effect<string | null, KeychainError> =>\n Effect.flatMap(createEntry(serviceName, account), (entry) =>\n Effect.try({\n try: () => entry.getPassword(),\n catch: () => new KeychainError({ message: `Failed reading secret for account '${account}'` }),\n }),\n );\n\nexport const setPassword = (\n serviceName: string,\n account: string,\n value: string,\n): Effect.Effect<void, KeychainError> =>\n Effect.flatMap(createEntry(serviceName, account), (entry) =>\n Effect.try({\n try: () => entry.setPassword(value),\n catch: (cause) =>\n new KeychainError({\n message: \"Failed writing secret\",\n cause,\n }),\n }).pipe(Effect.asVoid),\n );\n\nexport const deletePassword = (\n serviceName: string,\n account: string,\n): Effect.Effect<boolean, KeychainError> =>\n Effect.flatMap(createEntry(serviceName, account), (entry) =>\n Effect.try({\n try: () => {\n entry.deletePassword();\n return true;\n },\n catch: () =>\n new KeychainError({ message: `Failed deleting secret for account '${account}'` }),\n }),\n );\n","import { Data } from \"effect\";\n\nexport class KeychainError extends Data.TaggedError(\"KeychainError\")<{\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n","import { Effect } from \"effect\";\n\nimport {\n StorageError,\n ProviderKey,\n type CredentialProvider,\n type ProviderItemId,\n} from \"@executor-js/sdk\";\n\nimport type { KeychainError } from \"./errors\";\nimport { getPassword, setPassword, deletePassword } from \"./keyring\";\n\n// ---------------------------------------------------------------------------\n// CredentialProvider adapter — bridges keyring into the v2 resolution chain.\n//\n// The underlying `@napi-rs/keyring` sync API encodes \"no entry\" as an\n// ordinary return value (`getPassword()` → `null`, `deletePassword()` →\n// `false`), and only throws on real failures (keychain locked, permission\n// denied, platform init failure, etc.). `keyring.ts` wraps those thrown\n// failures as `KeychainError`. We translate `KeychainError` →\n// `StorageError` so the HTTP edge can capture it to telemetry and surface\n// an opaque `InternalError({ traceId })` — previously `orElseSucceed`\n// silently converted every failure into \"nothing found\", which made it\n// impossible to debug why secrets weren't resolving.\n//\n// v2: the provider sees only an opaque `ProviderItemId` (the keychain\n// account). There is NO scope arg — the connection row owns the (tenant,\n// owner, subject) partition. We use a single, flat keychain service name;\n// the connection's opaque id is the account that uniquely keys the entry.\n// ---------------------------------------------------------------------------\n\nconst toStorageError = (cause: KeychainError) => {\n const { cause: underlyingCause } = cause;\n // oxlint-disable-next-line executor/no-unknown-error-message -- boundary: typed KeychainError message becomes StorageError message\n return new StorageError({ message: cause.message, cause: underlyingCause ?? cause });\n};\n\nconst KEYCHAIN_PROVIDER_KEY = ProviderKey.make(\"keychain\");\n\nexport const makeKeychainProvider = (serviceName: string): CredentialProvider => ({\n key: KEYCHAIN_PROVIDER_KEY,\n writable: true,\n get: (id: ProviderItemId) => getPassword(serviceName, id).pipe(Effect.mapError(toStorageError)),\n has: (id: ProviderItemId) =>\n getPassword(serviceName, id).pipe(\n Effect.map((value: string | null) => value !== null),\n Effect.mapError(toStorageError),\n ),\n set: (id: ProviderItemId, value: string) =>\n setPassword(serviceName, id, value).pipe(Effect.mapError(toStorageError)),\n delete: (id: ProviderItemId) =>\n deletePassword(serviceName, id).pipe(Effect.asVoid, Effect.mapError(toStorageError)),\n // Keychain doesn't support enumerating — you need to know the account name.\n list: undefined,\n});\n"],"mappings":"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","names":["Effect","Effect","Effect","Effect"]}
|
package/dist/core.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { type PluginCtx } from "@executor-js/sdk
|
|
2
|
+
import { type PluginCtx } from "@executor-js/sdk";
|
|
3
3
|
export { KeychainError } from "./errors";
|
|
4
4
|
export { makeKeychainProvider } from "./provider";
|
|
5
5
|
export { isSupportedPlatform, displayName } from "./keyring";
|
|
@@ -8,19 +8,21 @@ export interface KeychainPluginConfig {
|
|
|
8
8
|
readonly serviceName?: string;
|
|
9
9
|
}
|
|
10
10
|
export type KeychainExtension = ReturnType<typeof makeKeychainExtension>;
|
|
11
|
-
declare const makeKeychainExtension: (
|
|
11
|
+
declare const makeKeychainExtension: (_ctx: PluginCtx<unknown>, options: KeychainPluginConfig | undefined) => {
|
|
12
12
|
/** Human-readable name for the keychain on this platform */
|
|
13
13
|
displayName: string;
|
|
14
14
|
/** Whether the current platform supports system keychain */
|
|
15
15
|
isSupported: boolean;
|
|
16
|
-
/** Check if a secret exists in the system keychain
|
|
16
|
+
/** Check if a secret exists in the system keychain. `id` is the opaque
|
|
17
|
+
* provider item id (the keychain account); v2 has no scope partitioning. */
|
|
17
18
|
has: (id: string) => Effect.Effect<boolean, never, never>;
|
|
18
19
|
};
|
|
19
|
-
export declare const keychainPlugin: import("@executor-js/sdk
|
|
20
|
+
export declare const keychainPlugin: import("@executor-js/sdk").ConfiguredPlugin<"keychain", {
|
|
20
21
|
/** Human-readable name for the keychain on this platform */
|
|
21
22
|
displayName: string;
|
|
22
23
|
/** Whether the current platform supports system keychain */
|
|
23
24
|
isSupported: boolean;
|
|
24
|
-
/** Check if a secret exists in the system keychain
|
|
25
|
+
/** Check if a secret exists in the system keychain. `id` is the opaque
|
|
26
|
+
* provider item id (the keychain account); v2 has no scope partitioning. */
|
|
25
27
|
has: (id: string) => Effect.Effect<boolean, never, never>;
|
|
26
|
-
}, {}, KeychainPluginConfig, undefined,
|
|
28
|
+
}, {}, KeychainPluginConfig, undefined, import("effect/Layer").Layer<unknown, never, never>, import("effect/unstable/httpapi/HttpApiGroup").Any>;
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/promise.ts"],"sourcesContent":["import { type Plugin } from \"@executor-js/sdk
|
|
1
|
+
{"version":3,"sources":["../src/promise.ts"],"sourcesContent":["import { type Plugin } from \"@executor-js/sdk\";\n\nimport {\n keychainPlugin as keychainPluginEffect,\n type KeychainExtension,\n type KeychainPluginConfig,\n} from \"./index\";\n\nexport type { KeychainPluginConfig } from \"./index\";\n\n// Explicit return type so the emitted dist/promise.d.ts references\n// `import(\"@executor-js/sdk\").Plugin` rather than the Promise-surface\n// root specifier (which doesn't re-export Plugin).\nexport const keychainPlugin = (\n config?: KeychainPluginConfig,\n): Plugin<\"keychain\", KeychainExtension, Record<string, never>> => keychainPluginEffect(config);\n"],"mappings":";;;;;AAaO,IAAMA,kBAAiB,CAC5B,WACiE,eAAqB,MAAM;","names":["keychainPlugin"]}
|
package/dist/promise.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Plugin } from "@executor-js/sdk
|
|
1
|
+
import { type Plugin } from "@executor-js/sdk";
|
|
2
2
|
import { type KeychainExtension, type KeychainPluginConfig } from "./index";
|
|
3
3
|
export type { KeychainPluginConfig } from "./index";
|
|
4
|
-
export declare const keychainPlugin: (config?: KeychainPluginConfig) => Plugin<"keychain", KeychainExtension, Record<string, never
|
|
4
|
+
export declare const keychainPlugin: (config?: KeychainPluginConfig) => Plugin<"keychain", KeychainExtension, Record<string, never>>;
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const makeKeychainProvider: (baseServiceName: string) => SecretProvider;
|
|
1
|
+
import { type CredentialProvider } from "@executor-js/sdk";
|
|
2
|
+
export declare const makeKeychainProvider: (serviceName: string) => CredentialProvider;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@executor-js/plugin-keychain",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"homepage": "https://github.com/RhysSullivan/executor/tree/main/packages/plugins/keychain",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/RhysSullivan/executor/issues"
|
|
@@ -40,15 +40,18 @@
|
|
|
40
40
|
"typecheck:slow": "bunx tsc --noEmit -p tsconfig.json"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@executor-js/sdk": "1.
|
|
44
|
-
"@napi-rs/keyring": "^1.2.0"
|
|
45
|
-
"effect": "4.0.0-beta.59"
|
|
43
|
+
"@executor-js/sdk": "1.5.0",
|
|
44
|
+
"@napi-rs/keyring": "^1.2.0"
|
|
46
45
|
},
|
|
47
46
|
"devDependencies": {
|
|
48
47
|
"@effect/vitest": "4.0.0-beta.59",
|
|
49
48
|
"@types/node": "^24.3.1",
|
|
50
49
|
"bun-types": "^1.2.22",
|
|
50
|
+
"effect": "4.0.0-beta.59",
|
|
51
51
|
"tsup": "^8.5.0",
|
|
52
52
|
"vitest": "^4.1.5"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"effect": "4.0.0-beta.59"
|
|
53
56
|
}
|
|
54
57
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/keyring.ts","../src/errors.ts","../src/provider.ts"],"sourcesContent":["import { Effect } from \"effect\";\n\nimport { definePlugin, type PluginCtx, type SecretProvider } from \"@executor-js/sdk/core\";\n\nimport {\n deletePassword,\n displayName,\n getPassword,\n isSupportedPlatform,\n resolveServiceName,\n setPassword,\n} from \"./keyring\";\nimport { makeKeychainProvider, scopedKeychainServiceName } from \"./provider\";\n\n// Probe the keychain by writing and then deleting a sentinel entry. A\n// read-only probe isn't enough — on some Linux environments (WSL2,\n// headless CI) `getPassword` for a missing key returns null without\n// error, but `setPassword` fails because the secret-service backend\n// isn't actually reachable. Writing is the capability the executor\n// cares about, so test it directly.\nconst PROBE_VALUE = \"probe\";\nconst probeAccount = (): string =>\n `__executor_keychain_probe__:${process.pid}:${Date.now()}:${Math.random().toString(36).slice(2)}`;\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport { KeychainError } from \"./errors\";\nexport { makeKeychainProvider } from \"./provider\";\nexport { isSupportedPlatform, displayName } from \"./keyring\";\n\n// ---------------------------------------------------------------------------\n// Plugin config\n// ---------------------------------------------------------------------------\n\nexport interface KeychainPluginConfig {\n /** Override the keychain service name (default: \"executor\") */\n readonly serviceName?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Plugin extension — public API on executor.keychain\n// ---------------------------------------------------------------------------\n\nexport type KeychainExtension = ReturnType<typeof makeKeychainExtension>;\n\n// ---------------------------------------------------------------------------\n// Plugin definition\n// ---------------------------------------------------------------------------\n\nconst makeKeychainExtension = (\n ctx: PluginCtx<unknown>,\n options: KeychainPluginConfig | undefined,\n) => {\n const baseServiceName = resolveServiceName(options?.serviceName);\n return {\n /** Human-readable name for the keychain on this platform */\n displayName: displayName(),\n\n /** Whether the current platform supports system keychain */\n isSupported: isSupportedPlatform(),\n\n /** Check if a secret exists in the system keychain */\n has: (id: string) =>\n Effect.gen(function* () {\n for (const scope of ctx.scopes) {\n const exists = yield* getPassword(\n scopedKeychainServiceName(baseServiceName, scope.id),\n id,\n ).pipe(\n Effect.map((v) => v !== null),\n Effect.orElseSucceed(() => false),\n );\n if (exists) return true;\n }\n return false;\n }),\n };\n};\n\nexport const keychainPlugin = definePlugin((options?: KeychainPluginConfig) => ({\n id: \"keychain\" as const,\n storage: () => ({}),\n\n extension: (ctx): KeychainExtension => makeKeychainExtension(ctx, options),\n\n secretProviders: (ctx): Effect.Effect<readonly SecretProvider[]> =>\n Effect.gen(function* () {\n const baseServiceName = resolveServiceName(options?.serviceName);\n const probeServiceName = scopedKeychainServiceName(baseServiceName, ctx.scopes[0]!.id);\n const account = probeAccount();\n const reachable = yield* setPassword(probeServiceName, account, PROBE_VALUE).pipe(\n Effect.andThen(\n deletePassword(probeServiceName, account).pipe(Effect.catch(() => Effect.void)),\n ),\n Effect.as(true),\n Effect.catch(() =>\n Effect.logWarning(\"keychain unavailable, skipping provider registration\").pipe(\n Effect.as(false),\n ),\n ),\n );\n return reachable ? [makeKeychainProvider(baseServiceName)] : [];\n }),\n}));\n","import { createRequire } from \"node:module\";\n\nimport { Effect } from \"effect\";\n\nimport { KeychainError } from \"./errors\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_SERVICE_NAME = \"executor\";\nconst SERVICE_NAME_ENV = \"EXECUTOR_KEYCHAIN_SERVICE_NAME\";\n\n// ---------------------------------------------------------------------------\n// Platform helpers\n// ---------------------------------------------------------------------------\n\nexport const isSupportedPlatform = () =>\n process.platform === \"darwin\" || process.platform === \"linux\" || process.platform === \"win32\";\n\nexport const displayName = () =>\n process.platform === \"darwin\"\n ? \"macOS Keychain\"\n : process.platform === \"win32\"\n ? \"Windows Credential Manager\"\n : \"Desktop Keyring\";\n\nexport const resolveServiceName = (explicit?: string): string =>\n explicit?.trim() || process.env[SERVICE_NAME_ENV]?.trim() || DEFAULT_SERVICE_NAME;\n\n// ---------------------------------------------------------------------------\n// Lazy-load @napi-rs/keyring (native module)\n// ---------------------------------------------------------------------------\n\ntype EntryConstructor = (typeof import(\"@napi-rs/keyring\"))[\"Entry\"];\n\nlet entryCtorPromise: Promise<EntryConstructor> | null = null;\n\n// In compiled bun binaries (`bun build --compile`) `.node` modules aren't\n// included in bunfs and there's no node_modules at runtime, so\n// @napi-rs/keyring's loader can't find its platform-specific binding.\n// `apps/cli/src/build.ts` copies the .node next to the executor and\n// `apps/cli/src/main.ts` exports its absolute path here. We load it\n// directly because @napi-rs/keyring@1.2.0's NAPI_RS_NATIVE_LIBRARY_PATH\n// branch is buggy (assigns to a local that gets overwritten before return).\nconst loadEntryCtor = async (): Promise<EntryConstructor> => {\n const directPath = process.env.EXECUTOR_KEYRING_NATIVE_PATH;\n if (directPath) {\n const req = createRequire(import.meta.url);\n return (req(directPath) as { Entry: EntryConstructor }).Entry;\n }\n const { Entry } = await import(\"@napi-rs/keyring\");\n return Entry;\n};\n\nconst loadEntry = (): Effect.Effect<EntryConstructor, KeychainError> =>\n isSupportedPlatform()\n ? Effect.tryPromise({\n try: async () => {\n entryCtorPromise ??= loadEntryCtor();\n return await entryCtorPromise;\n },\n catch: (cause) =>\n new KeychainError({\n message: \"Failed loading native keyring\",\n cause,\n }),\n })\n : Effect.fail(\n new KeychainError({\n message: `Failed loading native keyring: unsupported platform '${process.platform}'`,\n }),\n );\n\nconst createEntry = (serviceName: string, account: string) =>\n Effect.flatMap(loadEntry(), (Entry) =>\n Effect.try({\n try: () => new Entry(serviceName, account),\n catch: (cause) =>\n new KeychainError({\n message: \"Failed creating keyring entry\",\n cause,\n }),\n }),\n );\n\n// ---------------------------------------------------------------------------\n// Low-level keychain operations\n// ---------------------------------------------------------------------------\n\nexport const getPassword = (\n serviceName: string,\n account: string,\n): Effect.Effect<string | null, KeychainError> =>\n Effect.flatMap(createEntry(serviceName, account), (entry) =>\n Effect.try({\n try: () => entry.getPassword(),\n catch: () => new KeychainError({ message: `Failed reading secret for account '${account}'` }),\n }),\n );\n\nexport const setPassword = (\n serviceName: string,\n account: string,\n value: string,\n): Effect.Effect<void, KeychainError> =>\n Effect.flatMap(createEntry(serviceName, account), (entry) =>\n Effect.try({\n try: () => entry.setPassword(value),\n catch: (cause) =>\n new KeychainError({\n message: \"Failed writing secret\",\n cause,\n }),\n }).pipe(Effect.asVoid),\n );\n\nexport const deletePassword = (\n serviceName: string,\n account: string,\n): Effect.Effect<boolean, KeychainError> =>\n Effect.flatMap(createEntry(serviceName, account), (entry) =>\n Effect.try({\n try: () => {\n entry.deletePassword();\n return true;\n },\n catch: () =>\n new KeychainError({ message: `Failed deleting secret for account '${account}'` }),\n }),\n );\n","import { Data } from \"effect\";\n\nexport class KeychainError extends Data.TaggedError(\"KeychainError\")<{\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n","import { Effect } from \"effect\";\n\nimport { StorageError, type SecretProvider } from \"@executor-js/sdk/core\";\n\nimport type { KeychainError } from \"./errors\";\nimport { getPassword, setPassword, deletePassword } from \"./keyring\";\n\n// ---------------------------------------------------------------------------\n// SecretProvider adapter — bridges keyring into SDK resolution chain\n//\n// The underlying `@napi-rs/keyring` sync API encodes \"no entry\" as an\n// ordinary return value (`getPassword()` → `null`, `deletePassword()` →\n// `false`), and only throws on real failures (keychain locked, permission\n// denied, platform init failure, etc.). `keyring.ts` wraps those thrown\n// failures as `KeychainError`. We translate `KeychainError` →\n// `StorageError` so the HTTP edge can capture it to telemetry and surface\n// an opaque `InternalError({ traceId })` — previously `orElseSucceed`\n// silently converted every failure into \"nothing found\", which made it\n// impossible to debug why secrets weren't resolving.\n// ---------------------------------------------------------------------------\n\nconst toStorageError = (cause: KeychainError) => {\n const { cause: underlyingCause } = cause;\n // oxlint-disable-next-line executor/no-unknown-error-message -- boundary: typed KeychainError message becomes StorageError message\n return new StorageError({ message: cause.message, cause: underlyingCause ?? cause });\n};\n\nexport const scopedKeychainServiceName = (baseServiceName: string, scope: string): string =>\n `${baseServiceName}/${scope}`;\n\nexport const makeKeychainProvider = (baseServiceName: string): SecretProvider => ({\n key: \"keychain\",\n writable: true,\n get: (secretId, scope) =>\n getPassword(scopedKeychainServiceName(baseServiceName, scope), secretId).pipe(\n Effect.mapError(toStorageError),\n ),\n set: (secretId, value, scope) =>\n setPassword(scopedKeychainServiceName(baseServiceName, scope), secretId, value).pipe(\n Effect.mapError(toStorageError),\n ),\n delete: (secretId, scope) =>\n deletePassword(scopedKeychainServiceName(baseServiceName, scope), secretId).pipe(\n Effect.mapError(toStorageError),\n ),\n // Keychain doesn't support enumerating — you need to know the account name\n list: undefined,\n});\n"],"mappings":"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","names":["Effect","Effect","Effect","Effect"]}
|