@executor-js/config 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../src/config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ export { ExecutorFileConfig, SourceConfig, OpenApiSourceConfig, GraphqlSourceConfig, McpRemoteSourceConfig, McpStdioSourceConfig, McpAuthConfig, SecretMetadata, ConfigHeaderValue, SECRET_REF_PREFIX, } from "./schema";
2
+ export { loadConfig, ConfigParseError } from "./load";
3
+ export { addSourceToConfig, removeSourceFromConfig, writeConfig, addSecretToConfig, removeSecretFromConfig, } from "./write";
4
+ export type { ConfigFileSink, ConfigFileSinkOptions } from "./sink";
5
+ export { makeFileConfigSink, headerToConfigValue, headersToConfigValues, } from "./sink";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAEtD,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,WAAW,EACX,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AACpE,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,QAAQ,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,267 @@
1
+ // src/schema.ts
2
+ import { Schema } from "effect";
3
+ var SECRET_REF_PREFIX = "secret-public-ref:";
4
+ var ConfigHeaderValue = Schema.Union([
5
+ Schema.String,
6
+ Schema.Struct({
7
+ value: Schema.String,
8
+ prefix: Schema.optional(Schema.String)
9
+ })
10
+ ]);
11
+ var ConfigHeaders = Schema.Record(Schema.String, ConfigHeaderValue);
12
+ var OpenApiSourceConfig = Schema.Struct({
13
+ kind: Schema.Literal("openapi"),
14
+ spec: Schema.String,
15
+ baseUrl: Schema.optional(Schema.String),
16
+ namespace: Schema.optional(Schema.String),
17
+ headers: Schema.optional(ConfigHeaders)
18
+ });
19
+ var GraphqlSourceConfig = Schema.Struct({
20
+ kind: Schema.Literal("graphql"),
21
+ endpoint: Schema.String,
22
+ introspectionJson: Schema.optional(Schema.NullOr(Schema.String)),
23
+ namespace: Schema.optional(Schema.String),
24
+ headers: Schema.optional(ConfigHeaders)
25
+ });
26
+ var StringMap = Schema.Record(Schema.String, Schema.String);
27
+ var McpAuthConfig = Schema.Union([
28
+ Schema.Struct({ kind: Schema.Literal("none") }),
29
+ Schema.Struct({
30
+ kind: Schema.Literal("header"),
31
+ headerName: Schema.String,
32
+ secret: Schema.String,
33
+ prefix: Schema.optional(Schema.String)
34
+ }),
35
+ Schema.Struct({
36
+ kind: Schema.Literal("oauth2"),
37
+ /** Stable id of the SDK Connection holding access + refresh token
38
+ * material. Scope shadowing means the same id resolves per-user
39
+ * via the executor's innermost-wins lookup. */
40
+ connectionId: Schema.String
41
+ })
42
+ ]);
43
+ var McpRemoteSourceConfig = Schema.Struct({
44
+ kind: Schema.Literal("mcp"),
45
+ transport: Schema.Literal("remote"),
46
+ name: Schema.String,
47
+ endpoint: Schema.String,
48
+ remoteTransport: Schema.optional(Schema.Literals(["streamable-http", "sse", "auto"])),
49
+ namespace: Schema.optional(Schema.String),
50
+ queryParams: Schema.optional(StringMap),
51
+ headers: Schema.optional(StringMap),
52
+ auth: Schema.optional(McpAuthConfig)
53
+ });
54
+ var McpStdioSourceConfig = Schema.Struct({
55
+ kind: Schema.Literal("mcp"),
56
+ transport: Schema.Literal("stdio"),
57
+ name: Schema.String,
58
+ command: Schema.String,
59
+ args: Schema.optional(Schema.Array(Schema.String)),
60
+ env: Schema.optional(StringMap),
61
+ cwd: Schema.optional(Schema.String),
62
+ namespace: Schema.optional(Schema.String)
63
+ });
64
+ var SourceConfig = Schema.Union([
65
+ OpenApiSourceConfig,
66
+ GraphqlSourceConfig,
67
+ McpRemoteSourceConfig,
68
+ McpStdioSourceConfig
69
+ ]);
70
+ var SecretMetadata = Schema.Struct({
71
+ name: Schema.String,
72
+ provider: Schema.optional(Schema.String),
73
+ purpose: Schema.optional(Schema.String)
74
+ });
75
+ var ExecutorFileConfig = Schema.Struct({
76
+ $schema: Schema.optional(Schema.String),
77
+ name: Schema.optional(Schema.String),
78
+ sources: Schema.optional(Schema.Array(SourceConfig)),
79
+ secrets: Schema.optional(Schema.Record(Schema.String, SecretMetadata))
80
+ });
81
+
82
+ // src/load.ts
83
+ import { Effect, Schema as Schema2 } from "effect";
84
+ import { FileSystem } from "effect";
85
+ import * as jsonc from "jsonc-parser";
86
+ var ConfigParseError = class {
87
+ constructor(path, message) {
88
+ this.path = path;
89
+ this.message = message;
90
+ }
91
+ path;
92
+ message;
93
+ _tag = "ConfigParseError";
94
+ };
95
+ var loadConfig = (path) => Effect.gen(function* () {
96
+ const fs = yield* FileSystem.FileSystem;
97
+ const exists = yield* fs.exists(path);
98
+ if (!exists) return null;
99
+ const raw = yield* fs.readFileString(path);
100
+ const errors = [];
101
+ const parsed = jsonc.parse(raw, errors);
102
+ if (errors.length > 0) {
103
+ const msg = errors.map((e) => `offset ${e.offset}: ${jsonc.printParseErrorCode(e.error)}`).join("; ");
104
+ return yield* Effect.fail(new ConfigParseError(path, msg));
105
+ }
106
+ const decoded = yield* Schema2.decodeUnknownEffect(ExecutorFileConfig)(parsed).pipe(
107
+ Effect.mapError((e) => new ConfigParseError(path, String(e)))
108
+ );
109
+ return decoded;
110
+ });
111
+
112
+ // src/write.ts
113
+ import { Effect as Effect2 } from "effect";
114
+ import { FileSystem as FileSystem2 } from "effect";
115
+ import * as jsonc2 from "jsonc-parser";
116
+ var FORMATTING = {
117
+ tabSize: 2,
118
+ insertSpaces: true,
119
+ eol: "\n"
120
+ };
121
+ var DEFAULT_CONFIG = `{
122
+ "sources": []
123
+ }
124
+ `;
125
+ var readOrCreate = (fs, path) => Effect2.gen(function* () {
126
+ const exists = yield* fs.exists(path);
127
+ if (exists) return yield* fs.readFileString(path);
128
+ yield* fs.writeFileString(path, DEFAULT_CONFIG);
129
+ return DEFAULT_CONFIG;
130
+ });
131
+ var addSourceToConfig = (path, source) => Effect2.gen(function* () {
132
+ const fs = yield* FileSystem2.FileSystem;
133
+ let text = yield* readOrCreate(fs, path);
134
+ let tree = jsonc2.parseTree(text);
135
+ let sourcesNode = tree ? jsonc2.findNodeAtLocation(tree, ["sources"]) : void 0;
136
+ if (!sourcesNode) {
137
+ const edits = jsonc2.modify(text, ["sources"], [source], {
138
+ formattingOptions: FORMATTING
139
+ });
140
+ text = jsonc2.applyEdits(text, edits);
141
+ } else {
142
+ const ns = "namespace" in source ? source.namespace : void 0;
143
+ if (ns && sourcesNode.children) {
144
+ for (let i = sourcesNode.children.length - 1; i >= 0; i--) {
145
+ const child = sourcesNode.children[i];
146
+ const nsNode = jsonc2.findNodeAtLocation(child, ["namespace"]);
147
+ if (nsNode && jsonc2.getNodeValue(nsNode) === ns) {
148
+ const edits2 = jsonc2.modify(text, ["sources", i], void 0, {
149
+ formattingOptions: FORMATTING
150
+ });
151
+ text = jsonc2.applyEdits(text, edits2);
152
+ }
153
+ }
154
+ tree = jsonc2.parseTree(text);
155
+ sourcesNode = tree ? jsonc2.findNodeAtLocation(tree, ["sources"]) : void 0;
156
+ }
157
+ const count = sourcesNode?.children?.length ?? 0;
158
+ const edits = jsonc2.modify(text, ["sources", count], source, {
159
+ formattingOptions: FORMATTING
160
+ });
161
+ text = jsonc2.applyEdits(text, edits);
162
+ }
163
+ yield* fs.writeFileString(path, text);
164
+ });
165
+ var removeSourceFromConfig = (path, namespace) => Effect2.gen(function* () {
166
+ const fs = yield* FileSystem2.FileSystem;
167
+ const exists = yield* fs.exists(path);
168
+ if (!exists) return;
169
+ let text = yield* fs.readFileString(path);
170
+ const tree = jsonc2.parseTree(text);
171
+ if (!tree) return;
172
+ const sourcesNode = jsonc2.findNodeAtLocation(tree, ["sources"]);
173
+ if (!sourcesNode?.children) return;
174
+ for (let i = sourcesNode.children.length - 1; i >= 0; i--) {
175
+ const child = sourcesNode.children[i];
176
+ const nsNode = jsonc2.findNodeAtLocation(child, ["namespace"]);
177
+ if (nsNode && jsonc2.getNodeValue(nsNode) === namespace) {
178
+ const edits = jsonc2.modify(text, ["sources", i], void 0, {
179
+ formattingOptions: FORMATTING
180
+ });
181
+ text = jsonc2.applyEdits(text, edits);
182
+ }
183
+ }
184
+ yield* fs.writeFileString(path, text);
185
+ });
186
+ var writeConfig = (path, config) => Effect2.gen(function* () {
187
+ const fs = yield* FileSystem2.FileSystem;
188
+ const text = yield* Effect2.try({
189
+ try: () => JSON.stringify(config, null, 2) + "\n",
190
+ catch: (cause) => cause
191
+ }).pipe(Effect2.orDie);
192
+ yield* fs.writeFileString(path, text);
193
+ });
194
+ var addSecretToConfig = (path, secretId, metadata) => Effect2.gen(function* () {
195
+ const fs = yield* FileSystem2.FileSystem;
196
+ let text = yield* readOrCreate(fs, path);
197
+ const edits = jsonc2.modify(text, ["secrets", secretId], metadata, {
198
+ formattingOptions: FORMATTING
199
+ });
200
+ text = jsonc2.applyEdits(text, edits);
201
+ yield* fs.writeFileString(path, text);
202
+ });
203
+ var removeSecretFromConfig = (path, secretId) => Effect2.gen(function* () {
204
+ const fs = yield* FileSystem2.FileSystem;
205
+ const exists = yield* fs.exists(path);
206
+ if (!exists) return;
207
+ let text = yield* fs.readFileString(path);
208
+ const edits = jsonc2.modify(text, ["secrets", secretId], void 0, {
209
+ formattingOptions: FORMATTING
210
+ });
211
+ text = jsonc2.applyEdits(text, edits);
212
+ yield* fs.writeFileString(path, text);
213
+ });
214
+
215
+ // src/sink.ts
216
+ import { Effect as Effect3 } from "effect";
217
+ var headerToConfigValue = (value) => {
218
+ if (typeof value === "string") return value;
219
+ const ref = `${SECRET_REF_PREFIX}${value.secretId}`;
220
+ return value.prefix ? { value: ref, prefix: value.prefix } : ref;
221
+ };
222
+ var headersToConfigValues = (headers) => {
223
+ if (!headers) return void 0;
224
+ const out = {};
225
+ for (const [k, v] of Object.entries(headers)) out[k] = headerToConfigValue(v);
226
+ return out;
227
+ };
228
+ var defaultOnError = (op, err) => {
229
+ const msg = err instanceof Error ? err.message : String(err);
230
+ console.warn(`[config-sink] ${op} failed: ${msg}`);
231
+ };
232
+ var makeFileConfigSink = (options) => {
233
+ const { path, fsLayer, onError = defaultOnError } = options;
234
+ return {
235
+ upsertSource: (source) => addSourceToConfig(path, source).pipe(
236
+ Effect3.provide(fsLayer),
237
+ Effect3.catch((err) => Effect3.sync(() => onError("upsert", err)))
238
+ ),
239
+ removeSource: (namespace) => removeSourceFromConfig(path, namespace).pipe(
240
+ Effect3.provide(fsLayer),
241
+ Effect3.catch((err) => Effect3.sync(() => onError("remove", err)))
242
+ )
243
+ };
244
+ };
245
+ export {
246
+ ConfigHeaderValue,
247
+ ConfigParseError,
248
+ ExecutorFileConfig,
249
+ GraphqlSourceConfig,
250
+ McpAuthConfig,
251
+ McpRemoteSourceConfig,
252
+ McpStdioSourceConfig,
253
+ OpenApiSourceConfig,
254
+ SECRET_REF_PREFIX,
255
+ SecretMetadata,
256
+ SourceConfig,
257
+ addSecretToConfig,
258
+ addSourceToConfig,
259
+ headerToConfigValue,
260
+ headersToConfigValues,
261
+ loadConfig,
262
+ makeFileConfigSink,
263
+ removeSecretFromConfig,
264
+ removeSourceFromConfig,
265
+ writeConfig
266
+ };
267
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts","../src/load.ts","../src/write.ts","../src/sink.ts"],"sourcesContent":["import { Schema } from \"effect\";\n\n// ---------------------------------------------------------------------------\n// Header values\n//\n// Three forms:\n// \"static-value\" — literal string\n// \"secret-public-ref:my-token\" — secret reference (no prefix)\n// { value: \"secret-public-ref:x\", prefix } — secret reference with prefix\n// ---------------------------------------------------------------------------\n\nexport const SECRET_REF_PREFIX = \"secret-public-ref:\";\n\nexport const ConfigHeaderValue = Schema.Union([\n Schema.String,\n Schema.Struct({\n value: Schema.String,\n prefix: Schema.optional(Schema.String),\n }),\n]);\nexport type ConfigHeaderValue = typeof ConfigHeaderValue.Type;\n\nconst ConfigHeaders = Schema.Record(Schema.String, ConfigHeaderValue);\n\n// ---------------------------------------------------------------------------\n// Source configs — discriminated union on \"kind\"\n// ---------------------------------------------------------------------------\n\nexport const OpenApiSourceConfig = Schema.Struct({\n kind: Schema.Literal(\"openapi\"),\n spec: Schema.String,\n baseUrl: Schema.optional(Schema.String),\n namespace: Schema.optional(Schema.String),\n headers: Schema.optional(ConfigHeaders),\n});\nexport type OpenApiSourceConfig = typeof OpenApiSourceConfig.Type;\n\nexport const GraphqlSourceConfig = Schema.Struct({\n kind: Schema.Literal(\"graphql\"),\n endpoint: Schema.String,\n introspectionJson: Schema.optional(Schema.NullOr(Schema.String)),\n namespace: Schema.optional(Schema.String),\n headers: Schema.optional(ConfigHeaders),\n});\nexport type GraphqlSourceConfig = typeof GraphqlSourceConfig.Type;\n\nconst StringMap = Schema.Record(Schema.String, Schema.String);\n\nexport const McpAuthConfig = Schema.Union([\n Schema.Struct({ kind: Schema.Literal(\"none\") }),\n Schema.Struct({\n kind: Schema.Literal(\"header\"),\n headerName: Schema.String,\n secret: Schema.String,\n prefix: Schema.optional(Schema.String),\n }),\n Schema.Struct({\n kind: Schema.Literal(\"oauth2\"),\n /** Stable id of the SDK Connection holding access + refresh token\n * material. Scope shadowing means the same id resolves per-user\n * via the executor's innermost-wins lookup. */\n connectionId: Schema.String,\n }),\n]);\nexport type McpAuthConfig = typeof McpAuthConfig.Type;\n\nexport const McpRemoteSourceConfig = Schema.Struct({\n kind: Schema.Literal(\"mcp\"),\n transport: Schema.Literal(\"remote\"),\n name: Schema.String,\n endpoint: Schema.String,\n remoteTransport: Schema.optional(Schema.Literals([\"streamable-http\", \"sse\", \"auto\"])),\n namespace: Schema.optional(Schema.String),\n queryParams: Schema.optional(StringMap),\n headers: Schema.optional(StringMap),\n auth: Schema.optional(McpAuthConfig),\n});\nexport type McpRemoteSourceConfig = typeof McpRemoteSourceConfig.Type;\n\nexport const McpStdioSourceConfig = Schema.Struct({\n kind: Schema.Literal(\"mcp\"),\n transport: Schema.Literal(\"stdio\"),\n name: Schema.String,\n command: Schema.String,\n args: Schema.optional(Schema.Array(Schema.String)),\n env: Schema.optional(StringMap),\n cwd: Schema.optional(Schema.String),\n namespace: Schema.optional(Schema.String),\n});\nexport type McpStdioSourceConfig = typeof McpStdioSourceConfig.Type;\n\nexport const SourceConfig = Schema.Union([\n OpenApiSourceConfig,\n GraphqlSourceConfig,\n McpRemoteSourceConfig,\n McpStdioSourceConfig,\n]);\nexport type SourceConfig = typeof SourceConfig.Type;\n\n// ---------------------------------------------------------------------------\n// Secret metadata\n// ---------------------------------------------------------------------------\n\nexport const SecretMetadata = Schema.Struct({\n name: Schema.String,\n provider: Schema.optional(Schema.String),\n purpose: Schema.optional(Schema.String),\n});\nexport type SecretMetadata = typeof SecretMetadata.Type;\n\n// ---------------------------------------------------------------------------\n// Top-level config\n// ---------------------------------------------------------------------------\n\nexport const ExecutorFileConfig = Schema.Struct({\n $schema: Schema.optional(Schema.String),\n name: Schema.optional(Schema.String),\n sources: Schema.optional(Schema.Array(SourceConfig)),\n secrets: Schema.optional(Schema.Record(Schema.String, SecretMetadata)),\n});\nexport type ExecutorFileConfig = typeof ExecutorFileConfig.Type;\n","import { Effect, Schema } from \"effect\";\nimport { FileSystem } from \"effect\";\nimport type { PlatformError } from \"effect/PlatformError\";\nimport * as jsonc from \"jsonc-parser\";\nimport { ExecutorFileConfig } from \"./schema\";\n\nexport class ConfigParseError {\n readonly _tag = \"ConfigParseError\";\n constructor(\n readonly path: string,\n readonly message: string,\n ) {}\n}\n\n/**\n * Load and validate an executor config file.\n * Returns null if the file doesn't exist.\n */\nexport const loadConfig = (\n path: string,\n): Effect.Effect<\n ExecutorFileConfig | null,\n ConfigParseError | PlatformError,\n FileSystem.FileSystem\n> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n\n const exists = yield* fs.exists(path);\n if (!exists) return null;\n\n const raw = yield* fs.readFileString(path);\n\n const errors: jsonc.ParseError[] = [];\n const parsed = jsonc.parse(raw, errors);\n\n if (errors.length > 0) {\n const msg = errors\n .map((e) => `offset ${e.offset}: ${jsonc.printParseErrorCode(e.error)}`)\n .join(\"; \");\n return yield* Effect.fail(new ConfigParseError(path, msg));\n }\n\n const decoded = yield* Schema.decodeUnknownEffect(ExecutorFileConfig)(parsed).pipe(\n Effect.mapError((e) => new ConfigParseError(path, String(e))),\n );\n\n return decoded;\n });\n","import { Effect } from \"effect\";\nimport { FileSystem } from \"effect\";\nimport type { PlatformError } from \"effect/PlatformError\";\nimport * as jsonc from \"jsonc-parser\";\nimport type { SourceConfig, ExecutorFileConfig } from \"./schema\";\n\nconst FORMATTING: jsonc.FormattingOptions = {\n tabSize: 2,\n insertSpaces: true,\n eol: \"\\n\",\n};\n\nconst DEFAULT_CONFIG = `{\n \"sources\": []\n}\n`;\n\n/** Read the raw JSONC text from a config file, or create a default one. */\nconst readOrCreate = (\n fs: FileSystem.FileSystem,\n path: string,\n): Effect.Effect<string, PlatformError> =>\n Effect.gen(function* () {\n const exists = yield* fs.exists(path);\n if (exists) return yield* fs.readFileString(path);\n yield* fs.writeFileString(path, DEFAULT_CONFIG);\n return DEFAULT_CONFIG;\n });\n\n/**\n * Add a source entry to the config file. Creates the file if it doesn't exist.\n * Preserves existing comments and formatting.\n */\nexport const addSourceToConfig = (\n path: string,\n source: SourceConfig,\n): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n let text = yield* readOrCreate(fs, path);\n\n // Ensure \"sources\" array exists\n let tree = jsonc.parseTree(text);\n let sourcesNode = tree ? jsonc.findNodeAtLocation(tree, [\"sources\"]) : undefined;\n\n if (!sourcesNode) {\n const edits = jsonc.modify(text, [\"sources\"], [source], {\n formattingOptions: FORMATTING,\n });\n text = jsonc.applyEdits(text, edits);\n } else {\n // Remove existing entry with same namespace (if any) to avoid duplicates\n const ns = \"namespace\" in source ? source.namespace : undefined;\n if (ns && sourcesNode.children) {\n for (let i = sourcesNode.children.length - 1; i >= 0; i--) {\n const child = sourcesNode.children[i]!;\n const nsNode = jsonc.findNodeAtLocation(child, [\"namespace\"]);\n if (nsNode && jsonc.getNodeValue(nsNode) === ns) {\n const edits = jsonc.modify(text, [\"sources\", i], undefined, {\n formattingOptions: FORMATTING,\n });\n text = jsonc.applyEdits(text, edits);\n }\n }\n // Re-parse after removals\n tree = jsonc.parseTree(text);\n sourcesNode = tree ? jsonc.findNodeAtLocation(tree, [\"sources\"]) : undefined;\n }\n\n const count = sourcesNode?.children?.length ?? 0;\n const edits = jsonc.modify(text, [\"sources\", count], source, {\n formattingOptions: FORMATTING,\n });\n text = jsonc.applyEdits(text, edits);\n }\n\n yield* fs.writeFileString(path, text);\n });\n\n/**\n * Remove a source from the config file by namespace.\n */\nexport const removeSourceFromConfig = (\n path: string,\n namespace: string,\n): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n\n const exists = yield* fs.exists(path);\n if (!exists) return;\n\n let text = yield* fs.readFileString(path);\n const tree = jsonc.parseTree(text);\n if (!tree) return;\n\n const sourcesNode = jsonc.findNodeAtLocation(tree, [\"sources\"]);\n if (!sourcesNode?.children) return;\n\n // Walk backwards so indices stay valid after each removal\n for (let i = sourcesNode.children.length - 1; i >= 0; i--) {\n const child = sourcesNode.children[i]!;\n const nsNode = jsonc.findNodeAtLocation(child, [\"namespace\"]);\n if (nsNode && jsonc.getNodeValue(nsNode) === namespace) {\n const edits = jsonc.modify(text, [\"sources\", i], undefined, {\n formattingOptions: FORMATTING,\n });\n text = jsonc.applyEdits(text, edits);\n }\n }\n\n yield* fs.writeFileString(path, text);\n });\n\n/**\n * Write a full config object to a file.\n */\nexport const writeConfig = (\n path: string,\n config: ExecutorFileConfig,\n): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const text = yield* Effect.try({\n try: () => JSON.stringify(config, null, 2) + \"\\n\",\n catch: (cause) => cause,\n }).pipe(Effect.orDie);\n yield* fs.writeFileString(path, text);\n });\n\n/**\n * Add secret metadata to the config file.\n */\nexport const addSecretToConfig = (\n path: string,\n secretId: string,\n metadata: { name: string; provider?: string; purpose?: string },\n): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n let text = yield* readOrCreate(fs, path);\n\n const edits = jsonc.modify(text, [\"secrets\", secretId], metadata, {\n formattingOptions: FORMATTING,\n });\n text = jsonc.applyEdits(text, edits);\n\n yield* fs.writeFileString(path, text);\n });\n\n/**\n * Remove secret metadata from the config file.\n */\nexport const removeSecretFromConfig = (\n path: string,\n secretId: string,\n): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n\n const exists = yield* fs.exists(path);\n if (!exists) return;\n\n let text = yield* fs.readFileString(path);\n const edits = jsonc.modify(text, [\"secrets\", secretId], undefined, {\n formattingOptions: FORMATTING,\n });\n text = jsonc.applyEdits(text, edits);\n\n yield* fs.writeFileString(path, text);\n });\n","// ---------------------------------------------------------------------------\n// ConfigFileSink — best-effort write-back of source changes to executor.jsonc.\n//\n// Plugins (openapi, graphql, mcp) call `sink.upsertSource` after their DB\n// writes so the committable file stays in sync with runtime state. Errors\n// are logged and swallowed — a failed file write must never fail a DB\n// mutation, and the next successful mutation (or a boot-time sync) will\n// eventually reconcile.\n//\n// The FileSystem layer is injected so library code here doesn't pick a\n// platform binding. The host app provides NodeFileSystem (or BunFileSystem).\n// ---------------------------------------------------------------------------\n\nimport { Effect } from \"effect\";\nimport type { Layer } from \"effect\";\nimport type { FileSystem } from \"effect\";\n\nimport { SECRET_REF_PREFIX, type ConfigHeaderValue, type SourceConfig } from \"./schema\";\nimport { addSourceToConfig, removeSourceFromConfig } from \"./write\";\n\n// Translate a plugin-side header value (`{ secretId, prefix? }` for secret\n// refs) into the config file's `secret-public-ref:<id>` string form.\ntype PluginHeaderValue = string | { secretId: string; prefix?: string };\n\nexport const headerToConfigValue = (\n value: PluginHeaderValue,\n): ConfigHeaderValue => {\n if (typeof value === \"string\") return value;\n const ref = `${SECRET_REF_PREFIX}${value.secretId}`;\n return value.prefix ? { value: ref, prefix: value.prefix } : ref;\n};\n\nexport const headersToConfigValues = (\n headers: Record<string, PluginHeaderValue> | undefined,\n): Record<string, ConfigHeaderValue> | undefined => {\n if (!headers) return undefined;\n const out: Record<string, ConfigHeaderValue> = {};\n for (const [k, v] of Object.entries(headers)) out[k] = headerToConfigValue(v);\n return out;\n};\n\nexport interface ConfigFileSink {\n readonly upsertSource: (source: SourceConfig) => Effect.Effect<void>;\n readonly removeSource: (namespace: string) => Effect.Effect<void>;\n}\n\nexport interface ConfigFileSinkOptions {\n readonly path: string;\n readonly fsLayer: Layer.Layer<FileSystem.FileSystem>;\n /** Called when a file operation fails. Defaults to console.warn. */\n readonly onError?: (op: \"upsert\" | \"remove\", err: unknown) => void;\n}\n\nconst defaultOnError = (op: \"upsert\" | \"remove\", err: unknown): void => {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[config-sink] ${op} failed: ${msg}`);\n};\n\nexport const makeFileConfigSink = (\n options: ConfigFileSinkOptions,\n): ConfigFileSink => {\n const { path, fsLayer, onError = defaultOnError } = options;\n\n return {\n upsertSource: (source) =>\n addSourceToConfig(path, source).pipe(\n Effect.provide(fsLayer),\n Effect.catch((err: unknown) => Effect.sync(() => onError(\"upsert\", err))),\n ),\n\n removeSource: (namespace) =>\n removeSourceFromConfig(path, namespace).pipe(\n Effect.provide(fsLayer),\n Effect.catch((err: unknown) => Effect.sync(() => onError(\"remove\", err))),\n ),\n };\n};\n"],"mappings":"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","names":["Schema","Schema","Effect","FileSystem","jsonc","edits","Effect","Effect"]}
package/dist/load.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { Effect } from "effect";
2
+ import { FileSystem } from "effect";
3
+ import type { PlatformError } from "effect/PlatformError";
4
+ import { ExecutorFileConfig } from "./schema";
5
+ export declare class ConfigParseError {
6
+ readonly path: string;
7
+ readonly message: string;
8
+ readonly _tag = "ConfigParseError";
9
+ constructor(path: string, message: string);
10
+ }
11
+ /**
12
+ * Load and validate an executor config file.
13
+ * Returns null if the file doesn't exist.
14
+ */
15
+ export declare const loadConfig: (path: string) => Effect.Effect<ExecutorFileConfig | null, ConfigParseError | PlatformError, FileSystem.FileSystem>;
16
+ //# sourceMappingURL=load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C,qBAAa,gBAAgB;IAGzB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM;IAH1B,QAAQ,CAAC,IAAI,sBAAsB;gBAExB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM;CAE3B;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,GACrB,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CACd,kBAAkB,GAAG,IAAI,EACzB,gBAAgB,GAAG,aAAa,EAChC,UAAU,CAAC,UAAU,CAyBnB,CAAC"}
@@ -0,0 +1,200 @@
1
+ import { Schema } from "effect";
2
+ export declare const SECRET_REF_PREFIX = "secret-public-ref:";
3
+ export declare const ConfigHeaderValue: Schema.Union<readonly [Schema.String, Schema.Struct<{
4
+ readonly value: Schema.String;
5
+ readonly prefix: Schema.optional<Schema.String>;
6
+ }>]>;
7
+ export type ConfigHeaderValue = typeof ConfigHeaderValue.Type;
8
+ export declare const OpenApiSourceConfig: Schema.Struct<{
9
+ readonly kind: Schema.Literal<"openapi">;
10
+ readonly spec: Schema.String;
11
+ readonly baseUrl: Schema.optional<Schema.String>;
12
+ readonly namespace: Schema.optional<Schema.String>;
13
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Struct<{
14
+ readonly value: Schema.String;
15
+ readonly prefix: Schema.optional<Schema.String>;
16
+ }>]>>>;
17
+ }>;
18
+ export type OpenApiSourceConfig = typeof OpenApiSourceConfig.Type;
19
+ export declare const GraphqlSourceConfig: Schema.Struct<{
20
+ readonly kind: Schema.Literal<"graphql">;
21
+ readonly endpoint: Schema.String;
22
+ readonly introspectionJson: Schema.optional<Schema.NullOr<Schema.String>>;
23
+ readonly namespace: Schema.optional<Schema.String>;
24
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Struct<{
25
+ readonly value: Schema.String;
26
+ readonly prefix: Schema.optional<Schema.String>;
27
+ }>]>>>;
28
+ }>;
29
+ export type GraphqlSourceConfig = typeof GraphqlSourceConfig.Type;
30
+ export declare const McpAuthConfig: Schema.Union<readonly [Schema.Struct<{
31
+ readonly kind: Schema.Literal<"none">;
32
+ }>, Schema.Struct<{
33
+ readonly kind: Schema.Literal<"header">;
34
+ readonly headerName: Schema.String;
35
+ readonly secret: Schema.String;
36
+ readonly prefix: Schema.optional<Schema.String>;
37
+ }>, Schema.Struct<{
38
+ readonly kind: Schema.Literal<"oauth2">;
39
+ /** Stable id of the SDK Connection holding access + refresh token
40
+ * material. Scope shadowing means the same id resolves per-user
41
+ * via the executor's innermost-wins lookup. */
42
+ readonly connectionId: Schema.String;
43
+ }>]>;
44
+ export type McpAuthConfig = typeof McpAuthConfig.Type;
45
+ export declare const McpRemoteSourceConfig: Schema.Struct<{
46
+ readonly kind: Schema.Literal<"mcp">;
47
+ readonly transport: Schema.Literal<"remote">;
48
+ readonly name: Schema.String;
49
+ readonly endpoint: Schema.String;
50
+ readonly remoteTransport: Schema.optional<Schema.Literals<readonly ["streamable-http", "sse", "auto"]>>;
51
+ readonly namespace: Schema.optional<Schema.String>;
52
+ readonly queryParams: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
53
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
54
+ readonly auth: Schema.optional<Schema.Union<readonly [Schema.Struct<{
55
+ readonly kind: Schema.Literal<"none">;
56
+ }>, Schema.Struct<{
57
+ readonly kind: Schema.Literal<"header">;
58
+ readonly headerName: Schema.String;
59
+ readonly secret: Schema.String;
60
+ readonly prefix: Schema.optional<Schema.String>;
61
+ }>, Schema.Struct<{
62
+ readonly kind: Schema.Literal<"oauth2">;
63
+ /** Stable id of the SDK Connection holding access + refresh token
64
+ * material. Scope shadowing means the same id resolves per-user
65
+ * via the executor's innermost-wins lookup. */
66
+ readonly connectionId: Schema.String;
67
+ }>]>>;
68
+ }>;
69
+ export type McpRemoteSourceConfig = typeof McpRemoteSourceConfig.Type;
70
+ export declare const McpStdioSourceConfig: Schema.Struct<{
71
+ readonly kind: Schema.Literal<"mcp">;
72
+ readonly transport: Schema.Literal<"stdio">;
73
+ readonly name: Schema.String;
74
+ readonly command: Schema.String;
75
+ readonly args: Schema.optional<Schema.$Array<Schema.String>>;
76
+ readonly env: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
77
+ readonly cwd: Schema.optional<Schema.String>;
78
+ readonly namespace: Schema.optional<Schema.String>;
79
+ }>;
80
+ export type McpStdioSourceConfig = typeof McpStdioSourceConfig.Type;
81
+ export declare const SourceConfig: Schema.Union<readonly [Schema.Struct<{
82
+ readonly kind: Schema.Literal<"openapi">;
83
+ readonly spec: Schema.String;
84
+ readonly baseUrl: Schema.optional<Schema.String>;
85
+ readonly namespace: Schema.optional<Schema.String>;
86
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Struct<{
87
+ readonly value: Schema.String;
88
+ readonly prefix: Schema.optional<Schema.String>;
89
+ }>]>>>;
90
+ }>, Schema.Struct<{
91
+ readonly kind: Schema.Literal<"graphql">;
92
+ readonly endpoint: Schema.String;
93
+ readonly introspectionJson: Schema.optional<Schema.NullOr<Schema.String>>;
94
+ readonly namespace: Schema.optional<Schema.String>;
95
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Struct<{
96
+ readonly value: Schema.String;
97
+ readonly prefix: Schema.optional<Schema.String>;
98
+ }>]>>>;
99
+ }>, Schema.Struct<{
100
+ readonly kind: Schema.Literal<"mcp">;
101
+ readonly transport: Schema.Literal<"remote">;
102
+ readonly name: Schema.String;
103
+ readonly endpoint: Schema.String;
104
+ readonly remoteTransport: Schema.optional<Schema.Literals<readonly ["streamable-http", "sse", "auto"]>>;
105
+ readonly namespace: Schema.optional<Schema.String>;
106
+ readonly queryParams: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
107
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
108
+ readonly auth: Schema.optional<Schema.Union<readonly [Schema.Struct<{
109
+ readonly kind: Schema.Literal<"none">;
110
+ }>, Schema.Struct<{
111
+ readonly kind: Schema.Literal<"header">;
112
+ readonly headerName: Schema.String;
113
+ readonly secret: Schema.String;
114
+ readonly prefix: Schema.optional<Schema.String>;
115
+ }>, Schema.Struct<{
116
+ readonly kind: Schema.Literal<"oauth2">;
117
+ /** Stable id of the SDK Connection holding access + refresh token
118
+ * material. Scope shadowing means the same id resolves per-user
119
+ * via the executor's innermost-wins lookup. */
120
+ readonly connectionId: Schema.String;
121
+ }>]>>;
122
+ }>, Schema.Struct<{
123
+ readonly kind: Schema.Literal<"mcp">;
124
+ readonly transport: Schema.Literal<"stdio">;
125
+ readonly name: Schema.String;
126
+ readonly command: Schema.String;
127
+ readonly args: Schema.optional<Schema.$Array<Schema.String>>;
128
+ readonly env: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
129
+ readonly cwd: Schema.optional<Schema.String>;
130
+ readonly namespace: Schema.optional<Schema.String>;
131
+ }>]>;
132
+ export type SourceConfig = typeof SourceConfig.Type;
133
+ export declare const SecretMetadata: Schema.Struct<{
134
+ readonly name: Schema.String;
135
+ readonly provider: Schema.optional<Schema.String>;
136
+ readonly purpose: Schema.optional<Schema.String>;
137
+ }>;
138
+ export type SecretMetadata = typeof SecretMetadata.Type;
139
+ export declare const ExecutorFileConfig: Schema.Struct<{
140
+ readonly $schema: Schema.optional<Schema.String>;
141
+ readonly name: Schema.optional<Schema.String>;
142
+ readonly sources: Schema.optional<Schema.$Array<Schema.Union<readonly [Schema.Struct<{
143
+ readonly kind: Schema.Literal<"openapi">;
144
+ readonly spec: Schema.String;
145
+ readonly baseUrl: Schema.optional<Schema.String>;
146
+ readonly namespace: Schema.optional<Schema.String>;
147
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Struct<{
148
+ readonly value: Schema.String;
149
+ readonly prefix: Schema.optional<Schema.String>;
150
+ }>]>>>;
151
+ }>, Schema.Struct<{
152
+ readonly kind: Schema.Literal<"graphql">;
153
+ readonly endpoint: Schema.String;
154
+ readonly introspectionJson: Schema.optional<Schema.NullOr<Schema.String>>;
155
+ readonly namespace: Schema.optional<Schema.String>;
156
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.String, Schema.Struct<{
157
+ readonly value: Schema.String;
158
+ readonly prefix: Schema.optional<Schema.String>;
159
+ }>]>>>;
160
+ }>, Schema.Struct<{
161
+ readonly kind: Schema.Literal<"mcp">;
162
+ readonly transport: Schema.Literal<"remote">;
163
+ readonly name: Schema.String;
164
+ readonly endpoint: Schema.String;
165
+ readonly remoteTransport: Schema.optional<Schema.Literals<readonly ["streamable-http", "sse", "auto"]>>;
166
+ readonly namespace: Schema.optional<Schema.String>;
167
+ readonly queryParams: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
168
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
169
+ readonly auth: Schema.optional<Schema.Union<readonly [Schema.Struct<{
170
+ readonly kind: Schema.Literal<"none">;
171
+ }>, Schema.Struct<{
172
+ readonly kind: Schema.Literal<"header">;
173
+ readonly headerName: Schema.String;
174
+ readonly secret: Schema.String;
175
+ readonly prefix: Schema.optional<Schema.String>;
176
+ }>, Schema.Struct<{
177
+ readonly kind: Schema.Literal<"oauth2">;
178
+ /** Stable id of the SDK Connection holding access + refresh token
179
+ * material. Scope shadowing means the same id resolves per-user
180
+ * via the executor's innermost-wins lookup. */
181
+ readonly connectionId: Schema.String;
182
+ }>]>>;
183
+ }>, Schema.Struct<{
184
+ readonly kind: Schema.Literal<"mcp">;
185
+ readonly transport: Schema.Literal<"stdio">;
186
+ readonly name: Schema.String;
187
+ readonly command: Schema.String;
188
+ readonly args: Schema.optional<Schema.$Array<Schema.String>>;
189
+ readonly env: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
190
+ readonly cwd: Schema.optional<Schema.String>;
191
+ readonly namespace: Schema.optional<Schema.String>;
192
+ }>]>>>;
193
+ readonly secrets: Schema.optional<Schema.$Record<Schema.String, Schema.Struct<{
194
+ readonly name: Schema.String;
195
+ readonly provider: Schema.optional<Schema.String>;
196
+ readonly purpose: Schema.optional<Schema.String>;
197
+ }>>>;
198
+ }>;
199
+ export type ExecutorFileConfig = typeof ExecutorFileConfig.Type;
200
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAWhC,eAAO,MAAM,iBAAiB,uBAAuB,CAAC;AAEtD,eAAO,MAAM,iBAAiB;;;IAM5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC,IAAI,CAAC;AAQ9D,eAAO,MAAM,mBAAmB;;;;;;;;;EAM9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAC,IAAI,CAAC;AAElE,eAAO,MAAM,mBAAmB;;;;;;;;;EAM9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAC,IAAI,CAAC;AAIlE,eAAO,MAAM,aAAa;;;;;;;;;IAUtB;;oDAEgD;;IAGlD,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAC;AAEtD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;QAR9B;;wDAEgD;;;EAgBlD,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,qBAAqB,CAAC,IAAI,CAAC;AAEtE,eAAO,MAAM,oBAAoB;;;;;;;;;EAS/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,OAAO,oBAAoB,CAAC,IAAI,CAAC;AAEpE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAjCrB;;wDAEgD;;;;;;;;;;;;IAoClD,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAC;AAMpD,eAAO,MAAM,cAAc;;;;EAIzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAC;AAMxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAxD3B;;4DAEgD;;;;;;;;;;;;;;;;;;EA2DlD,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,kBAAkB,CAAC,IAAI,CAAC"}
package/dist/sink.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { Effect } from "effect";
2
+ import type { Layer } from "effect";
3
+ import type { FileSystem } from "effect";
4
+ import { type ConfigHeaderValue, type SourceConfig } from "./schema";
5
+ type PluginHeaderValue = string | {
6
+ secretId: string;
7
+ prefix?: string;
8
+ };
9
+ export declare const headerToConfigValue: (value: PluginHeaderValue) => ConfigHeaderValue;
10
+ export declare const headersToConfigValues: (headers: Record<string, PluginHeaderValue> | undefined) => Record<string, ConfigHeaderValue> | undefined;
11
+ export interface ConfigFileSink {
12
+ readonly upsertSource: (source: SourceConfig) => Effect.Effect<void>;
13
+ readonly removeSource: (namespace: string) => Effect.Effect<void>;
14
+ }
15
+ export interface ConfigFileSinkOptions {
16
+ readonly path: string;
17
+ readonly fsLayer: Layer.Layer<FileSystem.FileSystem>;
18
+ /** Called when a file operation fails. Defaults to console.warn. */
19
+ readonly onError?: (op: "upsert" | "remove", err: unknown) => void;
20
+ }
21
+ export declare const makeFileConfigSink: (options: ConfigFileSinkOptions) => ConfigFileSink;
22
+ export {};
23
+ //# sourceMappingURL=sink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sink.d.ts","sourceRoot":"","sources":["../src/sink.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,OAAO,EAAqB,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAKxF,KAAK,iBAAiB,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE,eAAO,MAAM,mBAAmB,GAC9B,OAAO,iBAAiB,KACvB,iBAIF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,SAAS,KACrD,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,SAKtC,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACrE,QAAQ,CAAC,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IACrD,oEAAoE;IACpE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CACpE;AAOD,eAAO,MAAM,kBAAkB,GAC7B,SAAS,qBAAqB,KAC7B,cAgBF,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { Effect } from "effect";
2
+ import { FileSystem } from "effect";
3
+ import type { PlatformError } from "effect/PlatformError";
4
+ import type { SourceConfig, ExecutorFileConfig } from "./schema";
5
+ /**
6
+ * Add a source entry to the config file. Creates the file if it doesn't exist.
7
+ * Preserves existing comments and formatting.
8
+ */
9
+ export declare const addSourceToConfig: (path: string, source: SourceConfig) => Effect.Effect<void, PlatformError, FileSystem.FileSystem>;
10
+ /**
11
+ * Remove a source from the config file by namespace.
12
+ */
13
+ export declare const removeSourceFromConfig: (path: string, namespace: string) => Effect.Effect<void, PlatformError, FileSystem.FileSystem>;
14
+ /**
15
+ * Write a full config object to a file.
16
+ */
17
+ export declare const writeConfig: (path: string, config: ExecutorFileConfig) => Effect.Effect<void, PlatformError, FileSystem.FileSystem>;
18
+ /**
19
+ * Add secret metadata to the config file.
20
+ */
21
+ export declare const addSecretToConfig: (path: string, secretId: string, metadata: {
22
+ name: string;
23
+ provider?: string;
24
+ purpose?: string;
25
+ }) => Effect.Effect<void, PlatformError, FileSystem.FileSystem>;
26
+ /**
27
+ * Remove secret metadata from the config file.
28
+ */
29
+ export declare const removeSecretFromConfig: (path: string, secretId: string) => Effect.Effect<void, PlatformError, FileSystem.FileSystem>;
30
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../src/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAyBjE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,EACZ,QAAQ,YAAY,KACnB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,CAAC,UAAU,CAyCvD,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,sBAAsB,GACjC,MAAM,MAAM,EACZ,WAAW,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,CAAC,UAAU,CA2BvD,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,WAAW,GACtB,MAAM,MAAM,EACZ,QAAQ,kBAAkB,KACzB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,CAAC,UAAU,CAQvD,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,EACZ,UAAU,MAAM,EAChB,UAAU;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAC9D,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,CAAC,UAAU,CAWvD,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,sBAAsB,GACjC,MAAM,MAAM,EACZ,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,CAAC,UAAU,CAcvD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@executor-js/config",
3
+ "version": "0.0.1",
4
+ "homepage": "https://github.com/RhysSullivan/executor/tree/main/packages/core/config",
5
+ "bugs": {
6
+ "url": "https://github.com/RhysSullivan/executor/issues"
7
+ },
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/RhysSullivan/executor.git",
12
+ "directory": "packages/core/config"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "type": "module",
18
+ "exports": {
19
+ ".": {
20
+ "import": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ }
24
+ }
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)",
31
+ "typecheck": "tsgo --noEmit",
32
+ "test": "vitest run",
33
+ "typecheck:slow": "tsc --noEmit"
34
+ },
35
+ "dependencies": {
36
+ "@executor-js/sdk": "0.0.1",
37
+ "effect": "4.0.0-beta.59",
38
+ "jsonc-parser": "^3.3.1"
39
+ },
40
+ "devDependencies": {
41
+ "@effect/platform-node": "4.0.0-beta.59",
42
+ "@effect/vitest": "4.0.0-beta.59",
43
+ "@types/node": "^24.3.1",
44
+ "tsup": "^8.5.0",
45
+ "typescript": "^5.9.3",
46
+ "vitest": "^4.1.5"
47
+ }
48
+ }