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
571 "version": "0.0.0",571 "version": "0.0.0",
572 "dependencies": {572 "dependencies": {
573 "@executor/platform-sdk": "workspace:*",573 "@executor/platform-sdk": "workspace:*",
574 "@napi-rs/keyring": "^1.2.0",
574 "effect": "catalog:",575 "effect": "catalog:",
575 },576 },
576 "devDependencies": {577 "devDependencies": {
@@ -1202,6 +1203,32 @@ · 625 unmodified lines
1202 1203
1203 "@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=="],1204 "@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=="],
1204 1205
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
1205 "@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=="],1232 "@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=="],
1206 1233
1207 "@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="],1234 "@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
65 updatedAt: number;65 updatedAt: number;
66};66};
67 67
68type SqliteSecretStoredData = {
69 value: string;
70};
71
68const SQLITE_SECRET_STORE_KIND = "sqlite";72const SQLITE_SECRET_STORE_KIND = "sqlite";
69const SQLITE_SECRET_STORE_ID = "sts_sqlite";73const SQLITE_SECRET_STORE_ID = "sts_sqlite";
70 74
@@ -92,7 +96,7 @@ const sourceArtifacts = sqliteTable("source_artifacts", { · 21 unmodified lines
92 96
93const secretMaterials = sqliteTable("secret_materials", {97const secretMaterials = sqliteTable("secret_materials", {
94 id: text("id").primaryKey(),98 id: text("id").primaryKey(),
95 providerId: text("provider_id").notNull(),99 storeId: text("store_id").notNull(),
96 name: text("name"),100 name: text("name"),
97 purpose: text("purpose").notNull(),101 purpose: text("purpose").notNull(),
98 createdAt: integer("created_at").notNull(),102 createdAt: integer("created_at").notNull(),
@@ -100,6 +104,11 @@ const secretMaterials = sqliteTable("secret_materials", { · 1 unmodified line
100 json: text("json").notNull(),104 json: text("json").notNull(),
101});105});
102 106
107const secretMaterialStoredData = sqliteTable("secret_material_stored_data", {
108 secretId: text("secret_id").primaryKey(),
109 json: text("json").notNull(),
110});
111
103const executions = sqliteTable("executions", {112const executions = sqliteTable("executions", {
104 id: text("id").primaryKey(),113 id: text("id").primaryKey(),
105 scopeId: text("scope_id").notNull(),114 scopeId: text("scope_id").notNull(),
@@ -213,7 +222,7 @@ const openSqliteStore = (databasePath: string) => { · 107 unmodified lines
213 222
214 CREATE TABLE IF NOT EXISTS secret_materials (223 CREATE TABLE IF NOT EXISTS secret_materials (
215 id TEXT PRIMARY KEY NOT NULL,224 id TEXT PRIMARY KEY NOT NULL,
216 provider_id TEXT NOT NULL,225 store_id TEXT NOT NULL,
217 name TEXT,226 name TEXT,
218 purpose TEXT NOT NULL,227 purpose TEXT NOT NULL,
219 created_at INTEGER NOT NULL,228 created_at INTEGER NOT NULL,
@@ -221,6 +230,11 @@ const openSqliteStore = (databasePath: string) => { · 1 unmodified line
221 json TEXT NOT NULL230 json TEXT NOT NULL
222 );231 );
223 232
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
224 CREATE TABLE IF NOT EXISTS executions (238 CREATE TABLE IF NOT EXISTS executions (
225 id TEXT PRIMARY KEY NOT NULL,239 id TEXT PRIMARY KEY NOT NULL,
226 scope_id TEXT NOT NULL,240 scope_id TEXT NOT NULL,
@@ -241,6 +255,7 @@ const openSqliteStore = (databasePath: string) => { · 14 unmodified lines
241 json TEXT NOT NULL255 json TEXT NOT NULL
242 );256 );
243 `);257 `);
258
244 return {259 return {
245 sqlite,260 sqlite,
246 db,261 db,
@@ -292,7 +307,7 @@ const createStorageDomains = ( · 45 unmodified lines
292 listAll: (): readonly SecretMaterialSummary[] =>307 listAll: (): readonly SecretMaterialSummary[] =>
293 store.db.select().from(secretMaterials).all().map((row) => ({308 store.db.select().from(secretMaterials).all().map((row) => ({
294 id: row.id,309 id: row.id,
295 storeId: row.providerId,310 storeId: row.storeId,
296 name: row.name,311 name: row.name,
297 purpose: row.purpose,312 purpose: row.purpose,
298 createdAt: row.createdAt,313 createdAt: row.createdAt,
@@ -301,7 +316,7 @@ const createStorageDomains = ( · 2 unmodified lines
301 upsert: (material: SecretMaterial) =>316 upsert: (material: SecretMaterial) =>
302 store.db.insert(secretMaterials).values({317 store.db.insert(secretMaterials).values({
303 id: material.id,318 id: material.id,
304 providerId: material.storeId,319 storeId: material.storeId,
305 name: material.name,320 name: material.name,
306 purpose: material.purpose,321 purpose: material.purpose,
307 createdAt: material.createdAt,322 createdAt: material.createdAt,
@@ -310,7 +325,7 @@ const createStorageDomains = ( · 2 unmodified lines
310 }).onConflictDoUpdate({325 }).onConflictDoUpdate({
311 target: secretMaterials.id,326 target: secretMaterials.id,
312 set: {327 set: {
313 providerId: material.storeId,328 storeId: material.storeId,
314 name: material.name,329 name: material.name,
315 purpose: material.purpose,330 purpose: material.purpose,
316 createdAt: material.createdAt,331 createdAt: material.createdAt,
@@ -320,7 +335,7 @@ const createStorageDomains = ( · 3 unmodified lines
320 }).run(),335 }).run(),
321 updateById: (336 updateById: (
322 id: SecretMaterial["id"],337 id: SecretMaterial["id"],
323 update: { name?: string | null; value?: string },338 update: { name?: string | null },
324 ) => {339 ) => {
325 const row = store.db.select().from(secretMaterials).where(eq(secretMaterials.id, id)).get();340 const row = store.db.select().from(secretMaterials).where(eq(secretMaterials.id, id)).get();
326 if (!row) return null;341 if (!row) return null;
@@ -328,11 +343,10 @@ const createStorageDomains = ( · 1 unmodified line
328 const next = {343 const next = {
329 ...current,344 ...current,
330 name: update.name === undefined ? current.name : update.name,345 name: update.name === undefined ? current.name : update.name,
331 value: update.value === undefined ? current.value : update.value,
332 updatedAt: Date.now(),346 updatedAt: Date.now(),
333 };347 };
334 store.db.update(secretMaterials).set({348 store.db.update(secretMaterials).set({
335 providerId: next.storeId,349 storeId: next.storeId,
336 name: next.name,350 name: next.name,
337 purpose: next.purpose,351 purpose: next.purpose,
338 createdAt: next.createdAt,352 createdAt: next.createdAt,
@@ -356,6 +370,40 @@ const createStorageDomains = ( · 17 unmodified lines
356 store.db.delete(secretMaterials).where(eq(secretMaterials.id, id)).run();370 store.db.delete(secretMaterials).where(eq(secretMaterials.id, id)).run();
357 return true;371 return true;
358 },372 },
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 },
359 },407 },
360 executions: {408 executions: {
361 runs: {409 runs: {
@@ -612,14 +660,18 @@ export const createSqliteExecutorBackend = ( · 250 unmodified lines
612 secrets: {660 secrets: {
613 ...secrets,661 ...secrets,
614 secretStores: secrets.secretStores,662 secretStores: secrets.secretStores,
663 secretMaterialStoredData: secrets.secretMaterialStoredData,
615 resolve: ({ ref }) => {664 resolve: ({ ref }) => {
616 const material = secrets.getById(665 const material = secrets.getById(
617 ref.secretId as SecretMaterial["id"],666 ref.secretId as SecretMaterial["id"],
618 );667 );
619 if (!material || material.value === null) {668 const stored = secrets.secretMaterialStoredData.getBySecretId(
669 ref.secretId as SecretMaterial["id"],
670 );
671 if (!material || !stored) {
620 throw new Error(`Missing secret material ${ref.secretId}`);672 throw new Error(`Missing secret material ${ref.secretId}`);
621 }673 }
622 return material.value;674 return (stored.data as SqliteSecretStoredData).value;
623 },675 },
624 store: ({ purpose, value, name, storeId }) => {676 store: ({ purpose, value, name, storeId }) => {
625 const now = Date.now();677 const now = Date.now();
@@ -627,28 +679,44 @@ export const createSqliteExecutorBackend = ( · 1 unmodified line
627 const material: SecretMaterial = {679 const material: SecretMaterial = {
628 id,680 id,
629 storeId: (storeId ?? SQLITE_SECRET_STORE_ID) as SecretMaterial["storeId"],681 storeId: (storeId ?? SQLITE_SECRET_STORE_ID) as SecretMaterial["storeId"],
630 handle: id,
631 name: name ?? null,682 name: name ?? null,
632 purpose,683 purpose,
633 value,
634 createdAt: now,684 createdAt: now,
635 updatedAt: now,685 updatedAt: now,
636 };686 };
637 secrets.upsert(material);687 secrets.upsert(material);
688 secrets.secretMaterialStoredData.upsert({
689 secretId: id,
690 data: {
691 value,
692 } satisfies SqliteSecretStoredData,
693 });
638 return {694 return {
639 secretId: material.id,695 secretId: material.id,
640 } satisfies SecretRef;696 } satisfies SecretRef;
641 },697 },
642 delete: (ref) =>698 delete: (ref) => {
643 secrets.removeById(ref.secretId as SecretMaterial["id"]),699 secrets.secretMaterialStoredData.removeBySecretId(
700 ref.secretId as SecretMaterial["id"],
701 );
702 return secrets.removeById(ref.secretId as SecretMaterial["id"]);
703 },
644 update: ({ ref, name, value }) => {704 update: ({ ref, name, value }) => {
645 const updated = secrets.updateById(705 const updated = secrets.updateById(
646 ref.secretId as SecretMaterial["id"],706 ref.secretId as SecretMaterial["id"],
647 { name, value },707 { name },
648 );708 );
649 if (!updated) {709 if (!updated) {
650 throw new Error(`Missing secret material ${ref.secretId}`);710 throw new Error(`Missing secret material ${ref.secretId}`);
651 }711 }
712 if (value !== undefined) {
713 secrets.secretMaterialStoredData.upsert({
714 secretId: ref.secretId as SecretMaterial["id"],
715 data: {
716 value,
717 } satisfies SqliteSecretStoredData,
718 });
719 }
652 return updated;720 return updated;
653 },721 },
654 },722 },
packages/platform/sdk-file/src/config.ts+0−24
@@ -11,7 +11,6 @@ import { · 10 unmodified lines
11 LocalExecutorConfigSchema,11 LocalExecutorConfigSchema,
12 type LocalExecutorConfig,12 type LocalExecutorConfig,
13 type LocalConfigPolicy,13 type LocalConfigPolicy,
14 type LocalConfigSecretProvider,
15 type LocalConfigSource,14 type LocalConfigSource,
16} from "@executor/platform-sdk/schema";15} from "@executor/platform-sdk/schema";
17import type { LoadedLocalExecutorConfig } from "@executor/platform-sdk/runtime";16import type { LoadedLocalExecutorConfig } from "@executor/platform-sdk/runtime";
@@ -249,19 +248,6 @@ const mergePolicyMaps = ( · 231 unmodified lines
249 };248 };
250};249};
251 250
252const 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
265export const mergeLocalExecutorConfigs = (251export const mergeLocalExecutorConfigs = (
266 base: LocalExecutorConfig | null,252 base: LocalExecutorConfig | null,
267 extra: LocalExecutorConfig | null,253 extra: LocalExecutorConfig | null,
@@ -278,16 +264,6 @@ export const mergeLocalExecutorConfigs = ( · 10 unmodified lines
278 },264 },
279 sources: mergeSourceMaps(base?.sources, extra?.sources),265 sources: mergeSourceMaps(base?.sources, extra?.sources),
280 policies: mergePolicyMaps(base?.policies, extra?.policies),266 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 },
291 });267 });
292};268};
293 269
packages/platform/sdk-file/src/executor-state-store.ts+75−12
@@ -26,13 +26,21 @@ import { · 25 unmodified lines
26 unknownLocalErrorDetails,26 unknownLocalErrorDetails,
27} from "./errors";27} from "./errors";
28 28
29const LOCAL_EXECUTOR_STATE_VERSION = 1 as const;29const LOCAL_EXECUTOR_STATE_VERSION = 2 as const;
30const LOCAL_EXECUTOR_STATE_BASENAME = "executor-state.json";30const LOCAL_EXECUTOR_STATE_BASENAME = "executor-state.json";
31 31
32const SecretMaterialStoredDataRecordSchema = Schema.Struct({
33 secretId: Schema.String,
34 data: Schema.Unknown,
35});
36
37type SecretMaterialStoredDataRecord = typeof SecretMaterialStoredDataRecordSchema.Type;
38
32const LocalExecutorStateSnapshotSchema = Schema.Struct({39const LocalExecutorStateSnapshotSchema = Schema.Struct({
33 version: Schema.Literal(LOCAL_EXECUTOR_STATE_VERSION),40 version: Schema.Literal(LOCAL_EXECUTOR_STATE_VERSION),
34 secretStores: Schema.Array(SecretStoreSchema),41 secretStores: Schema.Array(SecretStoreSchema),
35 secretMaterials: Schema.Array(SecretMaterialSchema),42 secretMaterials: Schema.Array(SecretMaterialSchema),
43 secretMaterialStoredData: Schema.Array(SecretMaterialStoredDataRecordSchema),
36 executions: Schema.Array(ExecutionSchema),44 executions: Schema.Array(ExecutionSchema),
37 executionInteractions: Schema.Array(ExecutionInteractionSchema),45 executionInteractions: Schema.Array(ExecutionInteractionSchema),
38 executionSteps: Schema.Array(ExecutionStepSchema),46 executionSteps: Schema.Array(ExecutionStepSchema),
@@ -53,6 +61,7 @@ const defaultLocalExecutorStateSnapshot = (): LocalExecutorStateSnapshot => ({ · 14 unmodified lines
53 version: LOCAL_EXECUTOR_STATE_VERSION,61 version: LOCAL_EXECUTOR_STATE_VERSION,
54 secretStores: [],62 secretStores: [],
55 secretMaterials: [],63 secretMaterials: [],
64 secretMaterialStoredData: [],
56 executions: [],65 executions: [],
57 executionInteractions: [],66 executionInteractions: [],
58 executionSteps: [],67 executionSteps: [],
@@ -60,7 +69,6 @@ const defaultLocalExecutorStateSnapshot = (): LocalExecutorStateSnapshot => ({ · 1 unmodified line
60 69
61const cloneValue = <T>(value: T): T =>70const cloneValue = <T>(value: T): T =>
62 JSON.parse(JSON.stringify(value)) as T;71 JSON.parse(JSON.stringify(value)) as T;
63
64const mapFileSystemError = (path: string, action: string) => (cause: unknown) =>72const mapFileSystemError = (path: string, action: string) => (cause: unknown) =>
65 new LocalFileSystemError({73 new LocalFileSystemError({
66 message: `Failed to ${action} ${path}: ${unknownLocalErrorDetails(cause)}`,74 message: `Failed to ${action} ${path}: ${unknownLocalErrorDetails(cause)}`,
@@ -156,18 +164,19 @@ export const writeLocalExecutorStateSnapshot = (input: { · 89 unmodified lines
156}): Effect.Effect<void, LocalFileSystemError> =>164}): Effect.Effect<void, LocalFileSystemError> =>
157 bindNodeFileSystem(writeStateToDisk(input.context, input.state));165 bindNodeFileSystem(writeStateToDisk(input.context, input.state));
158 166
159const mergeById = <T extends { id: string }>(167const mergeByKey = <T>(
160 current: readonly T[],168 current: readonly T[],
161 imported: readonly T[],169 imported: readonly T[],
170 getKey: (value: T) => string,
162): T[] => {171): T[] => {
163 const merged = new Map<string, T>();172 const merged = new Map<string, T>();
164 173
165 for (const item of imported) {174 for (const item of imported) {
166 merged.set(item.id, cloneValue(item));175 merged.set(getKey(item), cloneValue(item));
167 }176 }
168 177
169 for (const item of current) {178 for (const item of current) {
170 merged.set(item.id, cloneValue(item));179 merged.set(getKey(item), cloneValue(item));
171 }180 }
172 181
173 return [...merged.values()];182 return [...merged.values()];
@@ -178,22 +187,35 @@ export const mergeImportedLocalExecutorStateSnapshot = (input: { · 4 unmodified lines
178 imported: Partial<Omit<LocalExecutorStateSnapshot, "version">>;187 imported: Partial<Omit<LocalExecutorStateSnapshot, "version">>;
179}): LocalExecutorStateSnapshot => ({188}): LocalExecutorStateSnapshot => ({
180 version: LOCAL_EXECUTOR_STATE_VERSION,189 version: LOCAL_EXECUTOR_STATE_VERSION,
181 secretStores: mergeById(190 secretStores: mergeByKey(
182 input.current.secretStores,191 input.current.secretStores,
183 input.imported.secretStores ?? [],192 input.imported.secretStores ?? [],
193 (item) => item.id,
184 ),194 ),
185 secretMaterials: mergeById(195 secretMaterials: mergeByKey(
186 input.current.secretMaterials,196 input.current.secretMaterials,
187 input.imported.secretMaterials ?? [],197 input.imported.secretMaterials ?? [],
198 (item) => item.id,
188 ),199 ),
189 executions: mergeById(input.current.executions, input.imported.executions ?? []),200 secretMaterialStoredData: mergeByKey(
190 executionInteractions: mergeById(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(
191 input.current.executionInteractions,211 input.current.executionInteractions,
192 input.imported.executionInteractions ?? [],212 input.imported.executionInteractions ?? [],
213 (item) => item.id,
193 ),214 ),
194 executionSteps: mergeById(215 executionSteps: mergeByKey(
195 input.current.executionSteps,216 input.current.executionSteps,
196 input.imported.executionSteps ?? [],217 input.imported.executionSteps ?? [],
218 (item) => item.id,
197 ),219 ),
198});220});
199 221
@@ -390,7 +412,7 @@ export const createLocalExecutorStateStore = ( · 190 unmodified lines
390 412
391 updateById: (413 updateById: (
392 id: SecretMaterial["id"],414 id: SecretMaterial["id"],
393 update: { name?: string | null; value?: string },415 update: { name?: string | null },
394 ) =>416 ) =>
395 stateManager.mutate((state) => {417 stateManager.mutate((state) => {
396 let updated: SecretMaterial | null = null;418 let updated: SecretMaterial | null = null;
@@ -402,7 +424,6 @@ export const createLocalExecutorStateStore = ( · 5 unmodified lines
402 updated = {424 updated = {
403 ...material,425 ...material,
404 ...(update.name !== undefined ? { name: update.name } : {}),426 ...(update.name !== undefined ? { name: update.name } : {}),
405 ...(update.value !== undefined ? { value: update.value } : {}),
406 updatedAt: Date.now(),427 updatedAt: Date.now(),
407 } satisfies SecretMaterial;428 } satisfies SecretMaterial;
408 return updated;429 return updated;
@@ -432,6 +453,48 @@ export const createLocalExecutorStateStore = ( · 23 unmodified lines
432 } satisfies StateMutationResult<boolean>;453 } satisfies StateMutationResult<boolean>;
433 }),454 }),
434 },455 },
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 },
435 executions: {498 executions: {
436 listByScope: (scopeId: Execution["scopeId"]) =>499 listByScope: (scopeId: Execution["scopeId"]) =>
437 stateManager.read((state) =>500 stateManager.read((state) =>
packages/platform/sdk-file/src/index.ts+3−1
@@ -25,9 +25,9 @@ import type { · 24 unmodified lines
25} from "@executor/platform-sdk/schema";25} from "@executor/platform-sdk/schema";
26import {26import {
27 type ExecutorRuntime,27 type ExecutorRuntime,
28 emptyExecutorPluginRegistry,
28 type ExecutorRuntimeOptions,29 type ExecutorRuntimeOptions,
29 type ResolveSecretMaterial,30 type ResolveSecretMaterial,
30 emptyExecutorPluginRegistry,
31} from "@executor/platform-sdk/runtime";31} from "@executor/platform-sdk/runtime";
32import * as Effect from "effect/Effect";32import * as Effect from "effect/Effect";
33import {33import {
@@ -283,6 +283,8 @@ export const createLocalExecutorRepositoriesEffect = ( · 249 unmodified lines
283 },283 },
284 secrets: {284 secrets: {
285 secretStores: executorStateStorage.executorState.secretStores,285 secretStores: executorStateStorage.executorState.secretStores,
286 secretMaterialStoredData:
287 executorStateStorage.executorState.secretMaterialStoredData,
286 ...executorStateStorage.executorState.secretMaterials,288 ...executorStateStorage.executorState.secretMaterials,
287 resolve: resolveSecretMaterial,289 resolve: resolveSecretMaterial,
288 store: createDefaultSecretMaterialStorer({290 store: createDefaultSecretMaterialStorer({
packages/platform/sdk-file/src/secret-material-providers.ts+14−7
@@ -255,15 +255,17 @@ export const createDefaultSecretMaterialStorer = (input: { · 254 unmodified lines
255 const material: SecretMaterial = {255 const material: SecretMaterial = {
256 id: `sec_${randomUUID()}` as SecretMaterial["id"],256 id: `sec_${randomUUID()}` as SecretMaterial["id"],
257 storeId: store.id,257 storeId: store.id,
258 handle: created.handle,
259 name: created.name,258 name: created.name,
260 purpose,259 purpose,
261 value: created.value ?? null,
262 createdAt: now,260 createdAt: now,
263 updatedAt: now,261 updatedAt: now,
264 };262 };
265 263
266 yield* input.executorState.secretMaterials.upsert(material);264 yield* input.executorState.secretMaterials.upsert(material);
265 yield* input.executorState.secretMaterialStoredData.upsert({
266 secretId: material.id,
267 data: created.secretStored,
268 });
267 269
268 return {270 return {
269 secretId: material.id,271 secretId: material.id,
@@ -309,12 +311,16 @@ export const createDefaultSecretMaterialUpdater = (input: { · 39 unmodified lines
309 311
310 const nextMaterial: SecretMaterial = {312 const nextMaterial: SecretMaterial = {
311 ...material,313 ...material,
312 ...(updated.handle ? { handle: updated.handle } : {}),
313 name: updated.name,314 name: updated.name,
314 value: updated.value ?? material.value,
315 updatedAt: Date.now(),315 updatedAt: Date.now(),
316 };316 };
317 yield* input.executorState.secretMaterials.upsert(nextMaterial);317 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 }
318 324
319 return {325 return {
320 id: nextMaterial.id,326 id: nextMaterial.id,
@@ -359,6 +365,7 @@ export const createDefaultSecretMaterialDeleter = (input: { · 38 unmodified lines
359 return false;365 return false;
360 }366 }
361 367
368 yield* input.executorState.secretMaterialStoredData.removeBySecretId(material.id);
362 return yield* input.executorState.secretMaterials.removeById(material.id);369 return yield* input.executorState.secretMaterials.removeById(material.id);
363 });370 });
364};371};
@@ -442,6 +449,7 @@ export const SecretMaterialLive = (input: { · 77 unmodified lines
442 SecretMaterialResolverLive(input),449 SecretMaterialResolverLive(input),
443 SecretMaterialStorerLive({450 SecretMaterialStorerLive({
444 executorState: input.executorState,451 executorState: input.executorState,
452 pluginRegistry: input.pluginRegistry,
445 resolveSecretMaterial:453 resolveSecretMaterial:
446 input.resolveSecretMaterial454 input.resolveSecretMaterial
447 ?? createDefaultSecretMaterialResolver({455 ?? createDefaultSecretMaterialResolver({
@@ -449,11 +457,11 @@ export const SecretMaterialLive = (input: { · 1 unmodified line
449 pluginRegistry: input.pluginRegistry,457 pluginRegistry: input.pluginRegistry,
450 keychainServiceName: input.keychainServiceName,458 keychainServiceName: input.keychainServiceName,
451 }),459 }),
452 pluginRegistry: input.pluginRegistry,
453 keychainServiceName: input.keychainServiceName,460 keychainServiceName: input.keychainServiceName,
454 }),461 }),
455 SecretMaterialDeleterLive({462 SecretMaterialDeleterLive({
456 executorState: input.executorState,463 executorState: input.executorState,
464 pluginRegistry: input.pluginRegistry,
457 resolveSecretMaterial:465 resolveSecretMaterial:
458 input.resolveSecretMaterial466 input.resolveSecretMaterial
459 ?? createDefaultSecretMaterialResolver({467 ?? createDefaultSecretMaterialResolver({
@@ -461,11 +469,11 @@ export const SecretMaterialLive = (input: { · 1 unmodified line
461 pluginRegistry: input.pluginRegistry,469 pluginRegistry: input.pluginRegistry,
462 keychainServiceName: input.keychainServiceName,470 keychainServiceName: input.keychainServiceName,
463 }),471 }),
464 pluginRegistry: input.pluginRegistry,
465 keychainServiceName: input.keychainServiceName,472 keychainServiceName: input.keychainServiceName,
466 }),473 }),
467 SecretMaterialUpdaterLive({474 SecretMaterialUpdaterLive({
468 executorState: input.executorState,475 executorState: input.executorState,
476 pluginRegistry: input.pluginRegistry,
469 resolveSecretMaterial:477 resolveSecretMaterial:
470 input.resolveSecretMaterial478 input.resolveSecretMaterial
471 ?? createDefaultSecretMaterialResolver({479 ?? createDefaultSecretMaterialResolver({
@@ -473,7 +481,6 @@ export const SecretMaterialLive = (input: { · 1 unmodified line
473 pluginRegistry: input.pluginRegistry,481 pluginRegistry: input.pluginRegistry,
474 keychainServiceName: input.keychainServiceName,482 keychainServiceName: input.keychainServiceName,
475 }),483 }),
476 pluginRegistry: input.pluginRegistry,
477 keychainServiceName: input.keychainServiceName,484 keychainServiceName: input.keychainServiceName,
478 }),485 }),
479 );486 );
packages/platform/sdk/src/backend.ts+7−0
@@ -176,6 +176,13 @@ const toSecretsBackend = ( · 175 unmodified lines
176 toOptionEffect(input.secretStores.updateById(id, patch)),176 toOptionEffect(input.secretStores.updateById(id, patch)),
177 removeById: (id) => toEffect(input.secretStores.removeById(id)),177 removeById: (id) => toEffect(input.secretStores.removeById(id)),
178 },178 },
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 },
179 getById: (id) => toOptionEffect(input.getById(id)),186 getById: (id) => toOptionEffect(input.getById(id)),
180 listAll: () => toEffect(input.listAll()),187 listAll: () => toEffect(input.listAll()),
181 upsert: (material) => toEffect(input.upsert(material)),188 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
82 try {82 try {
83 return Option.some(getSecretStoreContribution(pluginRegistry, kind));83 return Option.some(getSecretStoreContribution(pluginRegistry, kind));
84 } catch {84 } catch {
85 return Option.none<typeof getSecretStoreContribution extends (registry: any, kind: string) => infer T ? T : never>();85 return Option.none<any>();
86 }86 }
87 });87 });
88 88
@@ -125,10 +125,9 @@ const toSecretStoreContract = ( · 36 unmodified lines
125 125
126const createImportedSecretRecord = (input: {126const createImportedSecretRecord = (input: {
127 store: SecretStore;127 store: SecretStore;
128 handle: string;128 secretStored: unknown;
129 name: string | null;129 name: string | null;
130 purpose: SecretMaterialPurpose;130 purpose: SecretMaterialPurpose;
131 value?: string | null;
132}) =>131}) =>
133 Effect.gen(function* () {132 Effect.gen(function* () {
134 const executorState = yield* ExecutorStateStore;133 const executorState = yield* ExecutorStateStore;
@@ -136,15 +135,17 @@ const createImportedSecretRecord = (input: { · 1 unmodified line
136 const material: SecretMaterial = {135 const material: SecretMaterial = {
137 id: SecretMaterialIdSchema.make(`sec_${crypto.randomUUID()}`),136 id: SecretMaterialIdSchema.make(`sec_${crypto.randomUUID()}`),
138 storeId: input.store.id,137 storeId: input.store.id,
139 handle: input.handle,
140 name: input.name,138 name: input.name,
141 purpose: input.purpose,139 purpose: input.purpose,
142 value: input.value ?? null,
143 createdAt: now,140 createdAt: now,
144 updatedAt: now,141 updatedAt: now,
145 };142 };
146 143
147 yield* executorState.secretMaterials.upsert(material);144 yield* executorState.secretMaterials.upsert(material);
145 yield* executorState.secretMaterialStoredData.upsert({
146 secretId: material.id,
147 data: input.secretStored,
148 });
148 149
149 return {150 return {
150 id: material.id,151 id: material.id,
@@ -384,7 +385,7 @@ export const browseLocalSecretStore = (input: { · 233 unmodified lines
384 cause instanceof Error ? cause.message : "Failed browsing secret store.",385 cause instanceof Error ? cause.message : "Failed browsing secret store.",
385 ),386 ),
386 ),387 ),
387 Effect.map((result) => result satisfies BrowseSecretStoreResult),388 Effect.map((result) => result as BrowseSecretStoreResult),
388 );389 );
389 });390 });
390 391
@@ -437,9 +438,8 @@ export const importLocalSecretFromStore = (input: { · 46 unmodified lines
437 438
438 return yield* createImportedSecretRecord({439 return yield* createImportedSecretRecord({
439 store,440 store,
440 handle: created.handle,441 secretStored: created.secretStored,
441 name: created.name,442 name: created.name,
442 purpose: input.payload.purpose ?? "auth_material",443 purpose: input.payload.purpose ?? "auth_material",
443 value: created.value,
444 });444 });
445 });445 });
packages/platform/sdk/src/plugins.ts+63−25
@@ -1,4 +1,5 @@
1import * as Effect from "effect/Effect";1import * as Effect from "effect/Effect";
2import * as Option from "effect/Option";
2 3
3import type { SourceCatalogSyncResult } from "@executor/source-core";4import type { SourceCatalogSyncResult } from "@executor/source-core";
4import type {5import type {
@@ -21,6 +22,9 @@ import type { · 16 unmodified lines
21} from "@executor/source-core";22} from "@executor/source-core";
22import type * as Schema from "effect/Schema";23import type * as Schema from "effect/Schema";
23import { runtimeEffectError } from "./runtime/effect-errors";24import { runtimeEffectError } from "./runtime/effect-errors";
25import {
26 ExecutorStateStore,
27} from "./runtime/executor-state-store";
24import { ScopeConfigStore } from "./runtime/scope/storage";28import { ScopeConfigStore } from "./runtime/scope/storage";
25 29
26export type PluginCleanup = {30export type PluginCleanup = {
@@ -329,6 +333,7 @@ export type ExecutorSecretStorePluginDefinition< · 302 unmodified lines
329 TConnectInput,333 TConnectInput,
330 TStoreConfig,334 TStoreConfig,
331 TStored,335 TStored,
336 TSecretStored,
332 _TUpdateInput extends {337 _TUpdateInput extends {
333 storeId: string;338 storeId: string;
334 config: TStoreConfig;339 config: TStoreConfig;
@@ -369,6 +374,7 @@ export type ExecutorSecretStorePluginDefinition< · 34 unmodified lines
369 }) => Effect.Effect<void, Error, any>;374 }) => Effect.Effect<void, Error, any>;
370 resolveSecret: (input: {375 resolveSecret: (input: {
371 secret: SecretMaterial;376 secret: SecretMaterial;
377 secretStored: TSecretStored;
372 store: SecretStore;378 store: SecretStore;
373 stored: TStored | null;379 stored: TStored | null;
374 context?: {380 context?: {
@@ -382,23 +388,23 @@ export type ExecutorSecretStorePluginDefinition< · 7 unmodified lines
382 value: string;388 value: string;
383 name?: string | null;389 name?: string | null;
384 }) => Effect.Effect<{390 }) => Effect.Effect<{
385 handle: string;
386 name: string | null;391 name: string | null;
387 value?: string | null;392 secretStored: TSecretStored;
388 }, Error, any>;393 }, Error, any>;
389 updateSecret?: (input: {394 updateSecret?: (input: {
390 secret: SecretMaterial;395 secret: SecretMaterial;
396 secretStored: TSecretStored;
391 store: SecretStore;397 store: SecretStore;
392 stored: TStored | null;398 stored: TStored | null;
393 name?: string | null;399 name?: string | null;
394 value?: string;400 value?: string;
395 }) => Effect.Effect<{401 }) => Effect.Effect<{
396 handle?: string;
397 name: string | null;402 name: string | null;
398 value?: string | null;403 secretStored?: TSecretStored;
399 }, Error, any>;404 }, Error, any>;
400 deleteSecret?: (input: {405 deleteSecret?: (input: {
401 secret: SecretMaterial;406 secret: SecretMaterial;
407 secretStored: TSecretStored;
402 store: SecretStore;408 store: SecretStore;
403 stored: TStored | null;409 stored: TStored | null;
404 }) => Effect.Effect<boolean, Error, any>;410 }) => Effect.Effect<boolean, Error, any>;
@@ -417,9 +423,8 @@ export type ExecutorSecretStorePluginDefinition< · 12 unmodified lines
417 purpose: SecretMaterialPurpose;423 purpose: SecretMaterialPurpose;
418 name?: string | null;424 name?: string | null;
419 }) => Effect.Effect<{425 }) => Effect.Effect<{
420 handle: string;
421 name: string | null;426 name: string | null;
422 value?: string | null;427 secretStored: TSecretStored;
423 }, Error, any>;428 }, Error, any>;
424 capabilities?: (input: {429 capabilities?: (input: {
425 store: SecretStore;430 store: SecretStore;
@@ -479,6 +484,7 @@ export type ExecutorSecretStorePluginInput< · 53 unmodified lines
479 TConnectInput = unknown,484 TConnectInput = unknown,
480 TStoreConfig = unknown,485 TStoreConfig = unknown,
481 TStored = unknown,486 TStored = unknown,
487 TSecretStored = unknown,
482 TUpdateInput extends {488 TUpdateInput extends {
483 storeId: string;489 storeId: string;
484 config: TStoreConfig;490 config: TStoreConfig;
@@ -494,6 +500,7 @@ export type ExecutorSecretStorePluginInput< · 9 unmodified lines
494 TConnectInput,500 TConnectInput,
495 TStoreConfig,501 TStoreConfig,
496 TStored,502 TStored,
503 TSecretStored,
497 TUpdateInput504 TUpdateInput
498 >;505 >;
499 extendExecutor?: (input: ExecutorSdkPluginContext & {506 extendExecutor?: (input: ExecutorSdkPluginContext & {
@@ -771,7 +778,7 @@ const createExecutorSourcePluginApi = < · 271 unmodified lines
771const loadStoreOfKind = (778const loadStoreOfKind = (
772 storeId: SecretStore["id"],779 storeId: SecretStore["id"],
773 input: {780 input: {
774 definition: ExecutorSecretStorePluginDefinition<any, any, any, any, any>;781 definition: ExecutorSecretStorePluginDefinition<any, any, any, any, any, any>;
775 host: ExecutorSecretStorePluginInternalHost;782 host: ExecutorSecretStorePluginInternalHost;
776 },783 },
777): Effect.Effect<SecretStore, Error, any> =>784): Effect.Effect<SecretStore, Error, any> =>
@@ -787,11 +794,28 @@ const loadStoreOfKind = ( · 9 unmodified lines
787 return store;794 return store;
788 });795 });
789 796
797const 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
790const createExecutorSecretStorePluginApi = <813const createExecutorSecretStorePluginApi = <
791 TAddInput,814 TAddInput,
792 TConnectInput,815 TConnectInput,
793 TStoreConfig,816 TStoreConfig,
794 TStored,817 TStored,
818 TSecretStored,
795 _TUpdateInput extends {819 _TUpdateInput extends {
796 storeId: string;820 storeId: string;
797 config: TStoreConfig;821 config: TStoreConfig;
@@ -802,6 +826,7 @@ const createExecutorSecretStorePluginApi = < · 4 unmodified lines
802 TConnectInput,826 TConnectInput,
803 TStoreConfig,827 TStoreConfig,
804 TStored,828 TStored,
829 TSecretStored,
805 _TUpdateInput830 _TUpdateInput
806 >,831 >,
807 host: ExecutorSecretStorePluginInternalHost,832 host: ExecutorSecretStorePluginInternalHost,
@@ -962,9 +987,8 @@ type ExecutorSecretStoreContribution<TInput = unknown> = { · 154 unmodified lines
962 value: string;987 value: string;
963 name?: string | null;988 name?: string | null;
964 }) => Effect.Effect<{989 }) => Effect.Effect<{
965 handle: string;
966 name: string | null;990 name: string | null;
967 value?: string | null;991 secretStored: unknown;
968 }, Error, any>;992 }, Error, any>;
969 updateSecret: (input: {993 updateSecret: (input: {
970 secret: SecretMaterial;994 secret: SecretMaterial;
@@ -972,9 +996,8 @@ type ExecutorSecretStoreContribution<TInput = unknown> = { · 1 unmodified line
972 name?: string | null;996 name?: string | null;
973 value?: string;997 value?: string;
974 }) => Effect.Effect<{998 }) => Effect.Effect<{
975 handle?: string;
976 name: string | null;999 name: string | null;
977 value?: string | null;1000 secretStored?: unknown;
978 }, Error, any>;1001 }, Error, any>;
979 deleteSecret: (input: {1002 deleteSecret: (input: {
980 secret: SecretMaterial;1003 secret: SecretMaterial;
@@ -993,9 +1016,8 @@ type ExecutorSecretStoreContribution<TInput = unknown> = { · 12 unmodified lines
993 purpose: SecretMaterialPurpose;1016 purpose: SecretMaterialPurpose;
994 name?: string | null;1017 name?: string | null;
995 }) => Effect.Effect<{1018 }) => Effect.Effect<{
996 handle: string;
997 name: string | null;1019 name: string | null;
998 value?: string | null;1020 secretStored: unknown;
999 }, Error, any>;1021 }, Error, any>;
1000};1022};
1001 1023
@@ -1076,6 +1098,7 @@ const createExecutorSecretStoreContribution = < · 74 unmodified lines
1076 TConnectInput,1098 TConnectInput,
1077 TStoreConfig,1099 TStoreConfig,
1078 TStored,1100 TStored,
1101 TSecretStored,
1079 TUpdateInput extends {1102 TUpdateInput extends {
1080 storeId: string;1103 storeId: string;
1081 config: TStoreConfig;1104 config: TStoreConfig;
@@ -1086,6 +1109,7 @@ const createExecutorSecretStoreContribution = < · 4 unmodified lines
1086 TConnectInput,1109 TConnectInput,
1087 TStoreConfig,1110 TStoreConfig,
1088 TStored,1111 TStored,
1112 TSecretStored,
1089 TUpdateInput1113 TUpdateInput
1090 >,1114 >,
1091): ExecutorSecretStoreContribution<TAddInput> => ({1115): ExecutorSecretStoreContribution<TAddInput> => ({
@@ -1153,13 +1177,17 @@ const createExecutorSecretStoreContribution = < · 61 unmodified lines
1153 ),1177 ),
1154 resolveSecret: ({ secret, store, context }) =>1178 resolveSecret: ({ secret, store, context }) =>
1155 Effect.flatMap(1179 Effect.flatMap(
1156 definition.storage.get({1180 Effect.all({
1157 scopeId: store.scopeId,1181 stored: definition.storage.get({
1158 storeId: store.id,1182 scopeId: store.scopeId,
1183 storeId: store.id,
1184 }),
1185 secretStored: loadSecretMaterialStoredData<TSecretStored>(secret.id),
1159 }),1186 }),
1160 (stored) =>1187 ({ stored, secretStored }) =>
1161 definition.store.resolveSecret({1188 definition.store.resolveSecret({
1162 secret,1189 secret,
1190 secretStored,
1163 store,1191 store,
1164 stored,1192 stored,
1165 context,1193 context,
@@ -1188,13 +1216,17 @@ const createExecutorSecretStoreContribution = < · 22 unmodified lines
1188 updateSecret: ({ secret, store, name, value }) =>1216 updateSecret: ({ secret, store, name, value }) =>
1189 definition.store.updateSecret1217 definition.store.updateSecret
1190 ? Effect.flatMap(1218 ? Effect.flatMap(
1191 definition.storage.get({1219 Effect.all({
1192 scopeId: store.scopeId,1220 stored: definition.storage.get({
1193 storeId: store.id,1221 scopeId: store.scopeId,
1222 storeId: store.id,
1223 }),
1224 secretStored: loadSecretMaterialStoredData<TSecretStored>(secret.id),
1194 }),1225 }),
1195 (stored) =>1226 ({ stored, secretStored }) =>
1196 definition.store.updateSecret!({1227 definition.store.updateSecret!({
1197 secret,1228 secret,
1229 secretStored,
1198 store,1230 store,
1199 stored,1231 stored,
1200 name,1232 name,
@@ -1208,13 +1240,17 @@ const createExecutorSecretStoreContribution = < · 7 unmodified lines
1208 deleteSecret: ({ secret, store }) =>1240 deleteSecret: ({ secret, store }) =>
1209 definition.store.deleteSecret1241 definition.store.deleteSecret
1210 ? Effect.flatMap(1242 ? Effect.flatMap(
1211 definition.storage.get({1243 Effect.all({
1212 scopeId: store.scopeId,1244 stored: definition.storage.get({
1213 storeId: store.id,1245 scopeId: store.scopeId,
1246 storeId: store.id,
1247 }),
1248 secretStored: loadSecretMaterialStoredData<TSecretStored>(secret.id),
1214 }),1249 }),
1215 (stored) =>1250 ({ stored, secretStored }) =>
1216 definition.store.deleteSecret!({1251 definition.store.deleteSecret!({
1217 secret,1252 secret,
1253 secretStored,
1218 store,1254 store,
1219 stored,1255 stored,
1220 }),1256 }),
@@ -1377,6 +1413,7 @@ export const defineExecutorSecretStorePlugin = < · 156 unmodified lines
1377 TConnectInput,1413 TConnectInput,
1378 TStoreConfig,1414 TStoreConfig,
1379 TStored,1415 TStored,
1416 TSecretStored,
1380 TUpdateInput extends {1417 TUpdateInput extends {
1381 storeId: string;1418 storeId: string;
1382 config: TStoreConfig;1419 config: TStoreConfig;
@@ -1389,6 +1426,7 @@ export const defineExecutorSecretStorePlugin = < · 6 unmodified lines
1389 TConnectInput,1426 TConnectInput,
1390 TStoreConfig,1427 TStoreConfig,
1391 TStored,1428 TStored,
1429 TSecretStored,
1392 TUpdateInput,1430 TUpdateInput,
1393 TExtension1431 TExtension
1394 >,1432 >,
packages/platform/sdk/src/runtime/execution/live.test.ts+2−3
@@ -14,9 +14,8 @@ describe("live-execution", () => { · 13 unmodified lines
14 action: "accept",14 action: "accept",
15 content: {15 content: {
16 authKind: "bearer",16 authKind: "bearer",
17 tokenRef: {17 tokenSecretRef: {
18 providerId: "local",18 secretId: "sec_123",
19 handle: "sec_123",
20 },19 },
21 },20 },
22 });21 });
packages/platform/sdk/src/runtime/execution/live.ts+4−1
@@ -71,7 +71,10 @@ const serializeJson = (value: unknown): string | null => { · 70 unmodified lines
71};71};
72 72
73const SENSITIVE_INTERACTION_CONTENT_KEYS = new Set([73const SENSITIVE_INTERACTION_CONTENT_KEYS = new Set([
74 "tokenRef",74 "tokenSecretRef",
75 "accessTokenRef",
76 "refreshTokenRef",
77 "clientSecretRef",
75 "tokenSecretMaterialId",78 "tokenSecretMaterialId",
76]);79]);
77 80
packages/platform/sdk/src/runtime/executor-state-store.ts+21−1
@@ -18,6 +18,11 @@ type SecretMaterialSummary = { · 17 unmodified lines
18 updatedAt: number;18 updatedAt: number;
19};19};
20 20
21export type SecretMaterialStoredDataRecord = {
22 secretId: string;
23 data: unknown;
24};
25
21type SecretStoreSummary = {26type SecretStoreSummary = {
22 id: string;27 id: string;
23 scopeId: string;28 scopeId: string;
@@ -56,7 +61,7 @@ export type ExecutorStateStoreShape = { · 32 unmodified lines
56 upsert: (material: SecretMaterial) => Effect.Effect<void, Error, never>;61 upsert: (material: SecretMaterial) => Effect.Effect<void, Error, never>;
57 updateById: (62 updateById: (
58 id: SecretMaterial["id"],63 id: SecretMaterial["id"],
59 update: { name?: string | null; value?: string },64 update: { name?: string | null },
60 ) => Effect.Effect<65 ) => Effect.Effect<
61 import("effect/Option").Option<SecretMaterialSummary>,66 import("effect/Option").Option<SecretMaterialSummary>,
62 Error,67 Error,
@@ -64,6 +69,21 @@ export type ExecutorStateStoreShape = { · 1 unmodified line
64 >;69 >;
65 removeById: (id: SecretMaterial["id"]) => Effect.Effect<boolean, Error, never>;70 removeById: (id: SecretMaterial["id"]) => Effect.Effect<boolean, Error, never>;
66 };71 };
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 };
67 executions: {87 executions: {
68 listByScope: (88 listByScope: (
69 scopeId: Execution["scopeId"],89 scopeId: Execution["scopeId"],
packages/platform/sdk/src/runtime/index.ts+9−2
@@ -112,7 +112,6 @@ export * from "./execution/service"; · 111 unmodified lines
112export * from "./source-artifacts";112export * from "./source-artifacts";
113export * from "./scope-config";113export * from "./scope-config";
114export * from "./scope-state";114export * from "./scope-state";
115export * from "./scope/config-secrets";
116export * from "./sources/source-store/config";115export * from "./sources/source-store/config";
117export * from "./sources/source-store/plugin-local-config";116export * from "./sources/source-store/plugin-local-config";
118export {117export {
@@ -244,6 +243,7 @@ export const prewarmWorkspaceSourceCatalogToolIndex = (input: { · 125 unmodified lines
244export type RuntimeSecretsStorageServices =243export type RuntimeSecretsStorageServices =
245 & {244 & {
246 secretStores: ExecutorStateStoreShape["secretStores"];245 secretStores: ExecutorStateStoreShape["secretStores"];
246 secretMaterialStoredData: ExecutorStateStoreShape["secretMaterialStoredData"];
247 }247 }
248 & ExecutorStateStoreShape["secretMaterials"]248 & ExecutorStateStoreShape["secretMaterials"]
249 & RuntimeSecretMaterialServices;249 & RuntimeSecretMaterialServices;
@@ -343,7 +343,14 @@ const toExecutorStateStoreShape = ( · 93 unmodified lines
343 input: RuntimeStorageServices,343 input: RuntimeStorageServices,
344): ExecutorStateStoreShape => ({344): ExecutorStateStoreShape => ({
345 secretStores: input.secrets.secretStores,345 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,
347 executions: input.executions.runs,354 executions: input.executions.runs,
348 executionInteractions: input.executions.interactions,355 executionInteractions: input.executions.interactions,
349 executionSteps: input.executions.steps,356 executionSteps: input.executions.steps,
packages/platform/sdk/src/runtime/scope/config-secrets.ts+0−9
@@ -1,9 +0,0 @@
1export const CONFIG_SECRET_PROVIDER_PREFIX = "config:";
2
3export const toConfigSecretProviderId = (providerAlias: string): string =>
4 `${CONFIG_SECRET_PROVIDER_PREFIX}${providerAlias}`;
5
6export 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
3 Source,3 Source,
4} from "#schema";4} from "#schema";
5import * as Schema from "effect/Schema";5import * as Schema from "effect/Schema";
6
7import {6import {
8 cloneJson,7 cloneJson,
9 configSourceBaseFromLocalSource,8 configSourceBaseFromLocalSource,
packages/platform/sdk/src/schema/models/local-config.ts+0−73
@@ -8,54 +8,6 @@ export const LocalExecutorRuntimeSchema = Schema.Literal( · 7 unmodified lines
8 "deno",8 "deno",
9);9);
10 10
11export const LocalConfigSecretProviderSourceSchema = Schema.Literal(
12 "env",
13 "file",
14 "exec",
15 "params",
16);
17
18export const LocalConfigEnvSecretProviderSchema = Schema.Struct({
19 source: Schema.Literal("env"),
20});
21
22export const LocalConfigFileSecretProviderModeSchema = Schema.Literal(
23 "singleValue",
24 "json",
25);
26
27export const LocalConfigFileSecretProviderSchema = Schema.Struct({
28 source: Schema.Literal("file"),
29 path: Schema.String,
30 mode: Schema.optional(LocalConfigFileSecretProviderModeSchema),
31});
32
33export 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
42export const LocalConfigSecretProviderSchema = Schema.Union(
43 LocalConfigEnvSecretProviderSchema,
44 LocalConfigFileSecretProviderSchema,
45 LocalConfigExecSecretProviderSchema,
46);
47
48export const LocalConfigExplicitSecretRefSchema = Schema.Struct({
49 source: LocalConfigSecretProviderSourceSchema,
50 provider: Schema.String,
51 id: Schema.String,
52});
53
54export const LocalConfigSecretInputSchema = Schema.Union(
55 Schema.String,
56 LocalConfigExplicitSecretRefSchema,
57);
58
59export const LocalConfigSourceConnectionSchema = Schema.Struct({11export const LocalConfigSourceConnectionSchema = Schema.Struct({
60 endpoint: Schema.optional(Schema.NullOr(Schema.String)),12 endpoint: Schema.optional(Schema.NullOr(Schema.String)),
61 auth: Schema.optional(Schema.Unknown),13 auth: Schema.optional(Schema.Unknown),
@@ -90,22 +42,6 @@ export const LocalConfigPolicySchema = Schema.Struct({ · 28 unmodified lines
90 priority: Schema.optional(Schema.Number),42 priority: Schema.optional(Schema.Number),
91});43});
92 44
93export 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
109export const LocalConfigWorkspaceSchema = Schema.Struct({45export const LocalConfigWorkspaceSchema = Schema.Struct({
110 name: Schema.optional(Schema.String),46 name: Schema.optional(Schema.String),
111});47});
@@ -125,17 +61,8 @@ export const LocalExecutorConfigSchema = Schema.Struct({ · 13 unmodified lines
125 value: LocalConfigPolicySchema,61 value: LocalConfigPolicySchema,
126 }),62 }),
127 ),63 ),
128 secrets: Schema.optional(LocalConfigSecretsSchema),
129});64});
130 65
131export type LocalConfigSecretProviderSource =
132 typeof LocalConfigSecretProviderSourceSchema.Type;
133export type LocalConfigSecretProvider =
134 typeof LocalConfigSecretProviderSchema.Type;
135export type LocalConfigExplicitSecretRef =
136 typeof LocalConfigExplicitSecretRefSchema.Type;
137export type LocalConfigSecretInput = typeof LocalConfigSecretInputSchema.Type;
138export type LocalConfigPolicy = typeof LocalConfigPolicySchema.Type;66export type LocalConfigPolicy = typeof LocalConfigPolicySchema.Type;
139export type LocalConfigSecrets = typeof LocalConfigSecretsSchema.Type;
140export type LocalExecutorRuntime = typeof LocalExecutorRuntimeSchema.Type;67export type LocalExecutorRuntime = typeof LocalExecutorRuntimeSchema.Type;
141export type LocalExecutorConfig = typeof LocalExecutorConfigSchema.Type;68export 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
22 name: Schema.NullOr(Schema.String),22 name: Schema.NullOr(Schema.String),
23 purpose: SecretMaterialPurposeSchema,23 purpose: SecretMaterialPurposeSchema,
24 storeId: SecretStoreIdSchema,24 storeId: SecretStoreIdSchema,
25 handle: Schema.String,
26 value: Schema.NullOr(Schema.String),
27 createdAt: TimestampMsSchema,25 createdAt: TimestampMsSchema,
28 updatedAt: TimestampMsSchema,26 updatedAt: TimestampMsSchema,
29});27});
packages/sources/core/src/source-models.ts+1−2
@@ -1,8 +1,7 @@
1import * as Schema from "effect/Schema";1import * as Schema from "effect/Schema";
2 2
3export const SecretRefSchema = Schema.Struct({3export const SecretRefSchema = Schema.Struct({
4 providerId: Schema.String,4 secretId: Schema.String,
5 handle: Schema.String,
6});5});
7 6
8export const CredentialSlotSchema = Schema.Literal("runtime", "import");7export const CredentialSlotSchema = Schema.Literal("runtime", "import");
plugins/google-discovery/sdk/index.ts+0−1
@@ -557,7 +557,6 @@ const googleDiscoveryStoredSourceDataFromLocalConfig = (input: { · 556 unmodified lines
557 if (Option.isSome(current)) {557 if (Option.isSome(current)) {
558 return current.value.config;558 return current.value.config;
559 }559 }
560
561 throw new Error("Unsupported Google Discovery local source config.");560 throw new Error("Unsupported Google Discovery local source config.");
562};561};
563 562
plugins/graphql/sdk/index.ts+0−1
@@ -342,7 +342,6 @@ const graphqlStoredSourceDataFromLocalConfig = (input: { · 341 unmodified lines
342 if (Option.isSome(current)) {342 if (Option.isSome(current)) {
343 return normalizeStoredSourceData(current.value.config);343 return normalizeStoredSourceData(current.value.config);
344 }344 }
345
346 throw new Error("Unsupported GraphQL local source config.");345 throw new Error("Unsupported GraphQL local source config.");
347};346};
348 347
plugins/keychain-secret-store/sdk/index.ts+103−275
@@ -1,5 +1,7 @@
1import { randomUUID } from "node:crypto";1import { randomUUID } from "node:crypto";
2import { spawn } from "node:child_process";2import {
3 Entry,
4} from "@napi-rs/keyring";
3 5
4import * as Effect from "effect/Effect";6import * as Effect from "effect/Effect";
5 7
@@ -13,16 +15,13 @@ import { · 7 unmodified lines
13export const KEYCHAIN_SECRET_STORE_KIND = "keychain";15export const KEYCHAIN_SECRET_STORE_KIND = "keychain";
14export const KEYCHAIN_SECRET_STORE_ID = "sts_builtin_keychain";16export const KEYCHAIN_SECRET_STORE_ID = "sts_builtin_keychain";
15 17
18type KeychainSecretStoredData = {
19 account: string;
20};
21
16const DEFAULT_KEYCHAIN_SERVICE_NAME = "executor";22const DEFAULT_KEYCHAIN_SERVICE_NAME = "executor";
17const KEYCHAIN_COMMAND_TIMEOUT_MS = 5_000;
18const KEYCHAIN_SERVICE_NAME_ENV = "EXECUTOR_KEYCHAIN_SERVICE_NAME";23const KEYCHAIN_SERVICE_NAME_ENV = "EXECUTOR_KEYCHAIN_SERVICE_NAME";
19 24
20type SpawnResult = {
21 exitCode: number;
22 stdout: string;
23 stderr: string;
24};
25
26const toError = (cause: unknown): Error =>25const toError = (cause: unknown): Error =>
27 cause instanceof Error ? cause : new Error(String(cause));26 cause instanceof Error ? cause : new Error(String(cause));
28 27
@@ -35,280 +34,99 @@ const trimOrNull = (value: string | null | undefined): string | null => { · 6 unmodified lines
35 return trimmed.length > 0 ? trimmed : null;34 return trimmed.length > 0 ? trimmed : null;
36};35};
37 36
38const ensureNonEmptyString = (value: string | undefined | null): string | null => {
39 const trimmed = value?.trim();
40 return trimmed && trimmed.length > 0 ? trimmed : null;
41};
42
43const resolveKeychainServiceName = (value: string | undefined): string =>37const resolveKeychainServiceName = (value: string | undefined): string =>
44 trimOrNull(value)38 trimOrNull(value)
45 ?? trimOrNull(process.env[KEYCHAIN_SERVICE_NAME_ENV])39 ?? trimOrNull(process.env[KEYCHAIN_SERVICE_NAME_ENV])
46 ?? DEFAULT_KEYCHAIN_SERVICE_NAME;40 ?? DEFAULT_KEYCHAIN_SERVICE_NAME;
47 41
48const runCommand = (input: {42const isSupportedPlatform = () =>
49 command: string;43 process.platform === "darwin"
50 args: ReadonlyArray<string>;44 || process.platform === "linux"
51 stdin?: string;45 || process.platform === "win32";
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 });
89 46
90 child.on("error", (error) => {47const keychainDisplayName = () =>
91 if (settled) {48 process.platform === "darwin"
92 return;49 ? "macOS Keychain"
93 }50 : process.platform === "win32"
94 settled = true;51 ? "Windows Credential Manager"
95 if (timeout) {52 : "Desktop Keyring";
96 clearTimeout(timeout);
97 }
98 reject(error);
99 });
100 53
101 child.on("close", (code) => {54const createKeyringEntry = (input: {
102 if (settled) {55 providerHandle: string;
103 return;56 keychainServiceName: string;
104 }57}) =>
105 settled = true;58 Effect.try({
106 if (timeout) {59 try: () => {
107 clearTimeout(timeout);60 if (!isSupportedPlatform()) {
108 }61 throw runtimeEffectError(
109 resolve({62 "plugin-keychain-secret-store",
110 exitCode: code ?? 0,63 `system-keyring: unsupported on platform '${process.platform}'`,
111 stdout,64 );
112 stderr,65 }
113 });
114 });
115
116 if (input.stdin !== undefined) {
117 child.stdin.write(input.stdin);
118 }
119 66
120 child.stdin.end();67 return new Entry(input.keychainServiceName, input.providerHandle);
121 }),68 },
122 catch: toError,69 catch: toError,
123 });70 });
124 71
125const 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
146const readKeychainSecretValue = (input: {72const readKeychainSecretValue = (input: {
147 providerHandle: string;73 providerHandle: string;
148 keychainServiceName: string;74 keychainServiceName: string;
149}) => {75}) =>
150 switch (process.platform) {76 Effect.flatMap(
151 case "darwin":77 createKeyringEntry(input),
152 return runCommand({78 (entry) =>
153 command: "security",79 Effect.try({
154 args: [80 try: () => entry.getPassword(),
155 "find-generic-password",81 catch: toError,
156 "-a",
157 input.providerHandle,
158 "-s",
159 input.keychainServiceName,
160 "-w",
161 ],
162 operation: "keychain.get",
163 timeoutMs: KEYCHAIN_COMMAND_TIMEOUT_MS,
164 }).pipe(82 }).pipe(
165 Effect.flatMap((result) =>83 Effect.flatMap((value) =>
166 ensureCommandSuccess({84 value !== null
167 result,85 ? Effect.succeed(value)
168 operation: "keychain.get",86 : Effect.fail(
169 message: "Failed loading secret from macOS keychain",87 runtimeEffectError(
170 })),88 "plugin-keychain-secret-store",
171 Effect.map((result) => result.stdout.trimEnd()),89 `keychain.get: secret not found for service '${input.keychainServiceName}' and account '${input.providerHandle}'`,
172 );90 ),
173 case "linux":91 )),
174 return runCommand({92 ),
175 command: "secret-tool",93 );
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};
203 94
204const writeKeychainSecretValue = (input: {95const writeKeychainSecretValue = (input: {
205 providerHandle: string;96 providerHandle: string;
206 name?: string | null;97 name?: string | null;
207 value: string;98 value: string;
208 keychainServiceName: string;99 keychainServiceName: string;
209}) => {100}) =>
210 const secretName = trimOrNull(input.name);101 Effect.flatMap(
211 102 createKeyringEntry(input),
212 switch (process.platform) {103 (entry) =>
213 case "darwin":104 Effect.try({
214 return runCommand({105 try: () => entry.setPassword(input.value),
215 command: "security",106 catch: (cause) =>
216 args: [107 runtimeEffectError(
217 "add-generic-password",108 "plugin-keychain-secret-store",
218 "-a",109 `keychain.put: Failed storing secret in ${keychainDisplayName().toLowerCase()}: ${toError(cause).message}`,
219 input.providerHandle,110 ),
220 "-s",111 }).pipe(Effect.asVoid),
221 input.keychainServiceName,112 );
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};
271 113
272const deleteKeychainSecretValue = (input: {114const deleteKeychainSecretValue = (input: {
273 providerHandle: string;115 providerHandle: string;
274 keychainServiceName: string;116 keychainServiceName: string;
275}) => {117}) =>
276 switch (process.platform) {118 Effect.flatMap(
277 case "darwin":119 createKeyringEntry(input),
278 return runCommand({120 (entry) =>
279 command: "security",121 Effect.try({
280 args: [122 try: () => entry.deletePassword(),
281 "delete-generic-password",123 catch: (cause) =>
282 "-a",124 runtimeEffectError(
283 input.providerHandle,125 "plugin-keychain-secret-store",
284 "-s",126 `keychain.delete: Failed deleting secret from ${keychainDisplayName().toLowerCase()}: ${toError(cause).message}`,
285 input.keychainServiceName,127 ),
286 ],128 }),
287 operation: "keychain.delete",129 );
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};
312 130
313const builtinSecretStoreStorage = <TStored>(value: TStored) => ({131const builtinSecretStoreStorage = <TStored>(value: TStored) => ({
314 get: () => Effect.succeed(value),132 get: () => Effect.succeed(value),
@@ -316,19 +134,29 @@ const builtinSecretStoreStorage = <TStored>(value: TStored) => ({ · 1 unmodified line
316 remove: () => Effect.void,134 remove: () => Effect.void,
317});135});
318 136
319export const keychainSecretStoreSdkPlugin = defineExecutorSecretStorePlugin({137export 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>({
320 key: KEYCHAIN_SECRET_STORE_KIND,149 key: KEYCHAIN_SECRET_STORE_KIND,
321 secretStore: {150 secretStore: {
322 kind: KEYCHAIN_SECRET_STORE_KIND,151 kind: KEYCHAIN_SECRET_STORE_KIND,
323 displayName:152 displayName: keychainDisplayName(),
324 process.platform === "darwin" ? "macOS Keychain" : "Desktop Keyring",
325 builtin: {153 builtin: {
326 storeId: KEYCHAIN_SECRET_STORE_ID,154 storeId: KEYCHAIN_SECRET_STORE_ID,
327 defaultPriority: 10,155 defaultPriority: 10,
328 enabled: () => process.platform === "darwin" || process.platform === "linux",156 enabled: isSupportedPlatform,
329 createStore: () => ({157 createStore: () => ({
330 kind: KEYCHAIN_SECRET_STORE_KIND,158 kind: KEYCHAIN_SECRET_STORE_KIND,
331 name: process.platform === "darwin" ? "macOS Keychain" : "Desktop Keyring",159 name: keychainDisplayName(),
332 status: "connected",160 status: "connected",
333 enabled: true,161 enabled: true,
334 }),162 }),
@@ -352,50 +180,50 @@ export const keychainSecretStoreSdkPlugin = defineExecutorSecretStorePlugin({ · 17 unmodified lines
352 kind: KEYCHAIN_SECRET_STORE_KIND,180 kind: KEYCHAIN_SECRET_STORE_KIND,
353 name: store.name,181 name: store.name,
354 }),182 }),
355 resolveSecret: ({ secret }) =>183 resolveSecret: ({ secretStored }) =>
356 readKeychainSecretValue({184 readKeychainSecretValue({
357 providerHandle: secret.handle,185 providerHandle: secretStored.account,
358 keychainServiceName: resolveKeychainServiceName(undefined),186 keychainServiceName: resolveKeychainServiceName(undefined),
359 }),187 }),
360 createSecret: ({ value, name }) =>188 createSecret: ({ value, name }) =>
361 Effect.gen(function* () {189 Effect.gen(function* () {
362 const providerHandle = randomUUID();190 const account = randomUUID();
363 yield* writeKeychainSecretValue({191 yield* writeKeychainSecretValue({
364 providerHandle,192 providerHandle: account,
365 name,193 name,
366 value,194 value,
367 keychainServiceName: resolveKeychainServiceName(undefined),195 keychainServiceName: resolveKeychainServiceName(undefined),
368 });196 });
369 return {197 return {
370 handle: providerHandle,
371 name: trimOrNull(name),198 name: trimOrNull(name),
372 value: null,199 secretStored: {
200 account,
201 } satisfies KeychainSecretStoredData,
373 };202 };
374 }),203 }),
375 updateSecret: ({ secret, name, value }) =>204 updateSecret: ({ secret, secretStored, name, value }) =>
376 Effect.gen(function* () {205 Effect.gen(function* () {
377 const keychainServiceName = resolveKeychainServiceName(undefined);206 const keychainServiceName = resolveKeychainServiceName(undefined);
378 const nextName = trimOrNull(name ?? secret.name);207 const nextName = trimOrNull(name ?? secret.name);
379 const nextValue = value208 const nextValue = value
380 ?? (yield* readKeychainSecretValue({209 ?? (yield* readKeychainSecretValue({
381 providerHandle: secret.handle,210 providerHandle: secretStored.account,
382 keychainServiceName,211 keychainServiceName,
383 }));212 }));
384 yield* writeKeychainSecretValue({213 yield* writeKeychainSecretValue({
385 providerHandle: secret.handle,214 providerHandle: secretStored.account,
386 name: nextName,215 name: nextName,
387 value: nextValue,216 value: nextValue,
388 keychainServiceName,217 keychainServiceName,
389 });218 });
390 return {219 return {
391 handle: secret.handle,
392 name: nextName,220 name: nextName,
393 value: null,221 secretStored,
394 };222 };
395 }),223 }),
396 deleteSecret: ({ secret }) =>224 deleteSecret: ({ secretStored }) =>
397 deleteKeychainSecretValue({225 deleteKeychainSecretValue({
398 providerHandle: secret.handle,226 providerHandle: secretStored.account,
399 keychainServiceName: resolveKeychainServiceName(undefined),227 keychainServiceName: resolveKeychainServiceName(undefined),
400 }),228 }),
401 capabilities: () => ({229 capabilities: () => ({
plugins/keychain-secret-store/sdk/package.json+1−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
13 },13 },
14 "dependencies": {14 "dependencies": {
15 "@executor/platform-sdk": "workspace:*",15 "@executor/platform-sdk": "workspace:*",
16 "@napi-rs/keyring": "^1.2.0",
16 "effect": "catalog:"17 "effect": "catalog:"
17 },18 },
18 "devDependencies": {19 "devDependencies": {
plugins/local-secret-store/sdk/index.ts+24−23
@@ -1,17 +1,16 @@
1import { randomUUID } from "node:crypto";
2
3import * as Effect from "effect/Effect";1import * as Effect from "effect/Effect";
4 2
5import {3import {
6 defineExecutorSecretStorePlugin,4 defineExecutorSecretStorePlugin,
7} from "@executor/platform-sdk/plugins";5} from "@executor/platform-sdk/plugins";
8import {
9 runtimeEffectError,
10} from "@executor/platform-sdk/runtime";
11 6
12export const LOCAL_SECRET_STORE_KIND = "local";7export const LOCAL_SECRET_STORE_KIND = "local";
13export const LOCAL_SECRET_STORE_ID = "sts_builtin_local";8export const LOCAL_SECRET_STORE_ID = "sts_builtin_local";
14 9
10type LocalSecretStoredData = {
11 value: string;
12};
13
15const builtinSecretStoreStorage = <TStored>(value: TStored) => ({14const builtinSecretStoreStorage = <TStored>(value: TStored) => ({
16 get: () => Effect.succeed(value),15 get: () => Effect.succeed(value),
17 put: () => Effect.void,16 put: () => Effect.void,
@@ -27,7 +26,18 @@ const trimOrNull = (value: string | null | undefined): string | null => { · 9 unmodified lines
27 return trimmed.length > 0 ? trimmed : null;26 return trimmed.length > 0 ? trimmed : null;
28};27};
29 28
30export const localSecretStoreSdkPlugin = defineExecutorSecretStorePlugin({29export 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>({
31 key: LOCAL_SECRET_STORE_KIND,41 key: LOCAL_SECRET_STORE_KIND,
32 secretStore: {42 secretStore: {
33 kind: LOCAL_SECRET_STORE_KIND,43 kind: LOCAL_SECRET_STORE_KIND,
@@ -61,29 +71,20 @@ export const localSecretStoreSdkPlugin = defineExecutorSecretStorePlugin({ · 27 unmodified lines
61 kind: LOCAL_SECRET_STORE_KIND,71 kind: LOCAL_SECRET_STORE_KIND,
62 name: store.name,72 name: store.name,
63 }),73 }),
64 resolveSecret: ({ secret }) => {74 resolveSecret: ({ secretStored }) => Effect.succeed(secretStored.value),
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 },
76 createSecret: ({ value, name }) =>75 createSecret: ({ value, name }) =>
77 Effect.succeed({76 Effect.succeed({
78 handle: `local:${randomUUID()}`,
79 name: trimOrNull(name),77 name: trimOrNull(name),
80 value,78 secretStored: {
79 value,
80 } satisfies LocalSecretStoredData,
81 }),81 }),
82 updateSecret: ({ secret, name, value }) =>82 updateSecret: ({ secret, secretStored, name, value }) =>
83 Effect.succeed({83 Effect.succeed({
84 handle: secret.handle,
85 name: trimOrNull(name ?? secret.name),84 name: trimOrNull(name ?? secret.name),
86 ...(value !== undefined ? { value } : {}),85 secretStored: {
86 value: value ?? secretStored.value,
87 } satisfies LocalSecretStoredData,
87 }),88 }),
88 deleteSecret: () => Effect.succeed(true),89 deleteSecret: () => Effect.succeed(true),
89 capabilities: () => ({90 capabilities: () => ({
plugins/onepassword/sdk/index.ts+34−17
@@ -58,6 +58,10 @@ export type OnePasswordStoreStorage = { · 57 unmodified lines
58 }) => Effect.Effect<void, Error, never>;58 }) => Effect.Effect<void, Error, never>;
59};59};
60 60
61type OnePasswordSecretStoredData = {
62 uri: string;
63};
64
61const ONEPASSWORD_REQUEST_TIMEOUT_MS = 15_000;65const ONEPASSWORD_REQUEST_TIMEOUT_MS = 15_000;
62 66
63const timedOutOnePasswordRequestError = (operation: string) =>67const timedOutOnePasswordRequestError = (operation: string) =>
@@ -373,9 +377,10 @@ const importSecretFromSelection = (input: { · 309 unmodified lines
373 }377 }
374 378
375 return {379 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,
377 name: input.name?.trim() || `${item.title} · ${field.title}`,383 name: input.name?.trim() || `${item.title} · ${field.title}`,
378 value: null,
379 };384 };
380 });385 });
381 386
@@ -385,29 +390,37 @@ const createImportedSecretRecord = (input: { · 3 unmodified lines
385 storage: {390 storage: {
386 secrets: {391 secrets: {
387 upsert: (value: SecretMaterial) => Effect.Effect<void, Error, never>;392 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 };
388 };399 };
389 };400 };
390 };401 };
391 };402 };
392 store: SecretStore;403 store: SecretStore;
393 handle: string;404 secretStored: OnePasswordSecretStoredData;
394 name: string | null;405 name: string | null;
395 purpose?: SecretMaterialPurpose;406 purpose?: SecretMaterialPurpose;
396}): Effect.Effect<OnePasswordImportSecretResult, Error, never> =>407}): Effect.Effect<OnePasswordImportSecretResult, Error, any> =>
397 Effect.gen(function* () {408 Effect.gen(function* () {
398 const now = Date.now();409 const now = Date.now();
399 const secret: SecretMaterial = {410 const secret: SecretMaterial = {
400 id: SecretMaterialIdSchema.make(`sec_${crypto.randomUUID()}`),411 id: SecretMaterialIdSchema.make(`sec_${crypto.randomUUID()}`),
401 storeId: input.store.id,412 storeId: input.store.id,
402 handle: input.handle,
403 name: input.name,413 name: input.name,
404 purpose: input.purpose ?? "auth_material",414 purpose: input.purpose ?? "auth_material",
405 value: null,
406 createdAt: now,415 createdAt: now,
407 updatedAt: now,416 updatedAt: now,
408 };417 };
409 418
410 yield* input.executor.runtime.storage.secrets.upsert(secret);419 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 });
411 424
412 return {425 return {
413 id: secret.id,426 id: secret.id,
@@ -419,14 +432,14 @@ const createImportedSecretRecord = (input: { · 5 unmodified lines
419 };432 };
420 });433 });
421 434
422const parseSecretReference = (handle: string): {435const parseSecretReference = (uri: string): {
423 vaultId: string;436 vaultId: string;
424 itemId: string;437 itemId: string;
425 fieldId: string;438 fieldId: string;
426} => {439} => {
427 const match = /^op:\/\/([^/]+)\/([^/]+)\/([^/]+)$/.exec(handle);440 const match = /^op:\/\/([^/]+)\/([^/]+)\/([^/]+)$/.exec(uri);
428 if (!match) {441 if (!match) {
429 throw new Error(`Invalid 1Password secret reference: ${handle}`);442 throw new Error(`Invalid 1Password secret reference: ${uri}`);
430 }443 }
431 444
432 return {445 return {
@@ -461,6 +474,7 @@ export const onePasswordSdkPlugin = (input: { · 28 unmodified lines
461 OnePasswordConnectInput,474 OnePasswordConnectInput,
462 OnePasswordStoreConfigPayload,475 OnePasswordStoreConfigPayload,
463 OnePasswordStoredStoreData,476 OnePasswordStoredStoreData,
477 OnePasswordSecretStoredData,
464 OnePasswordUpdateStoreInput478 OnePasswordUpdateStoreInput
465 >({479 >({
466 key: ONEPASSWORD_SECRET_STORE_KIND,480 key: ONEPASSWORD_SECRET_STORE_KIND,
@@ -502,12 +516,12 @@ export const onePasswordSdkPlugin = (input: { · 35 unmodified lines
502 vaultId: stored.vaultId,516 vaultId: stored.vaultId,
503 auth: stored.auth,517 auth: stored.auth,
504 } satisfies OnePasswordStoreConfigPayload),518 } satisfies OnePasswordStoreConfigPayload),
505 resolveSecret: ({ secret, stored }) =>519 resolveSecret: ({ secretStored, stored }) =>
506 Effect.flatMap(makeClient(stored), (client) =>520 Effect.flatMap(makeClient(stored), (client) =>
507 Effect.tryPromise({521 Effect.tryPromise({
508 try: () =>522 try: () =>
509 runOnePasswordRequest("secret resolution", () =>523 runOnePasswordRequest("secret resolution", () =>
510 client.secrets.resolve(secret.handle)524 client.secrets.resolve(secretStored.uri)
511 ),525 ),
512 catch: (cause) =>526 catch: (cause) =>
513 cause instanceof Error ? cause : new Error(String(cause)),527 cause instanceof Error ? cause : new Error(String(cause)),
@@ -533,18 +547,20 @@ export const onePasswordSdkPlugin = (input: { · 19 unmodified lines
533 );547 );
534 548
535 return {549 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,
537 name: item.title,553 name: item.title,
538 };554 };
539 },555 },
540 catch: (cause) =>556 catch: (cause) =>
541 cause instanceof Error ? cause : new Error(String(cause)),557 cause instanceof Error ? cause : new Error(String(cause)),
542 })),558 })),
543 updateSecret: ({ secret, stored, name, value }) =>559 updateSecret: ({ secret, secretStored, stored, name, value }) =>
544 Effect.flatMap(makeClient(stored), (client) =>560 Effect.flatMap(makeClient(stored), (client) =>
545 Effect.tryPromise({561 Effect.tryPromise({
546 try: async () => {562 try: async () => {
547 const parsed = parseSecretReference(secret.handle);563 const parsed = parseSecretReference(secretStored.uri);
548 const item = await runOnePasswordRequest("secret update", () =>564 const item = await runOnePasswordRequest("secret update", () =>
549 client.items.get(parsed.vaultId, parsed.itemId)565 client.items.get(parsed.vaultId, parsed.itemId)
550 );566 );
@@ -559,16 +575,17 @@ export const onePasswordSdkPlugin = (input: { · 8 unmodified lines
559 );575 );
560 return {576 return {
561 name: updated.title,577 name: updated.title,
578 secretStored,
562 };579 };
563 },580 },
564 catch: (cause) =>581 catch: (cause) =>
565 cause instanceof Error ? cause : new Error(String(cause)),582 cause instanceof Error ? cause : new Error(String(cause)),
566 })),583 })),
567 deleteSecret: ({ secret, stored }) =>584 deleteSecret: ({ secretStored, stored }) =>
568 Effect.flatMap(makeClient(stored), (client) =>585 Effect.flatMap(makeClient(stored), (client) =>
569 Effect.tryPromise({586 Effect.tryPromise({
570 try: async () => {587 try: async () => {
571 const parsed = parseSecretReference(secret.handle);588 const parsed = parseSecretReference(secretStored.uri);
572 await runOnePasswordRequest("secret deletion", () =>589 await runOnePasswordRequest("secret deletion", () =>
573 client.items.delete(parsed.vaultId, parsed.itemId)590 client.items.delete(parsed.vaultId, parsed.itemId)
574 );591 );
@@ -662,7 +679,7 @@ export const onePasswordSdkPlugin = (input: { · 87 unmodified lines
662 return yield* createImportedSecretRecord({679 return yield* createImportedSecretRecord({
663 executor,680 executor,
664 store,681 store,
665 handle: imported.handle,682 secretStored: imported.secretStored,
666 name: imported.name,683 name: imported.name,
667 });684 });
668 }),685 }),
plugins/openapi/sdk/index.ts+0−1
@@ -202,7 +202,6 @@ const openApiStoredSourceDataFromLocalConfig = (input: { · 201 unmodified lines
202 lastSyncAt: null,202 lastSyncAt: null,
203 });203 });
204 }204 }
205
206 throw new Error("Unsupported OpenAPI local source config.");205 throw new Error("Unsupported OpenAPI local source config.");
207};206};
208 207