@executor-js/plugin-onepassword 0.0.1 → 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.
- package/README.md +30 -10
- package/dist/api/group.d.ts +19 -20
- package/dist/api/handlers.d.ts +2 -2
- package/dist/chunk-2NSVLCQP.js +446 -0
- package/dist/chunk-2NSVLCQP.js.map +1 -0
- package/dist/core.js +3 -1
- package/dist/index.js +1 -1
- package/dist/promise.d.ts +4 -0
- package/dist/react/OnePasswordSettings.d.ts +1 -0
- package/dist/react/atoms.d.ts +22 -0
- package/dist/react/client.d.ts +16 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/plugin-client.d.ts +2 -0
- package/dist/react/secret-provider-plugin.d.ts +2 -0
- package/dist/sdk/errors.d.ts +4 -6
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/plugin.d.ts +43 -13
- package/dist/sdk/plugin.test.d.ts +1 -0
- package/dist/sdk/service.d.ts +1 -1
- package/dist/sdk/types.d.ts +23 -68
- package/package.json +8 -13
- package/dist/chunk-57NB4OW6.js +0 -311
- package/dist/chunk-57NB4OW6.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,29 +1,31 @@
|
|
|
1
|
-
# @executor/plugin-onepassword
|
|
1
|
+
# @executor-js/plugin-onepassword
|
|
2
2
|
|
|
3
3
|
[1Password](https://1password.com) integration for the executor. Provides a secret source that resolves values from a 1Password vault, backed by either the desktop app (connect.sock) or a service account token.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
bun add @executor/sdk @executor/plugin-onepassword
|
|
8
|
+
bun add @executor-js/sdk @executor-js/plugin-onepassword
|
|
9
9
|
# or
|
|
10
|
-
npm install @executor/sdk @executor/plugin-onepassword
|
|
10
|
+
npm install @executor-js/sdk @executor-js/plugin-onepassword
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import { createExecutor } from "@executor/sdk";
|
|
17
|
-
import { onepasswordPlugin } from "@executor/plugin-onepassword";
|
|
16
|
+
import { createExecutor } from "@executor-js/sdk";
|
|
17
|
+
import { onepasswordPlugin } from "@executor-js/plugin-onepassword";
|
|
18
18
|
|
|
19
19
|
const executor = await createExecutor({
|
|
20
|
-
|
|
20
|
+
onElicitation: "accept-all",
|
|
21
21
|
plugins: [onepasswordPlugin()] as const,
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
// Point the plugin at your account
|
|
25
25
|
await executor.onepassword.configure({
|
|
26
26
|
auth: { kind: "desktop-app", accountName: "my-account" },
|
|
27
|
+
vaultId: "my-vault-id",
|
|
28
|
+
name: "Personal",
|
|
27
29
|
});
|
|
28
30
|
|
|
29
31
|
// Inspect connection / list vaults
|
|
@@ -34,20 +36,38 @@ const vaults = await executor.onepassword.listVaults({
|
|
|
34
36
|
});
|
|
35
37
|
```
|
|
36
38
|
|
|
37
|
-
For CI and headless environments, use a service-account token instead of the desktop app:
|
|
39
|
+
For CI and headless environments, use a service-account token instead of the desktop app. Store the token through the executor's secret store first, then reference it by id:
|
|
38
40
|
|
|
39
41
|
```ts
|
|
42
|
+
import { createExecutor } from "@executor-js/sdk";
|
|
43
|
+
import { onepasswordPlugin } from "@executor-js/plugin-onepassword";
|
|
44
|
+
import { fileSecretsPlugin } from "@executor-js/plugin-file-secrets";
|
|
45
|
+
|
|
46
|
+
const executor = await createExecutor({
|
|
47
|
+
onElicitation: "accept-all",
|
|
48
|
+
plugins: [fileSecretsPlugin(), onepasswordPlugin()] as const,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
await executor.secrets.set({
|
|
52
|
+
id: "op-token",
|
|
53
|
+
name: "1Password service account",
|
|
54
|
+
value: process.env.OP_SERVICE_ACCOUNT_TOKEN!,
|
|
55
|
+
scope: executor.scopes[0]!.id,
|
|
56
|
+
});
|
|
57
|
+
|
|
40
58
|
await executor.onepassword.configure({
|
|
41
|
-
auth: { kind: "service-account",
|
|
59
|
+
auth: { kind: "service-account", tokenSecretId: "op-token" },
|
|
60
|
+
vaultId: "my-vault-id",
|
|
61
|
+
name: "CI",
|
|
42
62
|
});
|
|
43
63
|
```
|
|
44
64
|
|
|
45
65
|
## Using with Effect
|
|
46
66
|
|
|
47
|
-
If you're building on `@executor/sdk` (the raw Effect entry), import this plugin from its `/core` subpath instead:
|
|
67
|
+
If you're building on `@executor-js/sdk/core` (the raw Effect entry), import this plugin from its `/core` subpath instead — it returns the Effect-shaped plugin with `Effect.Effect<...>`-returning methods rather than promisified wrappers:
|
|
48
68
|
|
|
49
69
|
```ts
|
|
50
|
-
import { onepasswordPlugin } from "@executor/plugin-onepassword";
|
|
70
|
+
import { onepasswordPlugin } from "@executor-js/plugin-onepassword/core";
|
|
51
71
|
```
|
|
52
72
|
|
|
53
73
|
## Status
|
package/dist/api/group.d.ts
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { HttpApiEndpoint, HttpApiGroup } from "
|
|
1
|
+
import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
import { InternalError } from "@executor-js/api";
|
|
2
4
|
import { OnePasswordError } from "../sdk/errors";
|
|
3
5
|
import { OnePasswordConfig, Vault, ConnectionStatus } from "../sdk/types";
|
|
4
|
-
declare const
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
readonly authKind: "desktop-app"
|
|
16
|
-
readonly account:
|
|
17
|
-
}
|
|
18
|
-
readonly vaults:
|
|
19
|
-
}
|
|
20
|
-
export declare class OnePasswordGroup extends OnePasswordGroup_base {
|
|
21
|
-
}
|
|
22
|
-
export {};
|
|
6
|
+
export declare const OnePasswordGroup: HttpApiGroup.HttpApiGroup<"onepassword", HttpApiEndpoint.HttpApiEndpoint<"getConfig", "GET", "/scopes/:scopeId/onepassword/config", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
7
|
+
scopeId: Schema.brand<Schema.String, "ScopeId">;
|
|
8
|
+
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.NullOr<typeof OnePasswordConfig>>, HttpApiEndpoint.Json<typeof InternalError | typeof OnePasswordError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"configure", "PUT", "/scopes/:scopeId/onepassword/config", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
9
|
+
scopeId: Schema.brand<Schema.String, "ScopeId">;
|
|
10
|
+
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<typeof OnePasswordConfig>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Void>, HttpApiEndpoint.Json<typeof InternalError | typeof OnePasswordError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"removeConfig", "DELETE", "/scopes/:scopeId/onepassword/config", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
11
|
+
scopeId: Schema.brand<Schema.String, "ScopeId">;
|
|
12
|
+
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Void>, HttpApiEndpoint.Json<typeof InternalError | typeof OnePasswordError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"status", "GET", "/scopes/:scopeId/onepassword/status", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
13
|
+
scopeId: Schema.brand<Schema.String, "ScopeId">;
|
|
14
|
+
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<typeof ConnectionStatus>, HttpApiEndpoint.Json<typeof InternalError | typeof OnePasswordError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"listVaults", "GET", "/scopes/:scopeId/onepassword/vaults", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
15
|
+
scopeId: Schema.brand<Schema.String, "ScopeId">;
|
|
16
|
+
}>>, HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
17
|
+
readonly authKind: Schema.Literals<readonly ["desktop-app", "service-account"]>;
|
|
18
|
+
readonly account: Schema.String;
|
|
19
|
+
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
|
|
20
|
+
readonly vaults: Schema.$Array<typeof Vault>;
|
|
21
|
+
}>>, HttpApiEndpoint.Json<typeof InternalError | typeof OnePasswordError>, never, never>, false>;
|
package/dist/api/handlers.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Context } from "effect";
|
|
2
2
|
import type { OnePasswordExtension } from "../sdk/plugin";
|
|
3
|
-
declare const OnePasswordExtensionService_base: Context.
|
|
3
|
+
declare const OnePasswordExtensionService_base: Context.ServiceClass<OnePasswordExtensionService, "OnePasswordExtensionService", OnePasswordExtension>;
|
|
4
4
|
export declare class OnePasswordExtensionService extends OnePasswordExtensionService_base {
|
|
5
5
|
}
|
|
6
|
-
export declare const OnePasswordHandlers: import("effect/Layer").Layer<import("
|
|
6
|
+
export declare const OnePasswordHandlers: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").ApiGroup<"executor", "onepassword">, never, import("effect/unstable/http/HttpRouter").Request<"Requires", OnePasswordExtensionService>>;
|
|
7
7
|
export {};
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
// src/sdk/errors.ts
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
var OnePasswordError = class extends Schema.TaggedErrorClass()(
|
|
4
|
+
"OnePasswordError",
|
|
5
|
+
{
|
|
6
|
+
operation: Schema.String,
|
|
7
|
+
message: Schema.String
|
|
8
|
+
},
|
|
9
|
+
{ httpApiStatus: 502 }
|
|
10
|
+
) {
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/sdk/types.ts
|
|
14
|
+
import { Schema as Schema2 } from "effect";
|
|
15
|
+
var DesktopAppAuth = class extends Schema2.Class("DesktopAppAuth")({
|
|
16
|
+
kind: Schema2.Literal("desktop-app"),
|
|
17
|
+
/** 1Password account domain, e.g. "my.1password.com" */
|
|
18
|
+
accountName: Schema2.String
|
|
19
|
+
}) {
|
|
20
|
+
};
|
|
21
|
+
var ServiceAccountAuth = class extends Schema2.Class("ServiceAccountAuth")({
|
|
22
|
+
kind: Schema2.Literal("service-account"),
|
|
23
|
+
/** The service account token (stored as a secret) */
|
|
24
|
+
tokenSecretId: Schema2.String
|
|
25
|
+
}) {
|
|
26
|
+
};
|
|
27
|
+
var OnePasswordAuth = Schema2.Union([DesktopAppAuth, ServiceAccountAuth]);
|
|
28
|
+
var OnePasswordConfig = class extends Schema2.Class("OnePasswordConfig")({
|
|
29
|
+
auth: OnePasswordAuth,
|
|
30
|
+
/** Vault to scope operations to */
|
|
31
|
+
vaultId: Schema2.String,
|
|
32
|
+
/** Human label */
|
|
33
|
+
name: Schema2.String
|
|
34
|
+
}) {
|
|
35
|
+
};
|
|
36
|
+
var Vault = class extends Schema2.Class("Vault")({
|
|
37
|
+
id: Schema2.String,
|
|
38
|
+
name: Schema2.String
|
|
39
|
+
}) {
|
|
40
|
+
};
|
|
41
|
+
var ConnectionStatus = class extends Schema2.Class("ConnectionStatus")({
|
|
42
|
+
connected: Schema2.Boolean,
|
|
43
|
+
vaultName: Schema2.optional(Schema2.String),
|
|
44
|
+
error: Schema2.optional(Schema2.String)
|
|
45
|
+
}) {
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/sdk/service.ts
|
|
49
|
+
import { Context, Duration, Effect } from "effect";
|
|
50
|
+
import * as op from "@1password/op-js";
|
|
51
|
+
var OnePasswordServiceTag = class extends Context.Service()("@executor-js/plugin-onepassword/OnePasswordService") {
|
|
52
|
+
};
|
|
53
|
+
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
54
|
+
var loadOnePasswordSdk = () => Effect.tryPromise({
|
|
55
|
+
try: () => import("@1password/sdk"),
|
|
56
|
+
catch: (cause) => new OnePasswordError({
|
|
57
|
+
operation: "sdk module load",
|
|
58
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
var makeTimeoutMessage = (operation, timeoutMs) => [
|
|
62
|
+
`${operation}: timed out after ${Math.floor(timeoutMs / 1e3)}s.`,
|
|
63
|
+
"Troubleshooting:",
|
|
64
|
+
"1. Make sure the 1Password desktop app is open and unlocked",
|
|
65
|
+
"2. Check for an approval prompt in the 1Password app \u2014 it may be behind other windows",
|
|
66
|
+
"3. Ensure 'Developer > Connect with 1Password CLI' is enabled in 1Password Settings",
|
|
67
|
+
"4. Make sure no other app or terminal is waiting for 1Password approval (only one prompt at a time)",
|
|
68
|
+
"5. Try quitting 1Password completely and reopening it, then retry"
|
|
69
|
+
].join("\n");
|
|
70
|
+
var timeoutWithOnePasswordError = (operation, timeoutMs) => Effect.timeoutOrElse({
|
|
71
|
+
duration: Duration.millis(timeoutMs),
|
|
72
|
+
orElse: () => Effect.fail(
|
|
73
|
+
new OnePasswordError({
|
|
74
|
+
operation,
|
|
75
|
+
message: makeTimeoutMessage(operation, timeoutMs)
|
|
76
|
+
})
|
|
77
|
+
)
|
|
78
|
+
});
|
|
79
|
+
var makeNativeSdkService = (auth, timeoutMs = DEFAULT_TIMEOUT_MS) => Effect.gen(function* () {
|
|
80
|
+
const sdk = yield* loadOnePasswordSdk().pipe(
|
|
81
|
+
timeoutWithOnePasswordError("sdk module load", timeoutMs)
|
|
82
|
+
);
|
|
83
|
+
const client = yield* Effect.tryPromise({
|
|
84
|
+
try: () => sdk.createClient({
|
|
85
|
+
auth: auth.kind === "desktop-app" ? new sdk.DesktopAuth(auth.accountName) : auth.token,
|
|
86
|
+
integrationName: "Executor",
|
|
87
|
+
integrationVersion: "0.0.0"
|
|
88
|
+
}),
|
|
89
|
+
catch: (cause) => new OnePasswordError({
|
|
90
|
+
operation: "client setup",
|
|
91
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
92
|
+
})
|
|
93
|
+
}).pipe(
|
|
94
|
+
timeoutWithOnePasswordError("client setup", timeoutMs)
|
|
95
|
+
);
|
|
96
|
+
const wrap = (fn, operation) => Effect.tryPromise({
|
|
97
|
+
try: fn,
|
|
98
|
+
catch: (cause) => new OnePasswordError({
|
|
99
|
+
operation,
|
|
100
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
101
|
+
})
|
|
102
|
+
}).pipe(
|
|
103
|
+
timeoutWithOnePasswordError(operation, timeoutMs),
|
|
104
|
+
Effect.withSpan(`onepassword.sdk.${operation}`)
|
|
105
|
+
);
|
|
106
|
+
return OnePasswordServiceTag.of({
|
|
107
|
+
resolveSecret: (uri) => wrap(() => client.secrets.resolve(uri), "secret resolution"),
|
|
108
|
+
listVaults: () => wrap(() => client.vaults.list({ decryptDetails: true }), "vault listing").pipe(
|
|
109
|
+
Effect.map((vaults) => vaults.map((v) => ({ id: v.id, title: v.title })))
|
|
110
|
+
),
|
|
111
|
+
listItems: (vaultId) => wrap(() => client.items.list(vaultId), "item listing").pipe(
|
|
112
|
+
Effect.map((items) => items.map((i) => ({ id: i.id, title: i.title })))
|
|
113
|
+
)
|
|
114
|
+
});
|
|
115
|
+
}).pipe(Effect.withSpan("onepassword.sdk.make_service"));
|
|
116
|
+
var makeCliService = (auth) => Effect.sync(() => {
|
|
117
|
+
if (auth.kind === "service-account") {
|
|
118
|
+
op.setServiceAccount(auth.token);
|
|
119
|
+
} else {
|
|
120
|
+
op.setGlobalFlags({ account: auth.accountName });
|
|
121
|
+
}
|
|
122
|
+
const wrapSync = (fn, operation) => Effect.try({
|
|
123
|
+
try: fn,
|
|
124
|
+
catch: (cause) => new OnePasswordError({
|
|
125
|
+
operation,
|
|
126
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
127
|
+
})
|
|
128
|
+
}).pipe(Effect.withSpan(`onepassword.cli.${operation}`));
|
|
129
|
+
return OnePasswordServiceTag.of({
|
|
130
|
+
resolveSecret: (uri) => wrapSync(() => op.read.parse(uri), "secret resolution"),
|
|
131
|
+
listVaults: () => wrapSync(() => op.vault.list(), "vault listing").pipe(
|
|
132
|
+
Effect.map((vaults) => vaults.map((v) => ({ id: v.id, title: v.name })))
|
|
133
|
+
),
|
|
134
|
+
listItems: (vaultId) => wrapSync(() => op.item.list({ vault: vaultId }), "item listing").pipe(
|
|
135
|
+
Effect.map((items) => items.map((i) => ({ id: i.id, title: i.title })))
|
|
136
|
+
)
|
|
137
|
+
});
|
|
138
|
+
}).pipe(Effect.withSpan("onepassword.cli.make_service"));
|
|
139
|
+
var makeOnePasswordService = (auth, options) => {
|
|
140
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
141
|
+
if (options?.preferSdk) {
|
|
142
|
+
return makeNativeSdkService(auth, timeoutMs);
|
|
143
|
+
}
|
|
144
|
+
return makeCliService(auth).pipe(
|
|
145
|
+
Effect.catch(
|
|
146
|
+
(cliError) => (
|
|
147
|
+
// CLI unavailable (e.g. `op` not installed) — fall back to SDK
|
|
148
|
+
makeNativeSdkService(auth, timeoutMs).pipe(Effect.mapError(() => cliError))
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/sdk/plugin.ts
|
|
155
|
+
import { Effect as Effect3, Schema as Schema4 } from "effect";
|
|
156
|
+
import {
|
|
157
|
+
definePlugin,
|
|
158
|
+
StorageError
|
|
159
|
+
} from "@executor-js/sdk/core";
|
|
160
|
+
|
|
161
|
+
// src/api/group.ts
|
|
162
|
+
import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
|
|
163
|
+
import { Schema as Schema3 } from "effect";
|
|
164
|
+
import { ScopeId } from "@executor-js/sdk/core";
|
|
165
|
+
import { InternalError } from "@executor-js/api";
|
|
166
|
+
var ScopeParams = { scopeId: ScopeId };
|
|
167
|
+
var ConfigurePayload = OnePasswordConfig;
|
|
168
|
+
var ListVaultsParams = Schema3.Struct({
|
|
169
|
+
authKind: Schema3.Literals(["desktop-app", "service-account"]),
|
|
170
|
+
account: Schema3.String
|
|
171
|
+
});
|
|
172
|
+
var ListVaultsResponse = Schema3.Struct({
|
|
173
|
+
vaults: Schema3.Array(Vault)
|
|
174
|
+
});
|
|
175
|
+
var GetConfigResponse = Schema3.NullOr(OnePasswordConfig);
|
|
176
|
+
var OnePasswordGroup = HttpApiGroup.make("onepassword").add(
|
|
177
|
+
HttpApiEndpoint.get("getConfig", "/scopes/:scopeId/onepassword/config", {
|
|
178
|
+
params: ScopeParams,
|
|
179
|
+
success: GetConfigResponse,
|
|
180
|
+
error: [InternalError, OnePasswordError]
|
|
181
|
+
})
|
|
182
|
+
).add(
|
|
183
|
+
HttpApiEndpoint.put("configure", "/scopes/:scopeId/onepassword/config", {
|
|
184
|
+
params: ScopeParams,
|
|
185
|
+
payload: ConfigurePayload,
|
|
186
|
+
success: Schema3.Void,
|
|
187
|
+
error: [InternalError, OnePasswordError]
|
|
188
|
+
})
|
|
189
|
+
).add(
|
|
190
|
+
HttpApiEndpoint.delete("removeConfig", "/scopes/:scopeId/onepassword/config", {
|
|
191
|
+
params: ScopeParams,
|
|
192
|
+
success: Schema3.Void,
|
|
193
|
+
error: [InternalError, OnePasswordError]
|
|
194
|
+
})
|
|
195
|
+
).add(
|
|
196
|
+
HttpApiEndpoint.get("status", "/scopes/:scopeId/onepassword/status", {
|
|
197
|
+
params: ScopeParams,
|
|
198
|
+
success: ConnectionStatus,
|
|
199
|
+
error: [InternalError, OnePasswordError]
|
|
200
|
+
})
|
|
201
|
+
).add(
|
|
202
|
+
HttpApiEndpoint.get("listVaults", "/scopes/:scopeId/onepassword/vaults", {
|
|
203
|
+
params: ScopeParams,
|
|
204
|
+
query: ListVaultsParams,
|
|
205
|
+
success: ListVaultsResponse,
|
|
206
|
+
error: [InternalError, OnePasswordError]
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// src/api/handlers.ts
|
|
211
|
+
import { HttpApiBuilder } from "effect/unstable/httpapi";
|
|
212
|
+
import { Context as Context2, Effect as Effect2 } from "effect";
|
|
213
|
+
import { addGroup, capture } from "@executor-js/api";
|
|
214
|
+
var OnePasswordExtensionService = class extends Context2.Service()("OnePasswordExtensionService") {
|
|
215
|
+
};
|
|
216
|
+
var ExecutorApiWithOnePassword = addGroup(OnePasswordGroup);
|
|
217
|
+
var OnePasswordHandlers = HttpApiBuilder.group(
|
|
218
|
+
ExecutorApiWithOnePassword,
|
|
219
|
+
"onepassword",
|
|
220
|
+
(handlers) => handlers.handle(
|
|
221
|
+
"getConfig",
|
|
222
|
+
() => capture(Effect2.gen(function* () {
|
|
223
|
+
const ext = yield* OnePasswordExtensionService;
|
|
224
|
+
return yield* ext.getConfig();
|
|
225
|
+
}))
|
|
226
|
+
).handle(
|
|
227
|
+
"configure",
|
|
228
|
+
({ payload }) => capture(Effect2.gen(function* () {
|
|
229
|
+
const ext = yield* OnePasswordExtensionService;
|
|
230
|
+
yield* ext.configure(payload);
|
|
231
|
+
}))
|
|
232
|
+
).handle(
|
|
233
|
+
"removeConfig",
|
|
234
|
+
() => capture(Effect2.gen(function* () {
|
|
235
|
+
const ext = yield* OnePasswordExtensionService;
|
|
236
|
+
yield* ext.removeConfig();
|
|
237
|
+
}))
|
|
238
|
+
).handle(
|
|
239
|
+
"status",
|
|
240
|
+
() => capture(Effect2.gen(function* () {
|
|
241
|
+
const ext = yield* OnePasswordExtensionService;
|
|
242
|
+
return yield* ext.status();
|
|
243
|
+
}))
|
|
244
|
+
).handle(
|
|
245
|
+
"listVaults",
|
|
246
|
+
({ query: urlParams }) => capture(Effect2.gen(function* () {
|
|
247
|
+
const ext = yield* OnePasswordExtensionService;
|
|
248
|
+
const auth = urlParams.authKind === "desktop-app" ? { kind: "desktop-app", accountName: urlParams.account } : { kind: "service-account", tokenSecretId: urlParams.account };
|
|
249
|
+
const vaults = yield* ext.listVaults(auth);
|
|
250
|
+
return { vaults: [...vaults] };
|
|
251
|
+
}))
|
|
252
|
+
)
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// src/sdk/plugin.ts
|
|
256
|
+
var CREDENTIAL_FIELD = "credential";
|
|
257
|
+
var DEFAULT_TIMEOUT_MS2 = 15e3;
|
|
258
|
+
var CONFIG_KEY = "config";
|
|
259
|
+
var decodeConfig = Schema4.decodeUnknownSync(OnePasswordConfig);
|
|
260
|
+
var blobStorageError = (operation) => (cause) => new StorageError({
|
|
261
|
+
message: `onepassword blob ${operation}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
262
|
+
cause
|
|
263
|
+
});
|
|
264
|
+
var makeOnePasswordStore = (blobs, writeScope) => ({
|
|
265
|
+
getConfig: () => blobs.get(CONFIG_KEY).pipe(
|
|
266
|
+
Effect3.mapError(blobStorageError("read")),
|
|
267
|
+
Effect3.flatMap((raw) => {
|
|
268
|
+
if (raw === null) return Effect3.succeed(null);
|
|
269
|
+
return Effect3.try({
|
|
270
|
+
try: () => decodeConfig(JSON.parse(raw)),
|
|
271
|
+
catch: (cause) => new OnePasswordError({
|
|
272
|
+
operation: "config decode",
|
|
273
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
274
|
+
})
|
|
275
|
+
});
|
|
276
|
+
})
|
|
277
|
+
),
|
|
278
|
+
saveConfig: (config) => blobs.put(
|
|
279
|
+
CONFIG_KEY,
|
|
280
|
+
JSON.stringify({
|
|
281
|
+
auth: config.auth,
|
|
282
|
+
vaultId: config.vaultId,
|
|
283
|
+
name: config.name
|
|
284
|
+
}),
|
|
285
|
+
{ scope: writeScope }
|
|
286
|
+
).pipe(Effect3.mapError(blobStorageError("write"))),
|
|
287
|
+
deleteConfig: () => blobs.delete(CONFIG_KEY, { scope: writeScope }).pipe(Effect3.mapError(blobStorageError("delete")))
|
|
288
|
+
});
|
|
289
|
+
var resolveAuth = (auth, ctx) => {
|
|
290
|
+
if (auth.kind === "desktop-app") {
|
|
291
|
+
return Effect3.succeed({
|
|
292
|
+
kind: "desktop-app",
|
|
293
|
+
accountName: auth.accountName
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
return ctx.secrets.get(auth.tokenSecretId).pipe(
|
|
297
|
+
Effect3.mapError(
|
|
298
|
+
(err) => "_tag" in err && err._tag === "SecretOwnedByConnectionError" ? new OnePasswordError({
|
|
299
|
+
operation: "auth resolution",
|
|
300
|
+
message: `Service account token secret "${auth.tokenSecretId}" not found`
|
|
301
|
+
}) : err
|
|
302
|
+
),
|
|
303
|
+
Effect3.flatMap((token) => {
|
|
304
|
+
if (token === null) {
|
|
305
|
+
return Effect3.fail(
|
|
306
|
+
new OnePasswordError({
|
|
307
|
+
operation: "auth resolution",
|
|
308
|
+
message: `Service account token secret "${auth.tokenSecretId}" not found`
|
|
309
|
+
})
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
return Effect3.succeed({
|
|
313
|
+
kind: "service-account",
|
|
314
|
+
token
|
|
315
|
+
});
|
|
316
|
+
})
|
|
317
|
+
);
|
|
318
|
+
};
|
|
319
|
+
var getServiceFromConfig = (config, ctx, timeoutMs, preferSdk) => resolveAuth(config.auth, ctx).pipe(
|
|
320
|
+
Effect3.flatMap(
|
|
321
|
+
(resolved) => makeOnePasswordService(resolved, { timeoutMs, preferSdk })
|
|
322
|
+
)
|
|
323
|
+
);
|
|
324
|
+
var makeProvider = (ctx, timeoutMs, preferSdk) => ({
|
|
325
|
+
key: "onepassword",
|
|
326
|
+
writable: false,
|
|
327
|
+
// 1Password vaults are named in the stored config; the executor-scope
|
|
328
|
+
// arg isn't used for routing here. A future refactor could let the
|
|
329
|
+
// plugin store per-scope vault bindings and pick based on `scope`.
|
|
330
|
+
get: (secretId, _scope) => ctx.storage.getConfig().pipe(
|
|
331
|
+
Effect3.flatMap((config) => {
|
|
332
|
+
if (!config) return Effect3.succeed(null);
|
|
333
|
+
const uri = secretId.startsWith("op://") ? secretId : `op://${config.vaultId}/${secretId}/${CREDENTIAL_FIELD}`;
|
|
334
|
+
return getServiceFromConfig(config, ctx, timeoutMs, preferSdk).pipe(
|
|
335
|
+
Effect3.flatMap((svc) => svc.resolveSecret(uri)),
|
|
336
|
+
Effect3.map((v) => v),
|
|
337
|
+
Effect3.orElseSucceed(() => null)
|
|
338
|
+
);
|
|
339
|
+
}),
|
|
340
|
+
Effect3.orElseSucceed(() => null)
|
|
341
|
+
),
|
|
342
|
+
list: () => ctx.storage.getConfig().pipe(
|
|
343
|
+
Effect3.flatMap((config) => {
|
|
344
|
+
if (!config)
|
|
345
|
+
return Effect3.succeed(
|
|
346
|
+
[]
|
|
347
|
+
);
|
|
348
|
+
return getServiceFromConfig(config, ctx, timeoutMs, preferSdk).pipe(
|
|
349
|
+
Effect3.flatMap((svc) => svc.listItems(config.vaultId)),
|
|
350
|
+
Effect3.map(
|
|
351
|
+
(items) => items.map((item2) => ({ id: item2.id, name: item2.title }))
|
|
352
|
+
)
|
|
353
|
+
);
|
|
354
|
+
}),
|
|
355
|
+
Effect3.orElseSucceed(
|
|
356
|
+
() => []
|
|
357
|
+
)
|
|
358
|
+
)
|
|
359
|
+
});
|
|
360
|
+
var onepasswordPlugin = definePlugin(
|
|
361
|
+
(options) => {
|
|
362
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
363
|
+
const preferSdk = options?.preferSdk;
|
|
364
|
+
return {
|
|
365
|
+
id: "onepassword",
|
|
366
|
+
packageName: "@executor-js/plugin-onepassword",
|
|
367
|
+
storage: ({ blobs, scopes }) => makeOnePasswordStore(blobs, scopes.at(-1).id),
|
|
368
|
+
extension: (ctx) => {
|
|
369
|
+
return {
|
|
370
|
+
configure: (config) => ctx.storage.saveConfig(config),
|
|
371
|
+
getConfig: () => ctx.storage.getConfig(),
|
|
372
|
+
removeConfig: () => ctx.storage.deleteConfig(),
|
|
373
|
+
status: () => Effect3.gen(function* () {
|
|
374
|
+
const config = yield* ctx.storage.getConfig();
|
|
375
|
+
if (!config) {
|
|
376
|
+
return new ConnectionStatus({
|
|
377
|
+
connected: false,
|
|
378
|
+
error: "Not configured"
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
const svc = yield* getServiceFromConfig(
|
|
382
|
+
config,
|
|
383
|
+
ctx,
|
|
384
|
+
timeoutMs,
|
|
385
|
+
preferSdk
|
|
386
|
+
);
|
|
387
|
+
const vaults = yield* svc.listVaults();
|
|
388
|
+
const vault2 = vaults.find((v) => v.id === config.vaultId);
|
|
389
|
+
return new ConnectionStatus({
|
|
390
|
+
connected: true,
|
|
391
|
+
vaultName: vault2?.title
|
|
392
|
+
});
|
|
393
|
+
}),
|
|
394
|
+
listVaults: (auth) => Effect3.gen(function* () {
|
|
395
|
+
const resolved = yield* resolveAuth(auth, ctx);
|
|
396
|
+
const svc = yield* makeOnePasswordService(resolved, {
|
|
397
|
+
timeoutMs,
|
|
398
|
+
preferSdk
|
|
399
|
+
});
|
|
400
|
+
const vaults = yield* svc.listVaults();
|
|
401
|
+
return vaults.map((v) => new Vault({ id: v.id, name: v.title })).sort((a, b) => a.name.localeCompare(b.name));
|
|
402
|
+
}),
|
|
403
|
+
resolve: (uri) => Effect3.gen(function* () {
|
|
404
|
+
const config = yield* ctx.storage.getConfig();
|
|
405
|
+
if (!config) {
|
|
406
|
+
return yield* Effect3.fail(
|
|
407
|
+
new OnePasswordError({
|
|
408
|
+
operation: "resolve",
|
|
409
|
+
message: "1Password is not configured"
|
|
410
|
+
})
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
const svc = yield* getServiceFromConfig(
|
|
414
|
+
config,
|
|
415
|
+
ctx,
|
|
416
|
+
timeoutMs,
|
|
417
|
+
preferSdk
|
|
418
|
+
);
|
|
419
|
+
return yield* svc.resolveSecret(uri);
|
|
420
|
+
})
|
|
421
|
+
};
|
|
422
|
+
},
|
|
423
|
+
secretProviders: (ctx) => [makeProvider(ctx, timeoutMs, preferSdk)],
|
|
424
|
+
routes: () => OnePasswordGroup,
|
|
425
|
+
handlers: () => OnePasswordHandlers,
|
|
426
|
+
extensionService: OnePasswordExtensionService
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
export {
|
|
432
|
+
OnePasswordError,
|
|
433
|
+
DesktopAppAuth,
|
|
434
|
+
ServiceAccountAuth,
|
|
435
|
+
OnePasswordAuth,
|
|
436
|
+
OnePasswordConfig,
|
|
437
|
+
Vault,
|
|
438
|
+
ConnectionStatus,
|
|
439
|
+
OnePasswordServiceTag,
|
|
440
|
+
makeNativeSdkService,
|
|
441
|
+
makeCliService,
|
|
442
|
+
makeOnePasswordService,
|
|
443
|
+
makeOnePasswordStore,
|
|
444
|
+
onepasswordPlugin
|
|
445
|
+
};
|
|
446
|
+
//# sourceMappingURL=chunk-2NSVLCQP.js.map
|