History

Merge pull request #59 from RhysSullivan/t3code/5f897a8e

[codex] clean up plugin-based secret management

Rhys Sullivan committed 7 months ago

Commit eedcf4b2 parents 9010f52 2a973d025 files · +479 −504Browse files

Unified · Split

25 files
  1. bun.lock +27 −0
  2. examples/sqlite-sdk-consumer/src/sqlite-backend.ts +83 −15
  3. packages/platform/sdk-file/src/config.ts +0 −24
  4. packages/platform/sdk-file/src/executor-state-store.ts +75 −12
  5. packages/platform/sdk-file/src/index.ts +3 −1
  6. packages/platform/sdk-file/src/secret-material-providers.ts +14 −7
  7. packages/platform/sdk/src/backend.ts +7 −0
  8. packages/platform/sdk/src/local/secret-stores.ts +8 −8
  9. packages/platform/sdk/src/plugins.ts +63 −25
  10. packages/platform/sdk/src/runtime/execution/live.test.ts +2 −3
  11. packages/platform/sdk/src/runtime/execution/live.ts +4 −1
  12. packages/platform/sdk/src/runtime/executor-state-store.ts +21 −1
  13. packages/platform/sdk/src/runtime/index.ts +9 −2
  14. packages/platform/sdk/src/runtime/scope/config-secrets.ts +0 −9
  15. packages/platform/sdk/src/runtime/sources/source-store/plugin-local-config.ts +0 −1
  16. packages/platform/sdk/src/schema/models/local-config.ts +0 −73
  17. packages/platform/sdk/src/schema/models/secret-material.ts +0 −2
  18. packages/sources/core/src/source-models.ts +1 −2
  19. plugins/google-discovery/sdk/index.ts +0 −1
  20. plugins/graphql/sdk/index.ts +0 −1
  21. plugins/keychain-secret-store/sdk/index.ts +103 −275
  22. plugins/keychain-secret-store/sdk/package.json +1 −0
  23. plugins/local-secret-store/sdk/index.ts +24 −23
  24. plugins/onepassword/sdk/index.ts +34 −17
  25. plugins/openapi/sdk/index.ts +0 −1
bun.lock+27−0
@@ -571,6 +571,7 @@ · 570 unmodified lines
571571 "version": "0.0.0",
572572 "dependencies": {
573573 "@executor/platform-sdk": "workspace:*",
574+ "@napi-rs/keyring": "^1.2.0",
574575 "effect": "catalog:",
575576 },
576577 "devDependencies": {
@@ -1202,6 +1203,32 @@ · 625 unmodified lines
12021203
12031204 "@mswjs/interceptors": ["@mswjs/interceptors@0.41.3", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA=="],
12041205
1206+ "@napi-rs/keyring": ["@napi-rs/keyring@1.2.0", "", { "optionalDependencies": { "@napi-rs/keyring-darwin-arm64": "1.2.0", "@napi-rs/keyring-darwin-x64": "1.2.0", "@napi-rs/keyring-freebsd-x64": "1.2.0", "@napi-rs/keyring-linux-arm-gnueabihf": "1.2.0", "@napi-rs/keyring-linux-arm64-gnu": "1.2.0", "@napi-rs/keyring-linux-arm64-musl": "1.2.0", "@napi-rs/keyring-linux-riscv64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-musl": "1.2.0", "@napi-rs/keyring-win32-arm64-msvc": "1.2.0", "@napi-rs/keyring-win32-ia32-msvc": "1.2.0", "@napi-rs/keyring-win32-x64-msvc": "1.2.0" } }, "sha512-d0d4Oyxm+v980PEq1ZH2PmS6cvpMIRc17eYpiU47KgW+lzxklMu6+HOEOPmxrpnF/XQZ0+Q78I2mgMhbIIo/dg=="],
1207+
1208+ "@napi-rs/keyring-darwin-arm64": ["@napi-rs/keyring-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CA83rDeyONDADO25JLZsh3eHY8yTEtm/RS6ecPsY+1v+dSawzT9GywBMu2r6uOp1IEhQs/xAfxgybGAFr17lSA=="],
1209+
1210+ "@napi-rs/keyring-darwin-x64": ["@napi-rs/keyring-darwin-x64@1.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-dBHjtKRCj4ByfnfqIKIJLo3wueQNJhLRyuxtX/rR4K/XtcS7VLlRD01XXizjpre54vpmObj63w+ZpHG+mGM8uA=="],
1211+
1212+ "@napi-rs/keyring-freebsd-x64": ["@napi-rs/keyring-freebsd-x64@1.2.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-DPZFr11pNJSnaoh0dzSUNF+T6ORhy3CkzUT3uGixbA71cAOPJ24iG8e8QrLOkuC/StWrAku3gBnth2XMWOcR3Q=="],
1213+
1214+ "@napi-rs/keyring-linux-arm-gnueabihf": ["@napi-rs/keyring-linux-arm-gnueabihf@1.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-8xv6DyEMlvRdqJzp4F39RLUmmTQsLcGYYv/3eIfZNZN1O5257tHxTrFYqAsny659rJJK2EKeSa7PhrSibQqRWQ=="],
1215+
1216+ "@napi-rs/keyring-linux-arm64-gnu": ["@napi-rs/keyring-linux-arm64-gnu@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Pu2V6Py+PBt7inryEecirl+t+ti8bhZphjP+W68iVaXHUxLdWmkgL9KI1VkbRHbx5k8K5Tew9OP218YfmVguIA=="],
1217+
1218+ "@napi-rs/keyring-linux-arm64-musl": ["@napi-rs/keyring-linux-arm64-musl@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-8TDymrpC4P1a9iDEaegT7RnrkmrJN5eNZh3Im3UEV5PPYGtrb82CRxsuFohthCWQW81O483u1bu+25+XA4nKUw=="],
1219+
1220+ "@napi-rs/keyring-linux-riscv64-gnu": ["@napi-rs/keyring-linux-riscv64-gnu@1.2.0", "", { "os": "linux", "cpu": "none" }, "sha512-awsB5XI1MYL7fwfjMDGmKOWvNgJEO7mM7iVEMS0fO39f0kVJnOSjlu7RHcXAF0LOx+0VfF3oxbWqJmZbvRCRHw=="],
1221+
1222+ "@napi-rs/keyring-linux-x64-gnu": ["@napi-rs/keyring-linux-x64-gnu@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8E+7z4tbxSJXxIBqA+vfB1CGajpCDRyTyqXkBig5NtASrv4YXcntSo96Iah2QDR5zD3dSTsmbqJudcj9rKKuHQ=="],
1223+
1224+ "@napi-rs/keyring-linux-x64-musl": ["@napi-rs/keyring-linux-x64-musl@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8RZ8yVEnmWr/3BxKgBSzmgntI7lNEsY7xouNfOsQkuVAiCNmxzJwETspzK3PQ2FHtDxgz5vHQDEBVGMyM4hUHA=="],
1225+
1226+ "@napi-rs/keyring-win32-arm64-msvc": ["@napi-rs/keyring-win32-arm64-msvc@1.2.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-AoqaDZpQ6KPE19VBLpxyORcp+yWmHI9Xs9Oo0PJ4mfHma4nFSLVdhAubJCxdlNptHe5va7ghGCHj3L9Akiv4cQ=="],
1227+
1228+ "@napi-rs/keyring-win32-ia32-msvc": ["@napi-rs/keyring-win32-ia32-msvc@1.2.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-EYL+EEI6bCsYi3LfwcQdnX3P/R76ENKNn+3PmpGheBsUFLuh0gQuP7aMVHM4rTw6UVe+L3vCLZSptq/oeacz0A=="],
1229+
1230+ "@napi-rs/keyring-win32-x64-msvc": ["@napi-rs/keyring-win32-x64-msvc@1.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-xFlx/TsmqmCwNU9v+AVnEJgoEAlBYgzFF5Ihz1rMpPAt4qQWWkMd4sCyM1gMJ1A/GnRqRegDiQpwaxGUHFtFbA=="],
1231+
12051232 "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
12061233
12071234 "@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="],
examples/sqlite-sdk-consumer/src/sqlite-backend.ts+83−15
@@ -65,6 +65,10 @@ type SecretMaterialSummary = { · 64 unmodified lines
6565 updatedAt: number;
6666 };
6767
68+type SqliteSecretStoredData = {
69+ value: string;
70+};
71+
6872 const SQLITE_SECRET_STORE_KIND = "sqlite";
6973 const SQLITE_SECRET_STORE_ID = "sts_sqlite";
7074
@@ -92,7 +96,7 @@ const sourceArtifacts = sqliteTable("source_artifacts", { · 21 unmodified lines
9296
9397 const secretMaterials = sqliteTable("secret_materials", {
9498 id: text("id").primaryKey(),
95− providerId: text("provider_id").notNull(),
99+ storeId: text("store_id").notNull(),
96100 name: text("name"),
97101 purpose: text("purpose").notNull(),
98102 createdAt: integer("created_at").notNull(),
@@ -100,6 +104,11 @@ const secretMaterials = sqliteTable("secret_materials", { · 1 unmodified line
100104 json: text("json").notNull(),
101105 });
102106
107+const secretMaterialStoredData = sqliteTable("secret_material_stored_data", {
108+ secretId: text("secret_id").primaryKey(),
109+ json: text("json").notNull(),
110+});
111+
103112 const executions = sqliteTable("executions", {
104113 id: text("id").primaryKey(),
105114 scopeId: text("scope_id").notNull(),
@@ -213,7 +222,7 @@ const openSqliteStore = (databasePath: string) => { · 107 unmodified lines
213222
214223 CREATE TABLE IF NOT EXISTS secret_materials (
215224 id TEXT PRIMARY KEY NOT NULL,
216− provider_id TEXT NOT NULL,
225+ store_id TEXT NOT NULL,
217226 name TEXT,
218227 purpose TEXT NOT NULL,
219228 created_at INTEGER NOT NULL,
@@ -221,6 +230,11 @@ const openSqliteStore = (databasePath: string) => { · 1 unmodified line
221230 json TEXT NOT NULL
222231 );
223232
233+ CREATE TABLE IF NOT EXISTS secret_material_stored_data (
234+ secret_id TEXT PRIMARY KEY NOT NULL,
235+ json TEXT NOT NULL
236+ );
237+
224238 CREATE TABLE IF NOT EXISTS executions (
225239 id TEXT PRIMARY KEY NOT NULL,
226240 scope_id TEXT NOT NULL,
@@ -241,6 +255,7 @@ const openSqliteStore = (databasePath: string) => { · 14 unmodified lines
241255 json TEXT NOT NULL
242256 );
243257 `);
258+
244259 return {
245260 sqlite,
246261 db,
@@ -292,7 +307,7 @@ const createStorageDomains = ( · 45 unmodified lines
292307 listAll: (): readonly SecretMaterialSummary[] =>
293308 store.db.select().from(secretMaterials).all().map((row) => ({
294309 id: row.id,
295− storeId: row.providerId,
310+ storeId: row.storeId,
296311 name: row.name,
297312 purpose: row.purpose,
298313 createdAt: row.createdAt,
@@ -301,7 +316,7 @@ const createStorageDomains = ( · 2 unmodified lines
301316 upsert: (material: SecretMaterial) =>
302317 store.db.insert(secretMaterials).values({
303318 id: material.id,
304− providerId: material.storeId,
319+ storeId: material.storeId,
305320 name: material.name,
306321 purpose: material.purpose,
307322 createdAt: material.createdAt,
@@ -310,7 +325,7 @@ const createStorageDomains = ( · 2 unmodified lines
310325 }).onConflictDoUpdate({
311326 target: secretMaterials.id,
312327 set: {
313− providerId: material.storeId,
328+ storeId: material.storeId,
314329 name: material.name,
315330 purpose: material.purpose,
316331 createdAt: material.createdAt,
@@ -320,7 +335,7 @@ const createStorageDomains = ( · 3 unmodified lines
320335 }).run(),
321336 updateById: (
322337 id: SecretMaterial["id"],
323− update: { name?: string | null; value?: string },
338+ update: { name?: string | null },
324339 ) => {
325340 const row = store.db.select().from(secretMaterials).where(eq(secretMaterials.id, id)).get();
326341 if (!row) return null;
@@ -328,11 +343,10 @@ const createStorageDomains = ( · 1 unmodified line
328343 const next = {
329344 ...current,
330345 name: update.name === undefined ? current.name : update.name,
331− value: update.value === undefined ? current.value : update.value,
332346 updatedAt: Date.now(),
333347 };
334348 store.db.update(secretMaterials).set({
335− providerId: next.storeId,
349+ storeId: next.storeId,
336350 name: next.name,
337351 purpose: next.purpose,
338352 createdAt: next.createdAt,
@@ -356,6 +370,40 @@ const createStorageDomains = ( · 17 unmodified lines
356370 store.db.delete(secretMaterials).where(eq(secretMaterials.id, id)).run();
357371 return true;
358372 },
373+ secretMaterialStoredData: {
374+ getBySecretId: (secretId: SecretMaterial["id"]) => {
375+ const row = store.db.select().from(secretMaterialStoredData).where(
376+ eq(secretMaterialStoredData.secretId, secretId),
377+ ).get();
378+ return row
379+ ? {
380+ secretId,
381+ data: parseJson<SqliteSecretStoredData>(row.json),
382+ }
383+ : null;
384+ },
385+ upsert: (record: { secretId: SecretMaterial["id"]; data: unknown }) => {
386+ store.db.insert(secretMaterialStoredData).values({
387+ secretId: record.secretId,
388+ json: JSON.stringify(record.data),
389+ }).onConflictDoUpdate({
390+ target: secretMaterialStoredData.secretId,
391+ set: {
392+ json: JSON.stringify(record.data),
393+ },
394+ }).run();
395+ },
396+ removeBySecretId: (secretId: SecretMaterial["id"]) => {
397+ const row = store.db.select({ secretId: secretMaterialStoredData.secretId }).from(
398+ secretMaterialStoredData,
399+ ).where(eq(secretMaterialStoredData.secretId, secretId)).get();
400+ if (!row) return false;
401+ store.db.delete(secretMaterialStoredData).where(
402+ eq(secretMaterialStoredData.secretId, secretId),
403+ ).run();
404+ return true;
405+ },
406+ },
359407 },
360408 executions: {
361409 runs: {
@@ -612,14 +660,18 @@ export const createSqliteExecutorBackend = ( · 250 unmodified lines
612660 secrets: {
613661 ...secrets,
614662 secretStores: secrets.secretStores,
663+ secretMaterialStoredData: secrets.secretMaterialStoredData,
615664 resolve: ({ ref }) => {
616665 const material = secrets.getById(
617666 ref.secretId as SecretMaterial["id"],
618667 );
619− if (!material || material.value === null) {
668+ const stored = secrets.secretMaterialStoredData.getBySecretId(
669+ ref.secretId as SecretMaterial["id"],
670+ );
671+ if (!material || !stored) {
620672 throw new Error(`Missing secret material ${ref.secretId}`);
621673 }
622− return material.value;
674+ return (stored.data as SqliteSecretStoredData).value;
623675 },
624676 store: ({ purpose, value, name, storeId }) => {
625677 const now = Date.now();
@@ -627,28 +679,44 @@ export const createSqliteExecutorBackend = ( · 1 unmodified line
627679 const material: SecretMaterial = {
628680 id,
629681 storeId: (storeId ?? SQLITE_SECRET_STORE_ID) as SecretMaterial["storeId"],
630− handle: id,
631682 name: name ?? null,
632683 purpose,
633− value,
634684 createdAt: now,
635685 updatedAt: now,
636686 };
637687 secrets.upsert(material);
688+ secrets.secretMaterialStoredData.upsert({
689+ secretId: id,
690+ data: {
691+ value,
692+ } satisfies SqliteSecretStoredData,
693+ });
638694 return {
639695 secretId: material.id,
640696 } satisfies SecretRef;
641697 },
642− delete: (ref) =>
643− secrets.removeById(ref.secretId as SecretMaterial["id"]),
698+ delete: (ref) => {
699+ secrets.secretMaterialStoredData.removeBySecretId(
700+ ref.secretId as SecretMaterial["id"],
701+ );
702+ return secrets.removeById(ref.secretId as SecretMaterial["id"]);
703+ },
644704 update: ({ ref, name, value }) => {
645705 const updated = secrets.updateById(
646706 ref.secretId as SecretMaterial["id"],
647− { name, value },
707+ { name },
648708 );
649709 if (!updated) {
650710 throw new Error(`Missing secret material ${ref.secretId}`);
651711 }
712+ if (value !== undefined) {
713+ secrets.secretMaterialStoredData.upsert({
714+ secretId: ref.secretId as SecretMaterial["id"],
715+ data: {
716+ value,
717+ } satisfies SqliteSecretStoredData,
718+ });
719+ }
652720 return updated;
653721 },
654722 },
packages/platform/sdk-file/src/config.ts+0−24
@@ -11,7 +11,6 @@ import { · 10 unmodified lines
1111 LocalExecutorConfigSchema,
1212 type LocalExecutorConfig,
1313 type LocalConfigPolicy,
14− type LocalConfigSecretProvider,
1514 type LocalConfigSource,
1615 } from "@executor/platform-sdk/schema";
1716 import type { LoadedLocalExecutorConfig } from "@executor/platform-sdk/runtime";
@@ -249,19 +248,6 @@ const mergePolicyMaps = ( · 231 unmodified lines
249248 };
250249 };
251250
252−const mergeSecretProviderMaps = (
253− base: Record<string, LocalConfigSecretProvider> | undefined,
254− extra: Record<string, LocalConfigSecretProvider> | undefined,
255−): Record<string, LocalConfigSecretProvider> | undefined => {
256− if (!base && !extra) {
257− return undefined;
258− }
259− return {
260− ...base,
261− ...extra,
262− };
263−};
264−
265251 export const mergeLocalExecutorConfigs = (
266252 base: LocalExecutorConfig | null,
267253 extra: LocalExecutorConfig | null,
@@ -278,16 +264,6 @@ export const mergeLocalExecutorConfigs = ( · 10 unmodified lines
278264 },
279265 sources: mergeSourceMaps(base?.sources, extra?.sources),
280266 policies: mergePolicyMaps(base?.policies, extra?.policies),
281− secrets: {
282− providers: mergeSecretProviderMaps(
283− base?.secrets?.providers,
284− extra?.secrets?.providers,
285− ),
286− defaults: {
287− ...base?.secrets?.defaults,
288− ...extra?.secrets?.defaults,
289− },
290− },
291267 });
292268 };
293269
packages/platform/sdk-file/src/executor-state-store.ts+75−12
@@ -26,13 +26,21 @@ import { · 25 unmodified lines
2626 unknownLocalErrorDetails,
2727 } from "./errors";
2828
29−const LOCAL_EXECUTOR_STATE_VERSION = 1 as const;
29+const LOCAL_EXECUTOR_STATE_VERSION = 2 as const;
3030 const LOCAL_EXECUTOR_STATE_BASENAME = "executor-state.json";
3131
32+const SecretMaterialStoredDataRecordSchema = Schema.Struct({
33+ secretId: Schema.String,
34+ data: Schema.Unknown,
35+});
36+
37+type SecretMaterialStoredDataRecord = typeof SecretMaterialStoredDataRecordSchema.Type;
38+
3239 const LocalExecutorStateSnapshotSchema = Schema.Struct({
3340 version: Schema.Literal(LOCAL_EXECUTOR_STATE_VERSION),
3441 secretStores: Schema.Array(SecretStoreSchema),
3542 secretMaterials: Schema.Array(SecretMaterialSchema),
43+ secretMaterialStoredData: Schema.Array(SecretMaterialStoredDataRecordSchema),
3644 executions: Schema.Array(ExecutionSchema),
3745 executionInteractions: Schema.Array(ExecutionInteractionSchema),
3846 executionSteps: Schema.Array(ExecutionStepSchema),
@@ -53,6 +61,7 @@ const defaultLocalExecutorStateSnapshot = (): LocalExecutorStateSnapshot => ({ · 14 unmodified lines
5361 version: LOCAL_EXECUTOR_STATE_VERSION,
5462 secretStores: [],
5563 secretMaterials: [],
64+ secretMaterialStoredData: [],
5665 executions: [],
5766 executionInteractions: [],
5867 executionSteps: [],
@@ -60,7 +69,6 @@ const defaultLocalExecutorStateSnapshot = (): LocalExecutorStateSnapshot => ({ · 1 unmodified line
6069
6170 const cloneValue = <T>(value: T): T =>
6271 JSON.parse(JSON.stringify(value)) as T;
63−
6472 const mapFileSystemError = (path: string, action: string) => (cause: unknown) =>
6573 new LocalFileSystemError({
6674 message: `Failed to ${action} ${path}: ${unknownLocalErrorDetails(cause)}`,
@@ -156,18 +164,19 @@ export const writeLocalExecutorStateSnapshot = (input: { · 89 unmodified lines
156164 }): Effect.Effect<void, LocalFileSystemError> =>
157165 bindNodeFileSystem(writeStateToDisk(input.context, input.state));
158166
159−const mergeById = <T extends { id: string }>(
167+const mergeByKey = <T>(
160168 current: readonly T[],
161169 imported: readonly T[],
170+ getKey: (value: T) => string,
162171 ): T[] => {
163172 const merged = new Map<string, T>();
164173
165174 for (const item of imported) {
166− merged.set(item.id, cloneValue(item));
175+ merged.set(getKey(item), cloneValue(item));
167176 }
168177
169178 for (const item of current) {
170− merged.set(item.id, cloneValue(item));
179+ merged.set(getKey(item), cloneValue(item));
171180 }
172181
173182 return [...merged.values()];
@@ -178,22 +187,35 @@ export const mergeImportedLocalExecutorStateSnapshot = (input: { · 4 unmodified lines
178187 imported: Partial<Omit<LocalExecutorStateSnapshot, "version">>;
179188 }): LocalExecutorStateSnapshot => ({
180189 version: LOCAL_EXECUTOR_STATE_VERSION,
181− secretStores: mergeById(
190+ secretStores: mergeByKey(
182191 input.current.secretStores,
183192 input.imported.secretStores ?? [],
193+ (item) => item.id,
184194 ),
185− secretMaterials: mergeById(
195+ secretMaterials: mergeByKey(
186196 input.current.secretMaterials,
187197 input.imported.secretMaterials ?? [],
198+ (item) => item.id,
188199 ),
189− executions: mergeById(input.current.executions, input.imported.executions ?? []),
190− executionInteractions: mergeById(
200+ secretMaterialStoredData: mergeByKey(
201+ input.current.secretMaterialStoredData,
202+ input.imported.secretMaterialStoredData ?? [],
203+ (item) => item.secretId,
204+ ),
205+ executions: mergeByKey(
206+ input.current.executions,
207+ input.imported.executions ?? [],
208+ (item) => item.id,
209+ ),
210+ executionInteractions: mergeByKey(
191211 input.current.executionInteractions,
192212 input.imported.executionInteractions ?? [],
213+ (item) => item.id,
193214 ),
194− executionSteps: mergeById(
215+ executionSteps: mergeByKey(
195216 input.current.executionSteps,
196217 input.imported.executionSteps ?? [],
218+ (item) => item.id,
197219 ),
198220 });
199221
@@ -390,7 +412,7 @@ export const createLocalExecutorStateStore = ( · 190 unmodified lines
390412
391413 updateById: (
392414 id: SecretMaterial["id"],
393− update: { name?: string | null; value?: string },
415+ update: { name?: string | null },
394416 ) =>
395417 stateManager.mutate((state) => {
396418 let updated: SecretMaterial | null = null;
@@ -402,7 +424,6 @@ export const createLocalExecutorStateStore = ( · 5 unmodified lines
402424 updated = {
403425 ...material,
404426 ...(update.name !== undefined ? { name: update.name } : {}),
405− ...(update.value !== undefined ? { value: update.value } : {}),
406427 updatedAt: Date.now(),
407428 } satisfies SecretMaterial;
408429 return updated;
@@ -432,6 +453,48 @@ export const createLocalExecutorStateStore = ( · 23 unmodified lines
432453 } satisfies StateMutationResult<boolean>;
433454 }),
434455 },
456+ secretMaterialStoredData: {
457+ getBySecretId: (secretId: SecretMaterial["id"]) =>
458+ stateManager.read((state) => {
459+ const record = state.secretMaterialStoredData.find(
460+ (candidate) => candidate.secretId === secretId,
461+ );
462+ return record
463+ ? Option.some(cloneValue(record))
464+ : Option.none<SecretMaterialStoredDataRecord>();
465+ }),
466+
467+ upsert: (record: SecretMaterialStoredDataRecord) =>
468+ stateManager.mutate((state) => {
469+ const nextRecords = state.secretMaterialStoredData.filter(
470+ (candidate) => candidate.secretId !== record.secretId,
471+ );
472+ nextRecords.push(cloneValue(record));
473+
474+ return {
475+ state: {
476+ ...state,
477+ secretMaterialStoredData: nextRecords,
478+ },
479+ value: undefined,
480+ } satisfies StateMutationResult<void>;
481+ }),
482+
483+ removeBySecretId: (secretId: SecretMaterial["id"]) =>
484+ stateManager.mutate((state) => {
485+ const nextRecords = state.secretMaterialStoredData.filter(
486+ (candidate) => candidate.secretId !== secretId,
487+ );
488+
489+ return {
490+ state: {
491+ ...state,
492+ secretMaterialStoredData: nextRecords,
493+ },
494+ value: nextRecords.length !== state.secretMaterialStoredData.length,
495+ } satisfies StateMutationResult<boolean>;
496+ }),
497+ },
435498 executions: {
436499 listByScope: (scopeId: Execution["scopeId"]) =>
437500 stateManager.read((state) =>
packages/platform/sdk-file/src/index.ts+3−1
@@ -25,9 +25,9 @@ import type { · 24 unmodified lines
2525 } from "@executor/platform-sdk/schema";
2626 import {
2727 type ExecutorRuntime,
28+ emptyExecutorPluginRegistry,
2829 type ExecutorRuntimeOptions,
2930 type ResolveSecretMaterial,
30− emptyExecutorPluginRegistry,
3131 } from "@executor/platform-sdk/runtime";
3232 import * as Effect from "effect/Effect";
3333 import {
@@ -283,6 +283,8 @@ export const createLocalExecutorRepositoriesEffect = ( · 249 unmodified lines
283283 },
284284 secrets: {
285285 secretStores: executorStateStorage.executorState.secretStores,
286+ secretMaterialStoredData:
287+ executorStateStorage.executorState.secretMaterialStoredData,
286288 ...executorStateStorage.executorState.secretMaterials,
287289 resolve: resolveSecretMaterial,
288290 store: createDefaultSecretMaterialStorer({
packages/platform/sdk-file/src/secret-material-providers.ts+14−7
@@ -255,15 +255,17 @@ export const createDefaultSecretMaterialStorer = (input: { · 254 unmodified lines
255255 const material: SecretMaterial = {
256256 id: `sec_${randomUUID()}` as SecretMaterial["id"],
257257 storeId: store.id,
258− handle: created.handle,
259258 name: created.name,
260259 purpose,
261− value: created.value ?? null,
262260 createdAt: now,
263261 updatedAt: now,
264262 };
265263
266264 yield* input.executorState.secretMaterials.upsert(material);
265+ yield* input.executorState.secretMaterialStoredData.upsert({
266+ secretId: material.id,
267+ data: created.secretStored,
268+ });
267269
268270 return {
269271 secretId: material.id,
@@ -309,12 +311,16 @@ export const createDefaultSecretMaterialUpdater = (input: { · 39 unmodified lines
309311
310312 const nextMaterial: SecretMaterial = {
311313 ...material,
312− ...(updated.handle ? { handle: updated.handle } : {}),
313314 name: updated.name,
314− value: updated.value ?? material.value,
315315 updatedAt: Date.now(),
316316 };
317317 yield* input.executorState.secretMaterials.upsert(nextMaterial);
318+ if (updated.secretStored !== undefined) {
319+ yield* input.executorState.secretMaterialStoredData.upsert({
320+ secretId: nextMaterial.id,
321+ data: updated.secretStored,
322+ });
323+ }
318324
319325 return {
320326 id: nextMaterial.id,
@@ -359,6 +365,7 @@ export const createDefaultSecretMaterialDeleter = (input: { · 38 unmodified lines
359365 return false;
360366 }
361367
368+ yield* input.executorState.secretMaterialStoredData.removeBySecretId(material.id);
362369 return yield* input.executorState.secretMaterials.removeById(material.id);
363370 });
364371 };
@@ -442,6 +449,7 @@ export const SecretMaterialLive = (input: { · 77 unmodified lines
442449 SecretMaterialResolverLive(input),
443450 SecretMaterialStorerLive({
444451 executorState: input.executorState,
452+ pluginRegistry: input.pluginRegistry,
445453 resolveSecretMaterial:
446454 input.resolveSecretMaterial
447455 ?? createDefaultSecretMaterialResolver({
@@ -449,11 +457,11 @@ export const SecretMaterialLive = (input: { · 1 unmodified line
449457 pluginRegistry: input.pluginRegistry,
450458 keychainServiceName: input.keychainServiceName,
451459 }),
452− pluginRegistry: input.pluginRegistry,
453460 keychainServiceName: input.keychainServiceName,
454461 }),
455462 SecretMaterialDeleterLive({
456463 executorState: input.executorState,
464+ pluginRegistry: input.pluginRegistry,
457465 resolveSecretMaterial:
458466 input.resolveSecretMaterial
459467 ?? createDefaultSecretMaterialResolver({
@@ -461,11 +469,11 @@ export const SecretMaterialLive = (input: { · 1 unmodified line
461469 pluginRegistry: input.pluginRegistry,
462470 keychainServiceName: input.keychainServiceName,
463471 }),
464− pluginRegistry: input.pluginRegistry,
465472 keychainServiceName: input.keychainServiceName,
466473 }),
467474 SecretMaterialUpdaterLive({
468475 executorState: input.executorState,
476+ pluginRegistry: input.pluginRegistry,
469477 resolveSecretMaterial:
470478 input.resolveSecretMaterial
471479 ?? createDefaultSecretMaterialResolver({
@@ -473,7 +481,6 @@ export const SecretMaterialLive = (input: { · 1 unmodified line
473481 pluginRegistry: input.pluginRegistry,
474482 keychainServiceName: input.keychainServiceName,
475483 }),
476− pluginRegistry: input.pluginRegistry,
477484 keychainServiceName: input.keychainServiceName,
478485 }),
479486 );
packages/platform/sdk/src/backend.ts+7−0
@@ -176,6 +176,13 @@ const toSecretsBackend = ( · 175 unmodified lines
176176 toOptionEffect(input.secretStores.updateById(id, patch)),
177177 removeById: (id) => toEffect(input.secretStores.removeById(id)),
178178 },
179+ secretMaterialStoredData: {
180+ getBySecretId: (secretId) =>
181+ toOptionEffect(input.secretMaterialStoredData.getBySecretId(secretId)),
182+ upsert: (record) => toEffect(input.secretMaterialStoredData.upsert(record)),
183+ removeBySecretId: (secretId) =>
184+ toEffect(input.secretMaterialStoredData.removeBySecretId(secretId)),
185+ },
179186 getById: (id) => toOptionEffect(input.getById(id)),
180187 listAll: () => toEffect(input.listAll()),
181188 upsert: (material) => toEffect(input.upsert(material)),
packages/platform/sdk/src/local/secret-stores.ts+8−8
@@ -82,7 +82,7 @@ const getSecretStoreContributionOption = (kind: string) => · 81 unmodified lines
8282 try {
8383 return Option.some(getSecretStoreContribution(pluginRegistry, kind));
8484 } catch {
85− return Option.none<typeof getSecretStoreContribution extends (registry: any, kind: string) => infer T ? T : never>();
85+ return Option.none<any>();
8686 }
8787 });
8888
@@ -125,10 +125,9 @@ const toSecretStoreContract = ( · 36 unmodified lines
125125
126126 const createImportedSecretRecord = (input: {
127127 store: SecretStore;
128− handle: string;
128+ secretStored: unknown;
129129 name: string | null;
130130 purpose: SecretMaterialPurpose;
131− value?: string | null;
132131 }) =>
133132 Effect.gen(function* () {
134133 const executorState = yield* ExecutorStateStore;
@@ -136,15 +135,17 @@ const createImportedSecretRecord = (input: { · 1 unmodified line
136135 const material: SecretMaterial = {
137136 id: SecretMaterialIdSchema.make(`sec_${crypto.randomUUID()}`),
138137 storeId: input.store.id,
139− handle: input.handle,
140138 name: input.name,
141139 purpose: input.purpose,
142− value: input.value ?? null,
143140 createdAt: now,
144141 updatedAt: now,
145142 };
146143
147144 yield* executorState.secretMaterials.upsert(material);
145+ yield* executorState.secretMaterialStoredData.upsert({
146+ secretId: material.id,
147+ data: input.secretStored,
148+ });
148149
149150 return {
150151 id: material.id,
@@ -384,7 +385,7 @@ export const browseLocalSecretStore = (input: { · 233 unmodified lines
384385 cause instanceof Error ? cause.message : "Failed browsing secret store.",
385386 ),
386387 ),
387− Effect.map((result) => result satisfies BrowseSecretStoreResult),
388+ Effect.map((result) => result as BrowseSecretStoreResult),
388389 );
389390 });
390391
@@ -437,9 +438,8 @@ export const importLocalSecretFromStore = (input: { · 46 unmodified lines
437438
438439 return yield* createImportedSecretRecord({
439440 store,
440− handle: created.handle,
441+ secretStored: created.secretStored,
441442 name: created.name,
442443 purpose: input.payload.purpose ?? "auth_material",
443− value: created.value,
444444 });
445445 });
packages/platform/sdk/src/plugins.ts+63−25
@@ -1,4 +1,5 @@
11 import * as Effect from "effect/Effect";
2+import * as Option from "effect/Option";
23
34 import type { SourceCatalogSyncResult } from "@executor/source-core";
45 import type {
@@ -21,6 +22,9 @@ import type { · 16 unmodified lines
2122 } from "@executor/source-core";
2223 import type * as Schema from "effect/Schema";
2324 import { runtimeEffectError } from "./runtime/effect-errors";
25+import {
26+ ExecutorStateStore,
27+} from "./runtime/executor-state-store";
2428 import { ScopeConfigStore } from "./runtime/scope/storage";
2529
2630 export type PluginCleanup = {
@@ -329,6 +333,7 @@ export type ExecutorSecretStorePluginDefinition< · 302 unmodified lines
329333 TConnectInput,
330334 TStoreConfig,
331335 TStored,
336+ TSecretStored,
332337 _TUpdateInput extends {
333338 storeId: string;
334339 config: TStoreConfig;
@@ -369,6 +374,7 @@ export type ExecutorSecretStorePluginDefinition< · 34 unmodified lines
369374 }) => Effect.Effect<void, Error, any>;
370375 resolveSecret: (input: {
371376 secret: SecretMaterial;
377+ secretStored: TSecretStored;
372378 store: SecretStore;
373379 stored: TStored | null;
374380 context?: {
@@ -382,23 +388,23 @@ export type ExecutorSecretStorePluginDefinition< · 7 unmodified lines
382388 value: string;
383389 name?: string | null;
384390 }) => Effect.Effect<{
385− handle: string;
386391 name: string | null;
387− value?: string | null;
392+ secretStored: TSecretStored;
388393 }, Error, any>;
389394 updateSecret?: (input: {
390395 secret: SecretMaterial;
396+ secretStored: TSecretStored;
391397 store: SecretStore;
392398 stored: TStored | null;
393399 name?: string | null;
394400 value?: string;
395401 }) => Effect.Effect<{
396− handle?: string;
397402 name: string | null;
398− value?: string | null;
403+ secretStored?: TSecretStored;
399404 }, Error, any>;
400405 deleteSecret?: (input: {
401406 secret: SecretMaterial;
407+ secretStored: TSecretStored;
402408 store: SecretStore;
403409 stored: TStored | null;
404410 }) => Effect.Effect<boolean, Error, any>;
@@ -417,9 +423,8 @@ export type ExecutorSecretStorePluginDefinition< · 12 unmodified lines
417423 purpose: SecretMaterialPurpose;
418424 name?: string | null;
419425 }) => Effect.Effect<{
420− handle: string;
421426 name: string | null;
422− value?: string | null;
427+ secretStored: TSecretStored;
423428 }, Error, any>;
424429 capabilities?: (input: {
425430 store: SecretStore;
@@ -479,6 +484,7 @@ export type ExecutorSecretStorePluginInput< · 53 unmodified lines
479484 TConnectInput = unknown,
480485 TStoreConfig = unknown,
481486 TStored = unknown,
487+ TSecretStored = unknown,
482488 TUpdateInput extends {
483489 storeId: string;
484490 config: TStoreConfig;
@@ -494,6 +500,7 @@ export type ExecutorSecretStorePluginInput< · 9 unmodified lines
494500 TConnectInput,
495501 TStoreConfig,
496502 TStored,
503+ TSecretStored,
497504 TUpdateInput
498505 >;
499506 extendExecutor?: (input: ExecutorSdkPluginContext & {
@@ -771,7 +778,7 @@ const createExecutorSourcePluginApi = < · 271 unmodified lines
771778 const loadStoreOfKind = (
772779 storeId: SecretStore["id"],
773780 input: {
774− definition: ExecutorSecretStorePluginDefinition<any, any, any, any, any>;
781+ definition: ExecutorSecretStorePluginDefinition<any, any, any, any, any, any>;
775782 host: ExecutorSecretStorePluginInternalHost;
776783 },
777784 ): Effect.Effect<SecretStore, Error, any> =>
@@ -787,11 +794,28 @@ const loadStoreOfKind = ( · 9 unmodified lines
787794 return store;
788795 });
789796
797+const loadSecretMaterialStoredData = <TSecretStored>(
798+ secretId: SecretMaterial["id"],
799+): Effect.Effect<TSecretStored, Error, any> =>
800+ Effect.gen(function* () {
801+ const executorState = yield* ExecutorStateStore;
802+ const stored = yield* executorState.secretMaterialStoredData.getBySecretId(secretId);
803+ if (Option.isNone(stored)) {
804+ return yield* runtimeEffectError(
805+ "plugins",
806+ `Secret storage missing for ${secretId}.`,
807+ );
808+ }
809+
810+ return stored.value.data as TSecretStored;
811+ });
812+
790813 const createExecutorSecretStorePluginApi = <
791814 TAddInput,
792815 TConnectInput,
793816 TStoreConfig,
794817 TStored,
818+ TSecretStored,
795819 _TUpdateInput extends {
796820 storeId: string;
797821 config: TStoreConfig;
@@ -802,6 +826,7 @@ const createExecutorSecretStorePluginApi = < · 4 unmodified lines
802826 TConnectInput,
803827 TStoreConfig,
804828 TStored,
829+ TSecretStored,
805830 _TUpdateInput
806831 >,
807832 host: ExecutorSecretStorePluginInternalHost,
@@ -962,9 +987,8 @@ type ExecutorSecretStoreContribution<TInput = unknown> = { · 154 unmodified lines
962987 value: string;
963988 name?: string | null;
964989 }) => Effect.Effect<{
965− handle: string;
966990 name: string | null;
967− value?: string | null;
991+ secretStored: unknown;
968992 }, Error, any>;
969993 updateSecret: (input: {
970994 secret: SecretMaterial;
@@ -972,9 +996,8 @@ type ExecutorSecretStoreContribution<TInput = unknown> = { · 1 unmodified line
972996 name?: string | null;
973997 value?: string;
974998 }) => Effect.Effect<{
975− handle?: string;
976999 name: string | null;
977− value?: string | null;
1000+ secretStored?: unknown;
9781001 }, Error, any>;
9791002 deleteSecret: (input: {
9801003 secret: SecretMaterial;
@@ -993,9 +1016,8 @@ type ExecutorSecretStoreContribution<TInput = unknown> = { · 12 unmodified lines
9931016 purpose: SecretMaterialPurpose;
9941017 name?: string | null;
9951018 }) => Effect.Effect<{
996− handle: string;
9971019 name: string | null;
998− value?: string | null;
1020+ secretStored: unknown;
9991021 }, Error, any>;
10001022 };
10011023
@@ -1076,6 +1098,7 @@ const createExecutorSecretStoreContribution = < · 74 unmodified lines
10761098 TConnectInput,
10771099 TStoreConfig,
10781100 TStored,
1101+ TSecretStored,
10791102 TUpdateInput extends {
10801103 storeId: string;
10811104 config: TStoreConfig;
@@ -1086,6 +1109,7 @@ const createExecutorSecretStoreContribution = < · 4 unmodified lines
10861109 TConnectInput,
10871110 TStoreConfig,
10881111 TStored,
1112+ TSecretStored,
10891113 TUpdateInput
10901114 >,
10911115 ): ExecutorSecretStoreContribution<TAddInput> => ({
@@ -1153,13 +1177,17 @@ const createExecutorSecretStoreContribution = < · 61 unmodified lines
11531177 ),
11541178 resolveSecret: ({ secret, store, context }) =>
11551179 Effect.flatMap(
1156− definition.storage.get({
1157− scopeId: store.scopeId,
1158− storeId: store.id,
1180+ Effect.all({
1181+ stored: definition.storage.get({
1182+ scopeId: store.scopeId,
1183+ storeId: store.id,
1184+ }),
1185+ secretStored: loadSecretMaterialStoredData<TSecretStored>(secret.id),
11591186 }),
1160− (stored) =>
1187+ ({ stored, secretStored }) =>
11611188 definition.store.resolveSecret({
11621189 secret,
1190+ secretStored,
11631191 store,
11641192 stored,
11651193 context,
@@ -1188,13 +1216,17 @@ const createExecutorSecretStoreContribution = < · 22 unmodified lines
11881216 updateSecret: ({ secret, store, name, value }) =>
11891217 definition.store.updateSecret
11901218 ? Effect.flatMap(
1191− definition.storage.get({
1192− scopeId: store.scopeId,
1193− storeId: store.id,
1219+ Effect.all({
1220+ stored: definition.storage.get({
1221+ scopeId: store.scopeId,
1222+ storeId: store.id,
1223+ }),
1224+ secretStored: loadSecretMaterialStoredData<TSecretStored>(secret.id),
11941225 }),
1195− (stored) =>
1226+ ({ stored, secretStored }) =>
11961227 definition.store.updateSecret!({
11971228 secret,
1229+ secretStored,
11981230 store,
11991231 stored,
12001232 name,
@@ -1208,13 +1240,17 @@ const createExecutorSecretStoreContribution = < · 7 unmodified lines
12081240 deleteSecret: ({ secret, store }) =>
12091241 definition.store.deleteSecret
12101242 ? Effect.flatMap(
1211− definition.storage.get({
1212− scopeId: store.scopeId,
1213− storeId: store.id,
1243+ Effect.all({
1244+ stored: definition.storage.get({
1245+ scopeId: store.scopeId,
1246+ storeId: store.id,
1247+ }),
1248+ secretStored: loadSecretMaterialStoredData<TSecretStored>(secret.id),
12141249 }),
1215− (stored) =>
1250+ ({ stored, secretStored }) =>
12161251 definition.store.deleteSecret!({
12171252 secret,
1253+ secretStored,
12181254 store,
12191255 stored,
12201256 }),
@@ -1377,6 +1413,7 @@ export const defineExecutorSecretStorePlugin = < · 156 unmodified lines
13771413 TConnectInput,
13781414 TStoreConfig,
13791415 TStored,
1416+ TSecretStored,
13801417 TUpdateInput extends {
13811418 storeId: string;
13821419 config: TStoreConfig;
@@ -1389,6 +1426,7 @@ export const defineExecutorSecretStorePlugin = < · 6 unmodified lines
13891426 TConnectInput,
13901427 TStoreConfig,
13911428 TStored,
1429+ TSecretStored,
13921430 TUpdateInput,
13931431 TExtension
13941432 >,
packages/platform/sdk/src/runtime/execution/live.test.ts+2−3
@@ -14,9 +14,8 @@ describe("live-execution", () => { · 13 unmodified lines
1414 action: "accept",
1515 content: {
1616 authKind: "bearer",
17− tokenRef: {
18− providerId: "local",
19− handle: "sec_123",
17+ tokenSecretRef: {
18+ secretId: "sec_123",
2019 },
2120 },
2221 });
packages/platform/sdk/src/runtime/execution/live.ts+4−1
@@ -71,7 +71,10 @@ const serializeJson = (value: unknown): string | null => { · 70 unmodified lines
7171 };
7272
7373 const SENSITIVE_INTERACTION_CONTENT_KEYS = new Set([
74− "tokenRef",
74+ "tokenSecretRef",
75+ "accessTokenRef",
76+ "refreshTokenRef",
77+ "clientSecretRef",
7578 "tokenSecretMaterialId",
7679 ]);
7780
packages/platform/sdk/src/runtime/executor-state-store.ts+21−1
@@ -18,6 +18,11 @@ type SecretMaterialSummary = { · 17 unmodified lines
1818 updatedAt: number;
1919 };
2020
21+export type SecretMaterialStoredDataRecord = {
22+ secretId: string;
23+ data: unknown;
24+};
25+
2126 type SecretStoreSummary = {
2227 id: string;
2328 scopeId: string;
@@ -56,7 +61,7 @@ export type ExecutorStateStoreShape = { · 32 unmodified lines
5661 upsert: (material: SecretMaterial) => Effect.Effect<void, Error, never>;
5762 updateById: (
5863 id: SecretMaterial["id"],
59− update: { name?: string | null; value?: string },
64+ update: { name?: string | null },
6065 ) => Effect.Effect<
6166 import("effect/Option").Option<SecretMaterialSummary>,
6267 Error,
@@ -64,6 +69,21 @@ export type ExecutorStateStoreShape = { · 1 unmodified line
6469 >;
6570 removeById: (id: SecretMaterial["id"]) => Effect.Effect<boolean, Error, never>;
6671 };
72+ secretMaterialStoredData: {
73+ getBySecretId: (
74+ secretId: SecretMaterial["id"],
75+ ) => Effect.Effect<
76+ import("effect/Option").Option<SecretMaterialStoredDataRecord>,
77+ Error,
78+ never
79+ >;
80+ upsert: (
81+ record: SecretMaterialStoredDataRecord,
82+ ) => Effect.Effect<void, Error, never>;
83+ removeBySecretId: (
84+ secretId: SecretMaterial["id"],
85+ ) => Effect.Effect<boolean, Error, never>;
86+ };
6787 executions: {
6888 listByScope: (
6989 scopeId: Execution["scopeId"],
packages/platform/sdk/src/runtime/index.ts+9−2
@@ -112,7 +112,6 @@ export * from "./execution/service"; · 111 unmodified lines
112112 export * from "./source-artifacts";
113113 export * from "./scope-config";
114114 export * from "./scope-state";
115−export * from "./scope/config-secrets";
116115 export * from "./sources/source-store/config";
117116 export * from "./sources/source-store/plugin-local-config";
118117 export {
@@ -244,6 +243,7 @@ export const prewarmWorkspaceSourceCatalogToolIndex = (input: { · 125 unmodified lines
244243 export type RuntimeSecretsStorageServices =
245244 & {
246245 secretStores: ExecutorStateStoreShape["secretStores"];
246+ secretMaterialStoredData: ExecutorStateStoreShape["secretMaterialStoredData"];
247247 }
248248 & ExecutorStateStoreShape["secretMaterials"]
249249 & RuntimeSecretMaterialServices;
@@ -343,7 +343,14 @@ const toExecutorStateStoreShape = ( · 93 unmodified lines
343343 input: RuntimeStorageServices,
344344 ): ExecutorStateStoreShape => ({
345345 secretStores: input.secrets.secretStores,
346− secretMaterials: input.secrets,
346+ secretMaterials: {
347+ getById: input.secrets.getById,
348+ listAll: input.secrets.listAll,
349+ upsert: input.secrets.upsert,
350+ updateById: input.secrets.updateById,
351+ removeById: input.secrets.removeById,
352+ },
353+ secretMaterialStoredData: input.secrets.secretMaterialStoredData,
347354 executions: input.executions.runs,
348355 executionInteractions: input.executions.interactions,
349356 executionSteps: input.executions.steps,
packages/platform/sdk/src/runtime/scope/config-secrets.ts+0−9
@@ -1,9 +0,0 @@
1−export const CONFIG_SECRET_PROVIDER_PREFIX = "config:";
2−
3−export const toConfigSecretProviderId = (providerAlias: string): string =>
4− `${CONFIG_SECRET_PROVIDER_PREFIX}${providerAlias}`;
5−
6−export const fromConfigSecretProviderId = (providerId: string): string | null =>
7− providerId.startsWith(CONFIG_SECRET_PROVIDER_PREFIX)
8− ? providerId.slice(CONFIG_SECRET_PROVIDER_PREFIX.length)
9− : null;
packages/platform/sdk/src/runtime/sources/source-store/plugin-local-config.ts+0−1
@@ -3,7 +3,6 @@ import type { · 2 unmodified lines
33 Source,
44 } from "#schema";
55 import * as Schema from "effect/Schema";
6−
76 import {
87 cloneJson,
98 configSourceBaseFromLocalSource,
packages/platform/sdk/src/schema/models/local-config.ts+0−73
@@ -8,54 +8,6 @@ export const LocalExecutorRuntimeSchema = Schema.Literal( · 7 unmodified lines
88 "deno",
99 );
1010
11−export const LocalConfigSecretProviderSourceSchema = Schema.Literal(
12− "env",
13− "file",
14− "exec",
15− "params",
16−);
17−
18−export const LocalConfigEnvSecretProviderSchema = Schema.Struct({
19− source: Schema.Literal("env"),
20−});
21−
22−export const LocalConfigFileSecretProviderModeSchema = Schema.Literal(
23− "singleValue",
24− "json",
25−);
26−
27−export const LocalConfigFileSecretProviderSchema = Schema.Struct({
28− source: Schema.Literal("file"),
29− path: Schema.String,
30− mode: Schema.optional(LocalConfigFileSecretProviderModeSchema),
31−});
32−
33−export const LocalConfigExecSecretProviderSchema = Schema.Struct({
34− source: Schema.Literal("exec"),
35− command: Schema.String,
36− args: Schema.optional(Schema.Array(Schema.String)),
37− env: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String })),
38− allowSymlinkCommand: Schema.optional(Schema.Boolean),
39− trustedDirs: Schema.optional(Schema.Array(Schema.String)),
40−});
41−
42−export const LocalConfigSecretProviderSchema = Schema.Union(
43− LocalConfigEnvSecretProviderSchema,
44− LocalConfigFileSecretProviderSchema,
45− LocalConfigExecSecretProviderSchema,
46−);
47−
48−export const LocalConfigExplicitSecretRefSchema = Schema.Struct({
49− source: LocalConfigSecretProviderSourceSchema,
50− provider: Schema.String,
51− id: Schema.String,
52−});
53−
54−export const LocalConfigSecretInputSchema = Schema.Union(
55− Schema.String,
56− LocalConfigExplicitSecretRefSchema,
57−);
58−
5911 export const LocalConfigSourceConnectionSchema = Schema.Struct({
6012 endpoint: Schema.optional(Schema.NullOr(Schema.String)),
6113 auth: Schema.optional(Schema.Unknown),
@@ -90,22 +42,6 @@ export const LocalConfigPolicySchema = Schema.Struct({ · 28 unmodified lines
9042 priority: Schema.optional(Schema.Number),
9143 });
9244
93−export const LocalConfigSecretsSchema = Schema.Struct({
94− providers: Schema.optional(
95− Schema.Record({
96− key: Schema.String,
97− value: LocalConfigSecretProviderSchema,
98− }),
99− ),
100− defaults: Schema.optional(
101− Schema.Struct({
102− env: Schema.optional(Schema.String),
103− file: Schema.optional(Schema.String),
104− exec: Schema.optional(Schema.String),
105− }),
106− ),
107−});
108−
10945 export const LocalConfigWorkspaceSchema = Schema.Struct({
11046 name: Schema.optional(Schema.String),
11147 });
@@ -125,17 +61,8 @@ export const LocalExecutorConfigSchema = Schema.Struct({ · 13 unmodified lines
12561 value: LocalConfigPolicySchema,
12662 }),
12763 ),
128− secrets: Schema.optional(LocalConfigSecretsSchema),
12964 });
13065
131−export type LocalConfigSecretProviderSource =
132− typeof LocalConfigSecretProviderSourceSchema.Type;
133−export type LocalConfigSecretProvider =
134− typeof LocalConfigSecretProviderSchema.Type;
135−export type LocalConfigExplicitSecretRef =
136− typeof LocalConfigExplicitSecretRefSchema.Type;
137−export type LocalConfigSecretInput = typeof LocalConfigSecretInputSchema.Type;
13866 export type LocalConfigPolicy = typeof LocalConfigPolicySchema.Type;
139−export type LocalConfigSecrets = typeof LocalConfigSecretsSchema.Type;
14067 export type LocalExecutorRuntime = typeof LocalExecutorRuntimeSchema.Type;
14168 export type LocalExecutorConfig = typeof LocalExecutorConfigSchema.Type;
packages/platform/sdk/src/schema/models/secret-material.ts+0−2
@@ -22,8 +22,6 @@ export const SecretMaterialSchema = Schema.Struct({ · 21 unmodified lines
2222 name: Schema.NullOr(Schema.String),
2323 purpose: SecretMaterialPurposeSchema,
2424 storeId: SecretStoreIdSchema,
25− handle: Schema.String,
26− value: Schema.NullOr(Schema.String),
2725 createdAt: TimestampMsSchema,
2826 updatedAt: TimestampMsSchema,
2927 });
packages/sources/core/src/source-models.ts+1−2
@@ -1,8 +1,7 @@
11 import * as Schema from "effect/Schema";
22
33 export const SecretRefSchema = Schema.Struct({
4− providerId: Schema.String,
5− handle: Schema.String,
4+ secretId: Schema.String,
65 });
76
87 export const CredentialSlotSchema = Schema.Literal("runtime", "import");
plugins/google-discovery/sdk/index.ts+0−1
@@ -557,7 +557,6 @@ const googleDiscoveryStoredSourceDataFromLocalConfig = (input: { · 556 unmodified lines
557557 if (Option.isSome(current)) {
558558 return current.value.config;
559559 }
560−
561560 throw new Error("Unsupported Google Discovery local source config.");
562561 };
563562
plugins/graphql/sdk/index.ts+0−1
@@ -342,7 +342,6 @@ const graphqlStoredSourceDataFromLocalConfig = (input: { · 341 unmodified lines
342342 if (Option.isSome(current)) {
343343 return normalizeStoredSourceData(current.value.config);
344344 }
345−
346345 throw new Error("Unsupported GraphQL local source config.");
347346 };
348347
plugins/keychain-secret-store/sdk/index.ts+103−275
@@ -1,5 +1,7 @@
11 import { randomUUID } from "node:crypto";
2−import { spawn } from "node:child_process";
2+import {
3+ Entry,
4+} from "@napi-rs/keyring";
35
46 import * as Effect from "effect/Effect";
57
@@ -13,16 +15,13 @@ import { · 7 unmodified lines
1315 export const KEYCHAIN_SECRET_STORE_KIND = "keychain";
1416 export const KEYCHAIN_SECRET_STORE_ID = "sts_builtin_keychain";
1517
18+type KeychainSecretStoredData = {
19+ account: string;
20+};
21+
1622 const DEFAULT_KEYCHAIN_SERVICE_NAME = "executor";
17−const KEYCHAIN_COMMAND_TIMEOUT_MS = 5_000;
1823 const KEYCHAIN_SERVICE_NAME_ENV = "EXECUTOR_KEYCHAIN_SERVICE_NAME";
1924
20−type SpawnResult = {
21− exitCode: number;
22− stdout: string;
23− stderr: string;
24−};
25−
2625 const toError = (cause: unknown): Error =>
2726 cause instanceof Error ? cause : new Error(String(cause));
2827
@@ -35,280 +34,99 @@ const trimOrNull = (value: string | null | undefined): string | null => { · 6 unmodified lines
3534 return trimmed.length > 0 ? trimmed : null;
3635 };
3736
38−const ensureNonEmptyString = (value: string | undefined | null): string | null => {
39− const trimmed = value?.trim();
40− return trimmed && trimmed.length > 0 ? trimmed : null;
41−};
42−
4337 const resolveKeychainServiceName = (value: string | undefined): string =>
4438 trimOrNull(value)
4539 ?? trimOrNull(process.env[KEYCHAIN_SERVICE_NAME_ENV])
4640 ?? DEFAULT_KEYCHAIN_SERVICE_NAME;
4741
48−const runCommand = (input: {
49− command: string;
50− args: ReadonlyArray<string>;
51− stdin?: string;
52− operation: string;
53− timeoutMs?: number;
54−}): Effect.Effect<SpawnResult, Error, never> =>
55− Effect.tryPromise({
56− try: () =>
57− new Promise<SpawnResult>((resolve, reject) => {
58− const child = spawn(input.command, [...input.args], {
59− stdio: "pipe",
60− env: process.env,
61− });
62−
63− let stdout = "";
64− let stderr = "";
65− let settled = false;
66− const timeout = input.timeoutMs === undefined
67− ? null
68− : setTimeout(() => {
69− if (settled) {
70− return;
71− }
72−
73− settled = true;
74− child.kill("SIGKILL");
75− reject(
76− new Error(
77− `${input.operation}: '${input.command}' timed out after ${input.timeoutMs}ms`,
78− ),
79− );
80− }, input.timeoutMs);
81−
82− child.stdout.on("data", (chunk) => {
83− stdout += chunk.toString("utf8");
84− });
85−
86− child.stderr.on("data", (chunk) => {
87− stderr += chunk.toString("utf8");
88− });
42+const isSupportedPlatform = () =>
43+ process.platform === "darwin"
44+ || process.platform === "linux"
45+ || process.platform === "win32";
8946
90− child.on("error", (error) => {
91− if (settled) {
92− return;
93− }
94− settled = true;
95− if (timeout) {
96− clearTimeout(timeout);
97− }
98− reject(error);
99− });
47+const keychainDisplayName = () =>
48+ process.platform === "darwin"
49+ ? "macOS Keychain"
50+ : process.platform === "win32"
51+ ? "Windows Credential Manager"
52+ : "Desktop Keyring";
10053
101− child.on("close", (code) => {
102− if (settled) {
103− return;
104− }
105− settled = true;
106− if (timeout) {
107− clearTimeout(timeout);
108− }
109− resolve({
110− exitCode: code ?? 0,
111− stdout,
112− stderr,
113− });
114− });
115−
116− if (input.stdin !== undefined) {
117− child.stdin.write(input.stdin);
118− }
54+const createKeyringEntry = (input: {
55+ providerHandle: string;
56+ keychainServiceName: string;
57+}) =>
58+ Effect.try({
59+ try: () => {
60+ if (!isSupportedPlatform()) {
61+ throw runtimeEffectError(
62+ "plugin-keychain-secret-store",
63+ `system-keyring: unsupported on platform '${process.platform}'`,
64+ );
65+ }
11966
120− child.stdin.end();
121− }),
67+ return new Entry(input.keychainServiceName, input.providerHandle);
68+ },
12269 catch: toError,
12370 });
12471
125−const ensureCommandSuccess = (input: {
126− result: SpawnResult;
127− operation: string;
128− message: string;
129−}): Effect.Effect<SpawnResult, Error, never> => {
130− if (input.result.exitCode === 0) {
131− return Effect.succeed(input.result);
132− }
133−
134− const details = ensureNonEmptyString(input.result.stderr)
135− ?? ensureNonEmptyString(input.result.stdout)
136− ?? "command returned non-zero exit code";
137−
138− return Effect.fail(
139− runtimeEffectError(
140− "plugin-keychain-secret-store",
141− `${input.operation}: ${input.message}: ${details}`,
142− ),
143− );
144−};
145−
14672 const readKeychainSecretValue = (input: {
14773 providerHandle: string;
14874 keychainServiceName: string;
149−}) => {
150− switch (process.platform) {
151− case "darwin":
152− return runCommand({
153− command: "security",
154− args: [
155− "find-generic-password",
156− "-a",
157− input.providerHandle,
158− "-s",
159− input.keychainServiceName,
160− "-w",
161− ],
162− operation: "keychain.get",
163− timeoutMs: KEYCHAIN_COMMAND_TIMEOUT_MS,
75+}) =>
76+ Effect.flatMap(
77+ createKeyringEntry(input),
78+ (entry) =>
79+ Effect.try({
80+ try: () => entry.getPassword(),
81+ catch: toError,
16482 }).pipe(
165− Effect.flatMap((result) =>
166− ensureCommandSuccess({
167− result,
168− operation: "keychain.get",
169− message: "Failed loading secret from macOS keychain",
170− })),
171− Effect.map((result) => result.stdout.trimEnd()),
172− );
173− case "linux":
174− return runCommand({
175− command: "secret-tool",
176− args: [
177− "lookup",
178− "service",
179− input.keychainServiceName,
180− "account",
181− input.providerHandle,
182− ],
183− operation: "keychain.get",
184− timeoutMs: KEYCHAIN_COMMAND_TIMEOUT_MS,
185− }).pipe(
186− Effect.flatMap((result) =>
187− ensureCommandSuccess({
188− result,
189− operation: "keychain.get",
190− message: "Failed loading secret from desktop keyring",
191− })),
192− Effect.map((result) => result.stdout.trimEnd()),
193− );
194− default:
195− return Effect.fail(
196− runtimeEffectError(
197− "plugin-keychain-secret-store",
198− `keychain.get: unsupported on platform '${process.platform}'`,
199− ),
200− );
201− }
202−};
83+ Effect.flatMap((value) =>
84+ value !== null
85+ ? Effect.succeed(value)
86+ : Effect.fail(
87+ runtimeEffectError(
88+ "plugin-keychain-secret-store",
89+ `keychain.get: secret not found for service '${input.keychainServiceName}' and account '${input.providerHandle}'`,
90+ ),
91+ )),
92+ ),
93+ );
20394
20495 const writeKeychainSecretValue = (input: {
20596 providerHandle: string;
20697 name?: string | null;
20798 value: string;
20899 keychainServiceName: string;
209−}) => {
210− const secretName = trimOrNull(input.name);
211−
212− switch (process.platform) {
213− case "darwin":
214− return runCommand({
215− command: "security",
216− args: [
217− "add-generic-password",
218− "-a",
219− input.providerHandle,
220− "-s",
221− input.keychainServiceName,
222− "-w",
223− input.value,
224− "-U",
225− ...(secretName ? ["-l", secretName] : []),
226− ],
227− operation: "keychain.put",
228− timeoutMs: KEYCHAIN_COMMAND_TIMEOUT_MS,
229− }).pipe(
230− Effect.flatMap((result) =>
231− ensureCommandSuccess({
232− result,
233− operation: "keychain.put",
234− message: "Failed storing secret in macOS keychain",
235− })),
236− Effect.asVoid,
237− );
238− case "linux":
239− return runCommand({
240− command: "secret-tool",
241− args: [
242− "store",
243− "--label",
244− secretName ?? input.keychainServiceName,
245− "service",
246− input.keychainServiceName,
247− "account",
248− input.providerHandle,
249− ],
250− stdin: input.value,
251− operation: "keychain.put",
252− timeoutMs: KEYCHAIN_COMMAND_TIMEOUT_MS,
253− }).pipe(
254− Effect.flatMap((result) =>
255− ensureCommandSuccess({
256− result,
257− operation: "keychain.put",
258− message: "Failed storing secret in desktop keyring",
259− })),
260− Effect.asVoid,
261− );
262− default:
263− return Effect.fail(
264− runtimeEffectError(
265− "plugin-keychain-secret-store",
266− `keychain.put: unsupported on platform '${process.platform}'`,
267− ),
268− );
269− }
270−};
100+}) =>
101+ Effect.flatMap(
102+ createKeyringEntry(input),
103+ (entry) =>
104+ Effect.try({
105+ try: () => entry.setPassword(input.value),
106+ catch: (cause) =>
107+ runtimeEffectError(
108+ "plugin-keychain-secret-store",
109+ `keychain.put: Failed storing secret in ${keychainDisplayName().toLowerCase()}: ${toError(cause).message}`,
110+ ),
111+ }).pipe(Effect.asVoid),
112+ );
271113
272114 const deleteKeychainSecretValue = (input: {
273115 providerHandle: string;
274116 keychainServiceName: string;
275−}) => {
276− switch (process.platform) {
277− case "darwin":
278− return runCommand({
279− command: "security",
280− args: [
281− "delete-generic-password",
282− "-a",
283− input.providerHandle,
284− "-s",
285− input.keychainServiceName,
286− ],
287− operation: "keychain.delete",
288− timeoutMs: KEYCHAIN_COMMAND_TIMEOUT_MS,
289− }).pipe(Effect.map((result) => result.exitCode === 0));
290− case "linux":
291− return runCommand({
292− command: "secret-tool",
293− args: [
294− "clear",
295− "service",
296− input.keychainServiceName,
297− "account",
298− input.providerHandle,
299− ],
300− operation: "keychain.delete",
301− timeoutMs: KEYCHAIN_COMMAND_TIMEOUT_MS,
302− }).pipe(Effect.map((result) => result.exitCode === 0));
303− default:
304− return Effect.fail(
305− runtimeEffectError(
306− "plugin-keychain-secret-store",
307− `keychain.delete: unsupported on platform '${process.platform}'`,
308− ),
309− );
310− }
311−};
117+}) =>
118+ Effect.flatMap(
119+ createKeyringEntry(input),
120+ (entry) =>
121+ Effect.try({
122+ try: () => entry.deletePassword(),
123+ catch: (cause) =>
124+ runtimeEffectError(
125+ "plugin-keychain-secret-store",
126+ `keychain.delete: Failed deleting secret from ${keychainDisplayName().toLowerCase()}: ${toError(cause).message}`,
127+ ),
128+ }),
129+ );
312130
313131 const builtinSecretStoreStorage = <TStored>(value: TStored) => ({
314132 get: () => Effect.succeed(value),
@@ -316,19 +134,29 @@ const builtinSecretStoreStorage = <TStored>(value: TStored) => ({ · 1 unmodified line
316134 remove: () => Effect.void,
317135 });
318136
319−export const keychainSecretStoreSdkPlugin = defineExecutorSecretStorePlugin({
137+export const keychainSecretStoreSdkPlugin = defineExecutorSecretStorePlugin<
138+ typeof KEYCHAIN_SECRET_STORE_KIND,
139+ unknown,
140+ { name: string },
141+ { kind: typeof KEYCHAIN_SECRET_STORE_KIND; name: string },
142+ {},
143+ KeychainSecretStoredData,
144+ {
145+ storeId: string;
146+ config: { kind: typeof KEYCHAIN_SECRET_STORE_KIND; name: string };
147+ }
148+>({
320149 key: KEYCHAIN_SECRET_STORE_KIND,
321150 secretStore: {
322151 kind: KEYCHAIN_SECRET_STORE_KIND,
323− displayName:
324− process.platform === "darwin" ? "macOS Keychain" : "Desktop Keyring",
152+ displayName: keychainDisplayName(),
325153 builtin: {
326154 storeId: KEYCHAIN_SECRET_STORE_ID,
327155 defaultPriority: 10,
328− enabled: () => process.platform === "darwin" || process.platform === "linux",
156+ enabled: isSupportedPlatform,
329157 createStore: () => ({
330158 kind: KEYCHAIN_SECRET_STORE_KIND,
331− name: process.platform === "darwin" ? "macOS Keychain" : "Desktop Keyring",
159+ name: keychainDisplayName(),
332160 status: "connected",
333161 enabled: true,
334162 }),
@@ -352,50 +180,50 @@ export const keychainSecretStoreSdkPlugin = defineExecutorSecretStorePlugin({ · 17 unmodified lines
352180 kind: KEYCHAIN_SECRET_STORE_KIND,
353181 name: store.name,
354182 }),
355− resolveSecret: ({ secret }) =>
183+ resolveSecret: ({ secretStored }) =>
356184 readKeychainSecretValue({
357− providerHandle: secret.handle,
185+ providerHandle: secretStored.account,
358186 keychainServiceName: resolveKeychainServiceName(undefined),
359187 }),
360188 createSecret: ({ value, name }) =>
361189 Effect.gen(function* () {
362− const providerHandle = randomUUID();
190+ const account = randomUUID();
363191 yield* writeKeychainSecretValue({
364− providerHandle,
192+ providerHandle: account,
365193 name,
366194 value,
367195 keychainServiceName: resolveKeychainServiceName(undefined),
368196 });
369197 return {
370− handle: providerHandle,
371198 name: trimOrNull(name),
372− value: null,
199+ secretStored: {
200+ account,
201+ } satisfies KeychainSecretStoredData,
373202 };
374203 }),
375− updateSecret: ({ secret, name, value }) =>
204+ updateSecret: ({ secret, secretStored, name, value }) =>
376205 Effect.gen(function* () {
377206 const keychainServiceName = resolveKeychainServiceName(undefined);
378207 const nextName = trimOrNull(name ?? secret.name);
379208 const nextValue = value
380209 ?? (yield* readKeychainSecretValue({
381− providerHandle: secret.handle,
210+ providerHandle: secretStored.account,
382211 keychainServiceName,
383212 }));
384213 yield* writeKeychainSecretValue({
385− providerHandle: secret.handle,
214+ providerHandle: secretStored.account,
386215 name: nextName,
387216 value: nextValue,
388217 keychainServiceName,
389218 });
390219 return {
391− handle: secret.handle,
392220 name: nextName,
393− value: null,
221+ secretStored,
394222 };
395223 }),
396− deleteSecret: ({ secret }) =>
224+ deleteSecret: ({ secretStored }) =>
397225 deleteKeychainSecretValue({
398− providerHandle: secret.handle,
226+ providerHandle: secretStored.account,
399227 keychainServiceName: resolveKeychainServiceName(undefined),
400228 }),
401229 capabilities: () => ({
plugins/keychain-secret-store/sdk/package.json+1−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
1313 },
1414 "dependencies": {
1515 "@executor/platform-sdk": "workspace:*",
16+ "@napi-rs/keyring": "^1.2.0",
1617 "effect": "catalog:"
1718 },
1819 "devDependencies": {
plugins/local-secret-store/sdk/index.ts+24−23
@@ -1,17 +1,16 @@
1−import { randomUUID } from "node:crypto";
2−
31 import * as Effect from "effect/Effect";
42
53 import {
64 defineExecutorSecretStorePlugin,
75 } from "@executor/platform-sdk/plugins";
8−import {
9− runtimeEffectError,
10−} from "@executor/platform-sdk/runtime";
116
127 export const LOCAL_SECRET_STORE_KIND = "local";
138 export const LOCAL_SECRET_STORE_ID = "sts_builtin_local";
149
10+type LocalSecretStoredData = {
11+ value: string;
12+};
13+
1514 const builtinSecretStoreStorage = <TStored>(value: TStored) => ({
1615 get: () => Effect.succeed(value),
1716 put: () => Effect.void,
@@ -27,7 +26,18 @@ const trimOrNull = (value: string | null | undefined): string | null => { · 9 unmodified lines
2726 return trimmed.length > 0 ? trimmed : null;
2827 };
2928
30−export const localSecretStoreSdkPlugin = defineExecutorSecretStorePlugin({
29+export const localSecretStoreSdkPlugin = defineExecutorSecretStorePlugin<
30+ typeof LOCAL_SECRET_STORE_KIND,
31+ unknown,
32+ { name: string },
33+ { kind: typeof LOCAL_SECRET_STORE_KIND; name: string },
34+ {},
35+ LocalSecretStoredData,
36+ {
37+ storeId: string;
38+ config: { kind: typeof LOCAL_SECRET_STORE_KIND; name: string };
39+ }
40+>({
3141 key: LOCAL_SECRET_STORE_KIND,
3242 secretStore: {
3343 kind: LOCAL_SECRET_STORE_KIND,
@@ -61,29 +71,20 @@ export const localSecretStoreSdkPlugin = defineExecutorSecretStorePlugin({ · 27 unmodified lines
6171 kind: LOCAL_SECRET_STORE_KIND,
6272 name: store.name,
6373 }),
64− resolveSecret: ({ secret }) => {
65− if (secret.value === null) {
66− return Effect.fail(
67− runtimeEffectError(
68− "plugin-local-secret-store",
69− `Local secret ${secret.id} does not have a stored value`,
70− ),
71− );
72− }
73−
74− return Effect.succeed(secret.value);
75− },
74+ resolveSecret: ({ secretStored }) => Effect.succeed(secretStored.value),
7675 createSecret: ({ value, name }) =>
7776 Effect.succeed({
78− handle: `local:${randomUUID()}`,
7977 name: trimOrNull(name),
80− value,
78+ secretStored: {
79+ value,
80+ } satisfies LocalSecretStoredData,
8181 }),
82− updateSecret: ({ secret, name, value }) =>
82+ updateSecret: ({ secret, secretStored, name, value }) =>
8383 Effect.succeed({
84− handle: secret.handle,
8584 name: trimOrNull(name ?? secret.name),
86− ...(value !== undefined ? { value } : {}),
85+ secretStored: {
86+ value: value ?? secretStored.value,
87+ } satisfies LocalSecretStoredData,
8788 }),
8889 deleteSecret: () => Effect.succeed(true),
8990 capabilities: () => ({
plugins/onepassword/sdk/index.ts+34−17
@@ -58,6 +58,10 @@ export type OnePasswordStoreStorage = { · 57 unmodified lines
5858 }) => Effect.Effect<void, Error, never>;
5959 };
6060
61+type OnePasswordSecretStoredData = {
62+ uri: string;
63+};
64+
6165 const ONEPASSWORD_REQUEST_TIMEOUT_MS = 15_000;
6266
6367 const timedOutOnePasswordRequestError = (operation: string) =>
@@ -373,9 +377,10 @@ const importSecretFromSelection = (input: { · 309 unmodified lines
373377 }
374378
375379 return {
376− handle: `op://${input.stored!.vaultId}/${item.id}/${field.id}`,
380+ secretStored: {
381+ uri: `op://${input.stored!.vaultId}/${item.id}/${field.id}`,
382+ } satisfies OnePasswordSecretStoredData,
377383 name: input.name?.trim() || `${item.title} · ${field.title}`,
378− value: null,
379384 };
380385 });
381386
@@ -385,29 +390,37 @@ const createImportedSecretRecord = (input: { · 3 unmodified lines
385390 storage: {
386391 secrets: {
387392 upsert: (value: SecretMaterial) => Effect.Effect<void, Error, never>;
393+ secretMaterialStoredData: {
394+ upsert: (value: {
395+ secretId: SecretMaterial["id"];
396+ data: OnePasswordSecretStoredData;
397+ }) => Effect.Effect<void, Error, never>;
398+ };
388399 };
389400 };
390401 };
391402 };
392403 store: SecretStore;
393− handle: string;
404+ secretStored: OnePasswordSecretStoredData;
394405 name: string | null;
395406 purpose?: SecretMaterialPurpose;
396−}): Effect.Effect<OnePasswordImportSecretResult, Error, never> =>
407+}): Effect.Effect<OnePasswordImportSecretResult, Error, any> =>
397408 Effect.gen(function* () {
398409 const now = Date.now();
399410 const secret: SecretMaterial = {
400411 id: SecretMaterialIdSchema.make(`sec_${crypto.randomUUID()}`),
401412 storeId: input.store.id,
402− handle: input.handle,
403413 name: input.name,
404414 purpose: input.purpose ?? "auth_material",
405− value: null,
406415 createdAt: now,
407416 updatedAt: now,
408417 };
409418
410419 yield* input.executor.runtime.storage.secrets.upsert(secret);
420+ yield* input.executor.runtime.storage.secrets.secretMaterialStoredData.upsert({
421+ secretId: secret.id,
422+ data: input.secretStored,
423+ });
411424
412425 return {
413426 id: secret.id,
@@ -419,14 +432,14 @@ const createImportedSecretRecord = (input: { · 5 unmodified lines
419432 };
420433 });
421434
422−const parseSecretReference = (handle: string): {
435+const parseSecretReference = (uri: string): {
423436 vaultId: string;
424437 itemId: string;
425438 fieldId: string;
426439 } => {
427− const match = /^op:\/\/([^/]+)\/([^/]+)\/([^/]+)$/.exec(handle);
440+ const match = /^op:\/\/([^/]+)\/([^/]+)\/([^/]+)$/.exec(uri);
428441 if (!match) {
429− throw new Error(`Invalid 1Password secret reference: ${handle}`);
442+ throw new Error(`Invalid 1Password secret reference: ${uri}`);
430443 }
431444
432445 return {
@@ -461,6 +474,7 @@ export const onePasswordSdkPlugin = (input: { · 28 unmodified lines
461474 OnePasswordConnectInput,
462475 OnePasswordStoreConfigPayload,
463476 OnePasswordStoredStoreData,
477+ OnePasswordSecretStoredData,
464478 OnePasswordUpdateStoreInput
465479 >({
466480 key: ONEPASSWORD_SECRET_STORE_KIND,
@@ -502,12 +516,12 @@ export const onePasswordSdkPlugin = (input: { · 35 unmodified lines
502516 vaultId: stored.vaultId,
503517 auth: stored.auth,
504518 } satisfies OnePasswordStoreConfigPayload),
505− resolveSecret: ({ secret, stored }) =>
519+ resolveSecret: ({ secretStored, stored }) =>
506520 Effect.flatMap(makeClient(stored), (client) =>
507521 Effect.tryPromise({
508522 try: () =>
509523 runOnePasswordRequest("secret resolution", () =>
510− client.secrets.resolve(secret.handle)
524+ client.secrets.resolve(secretStored.uri)
511525 ),
512526 catch: (cause) =>
513527 cause instanceof Error ? cause : new Error(String(cause)),
@@ -533,18 +547,20 @@ export const onePasswordSdkPlugin = (input: { · 19 unmodified lines
533547 );
534548
535549 return {
536− handle: `op://${stored!.vaultId}/${item.id}/${ONEPASSWORD_SECRET_FIELD_ID}`,
550+ secretStored: {
551+ uri: `op://${stored!.vaultId}/${item.id}/${ONEPASSWORD_SECRET_FIELD_ID}`,
552+ } satisfies OnePasswordSecretStoredData,
537553 name: item.title,
538554 };
539555 },
540556 catch: (cause) =>
541557 cause instanceof Error ? cause : new Error(String(cause)),
542558 })),
543− updateSecret: ({ secret, stored, name, value }) =>
559+ updateSecret: ({ secret, secretStored, stored, name, value }) =>
544560 Effect.flatMap(makeClient(stored), (client) =>
545561 Effect.tryPromise({
546562 try: async () => {
547− const parsed = parseSecretReference(secret.handle);
563+ const parsed = parseSecretReference(secretStored.uri);
548564 const item = await runOnePasswordRequest("secret update", () =>
549565 client.items.get(parsed.vaultId, parsed.itemId)
550566 );
@@ -559,16 +575,17 @@ export const onePasswordSdkPlugin = (input: { · 8 unmodified lines
559575 );
560576 return {
561577 name: updated.title,
578+ secretStored,
562579 };
563580 },
564581 catch: (cause) =>
565582 cause instanceof Error ? cause : new Error(String(cause)),
566583 })),
567− deleteSecret: ({ secret, stored }) =>
584+ deleteSecret: ({ secretStored, stored }) =>
568585 Effect.flatMap(makeClient(stored), (client) =>
569586 Effect.tryPromise({
570587 try: async () => {
571− const parsed = parseSecretReference(secret.handle);
588+ const parsed = parseSecretReference(secretStored.uri);
572589 await runOnePasswordRequest("secret deletion", () =>
573590 client.items.delete(parsed.vaultId, parsed.itemId)
574591 );
@@ -662,7 +679,7 @@ export const onePasswordSdkPlugin = (input: { · 87 unmodified lines
662679 return yield* createImportedSecretRecord({
663680 executor,
664681 store,
665− handle: imported.handle,
682+ secretStored: imported.secretStored,
666683 name: imported.name,
667684 });
668685 }),
plugins/openapi/sdk/index.ts+0−1
@@ -202,7 +202,6 @@ const openApiStoredSourceDataFromLocalConfig = (input: { · 201 unmodified lines
202202 lastSyncAt: null,
203203 });
204204 }
205−
206205 throw new Error("Unsupported OpenAPI local source config.");
207206 };
208207