History

Trace source connect and stop typegen from blocking it

Rhys Sullivan committed 8 months ago

Commit 334475b1 parent 3b054b117 files · +1086 −105Browse files

Unified · Split

17 files
  1. TRACING.md +43 −0
  2. bun.lock +118 −0
  3. compose.tracing.yaml +9 −0
  4. package.json +2 −0
  5. packages/control-plane/package.json +2 −2
  6. packages/control-plane/src/runtime/index.ts +2 −5
  7. packages/control-plane/src/runtime/source-adapters/graphql.ts +59 −11
  8. packages/control-plane/src/runtime/source-auth-service.ts +19 −3
  9. packages/control-plane/src/runtime/source-catalog-sync.ts +10 −1
  10. packages/control-plane/src/runtime/source-store.ts +48 −10
  11. packages/control-plane/src/runtime/source-type-declarations.test.ts +48 −1
  12. packages/control-plane/src/runtime/source-type-declarations.ts +561 −69
  13. packages/server/package.json +8 −0
  14. packages/server/src/config.ts +5 −0
  15. packages/server/src/index.ts +36 −3
  16. packages/server/src/tracing.ts +66 −0
  17. personal-notes/otel-tracing-for-source-connect.md +50 −0
TRACING.md+43−0
@@ -0,0 +1,43 @@
1+# Local Tracing
2+
3+Start Jaeger locally:
4+
5+```bash
6+bun run trace:up
7+```
8+
9+Run the web/server dev process with tracing enabled:
10+
11+```bash
12+EXECUTOR_TRACE_ENABLED=1 \
13+EXECUTOR_TRACE_SERVICE_NAME=executor-local \
14+EXECUTOR_TRACE_OTLP_ENDPOINT=http://127.0.0.1:4317 \
15+bun run dev
16+```
17+
18+Then open Jaeger:
19+
20+- UI: `http://127.0.0.1:16686/search?service=executor-local`
21+
22+You can also query traces over HTTP:
23+
24+```bash
25+curl "http://127.0.0.1:16686/api/traces?service=executor-local&limit=20"
26+```
27+
28+Useful operations to search for:
29+
30+- `source.connect.http`
31+- `source.catalog.sync`
32+- `graphql.syncCatalog`
33+- `graphql.introspection.fetch`
34+- `graphql.manifest.extract`
35+- `graphql.definitions.compile`
36+- `graphql.operations.build`
37+- `graphql.snapshot.build`
38+
39+Stop Jaeger:
40+
41+```bash
42+bun run trace:down
43+```
bun.lock+118−0
@@ -284,9 +284,17 @@ · 283 unmodified lines
284284 "packages/server": {
285285 "name": "@executor/server",
286286 "dependencies": {
287+ "@effect/opentelemetry": "^0.61.0",
287288 "@effect/platform": "catalog:",
288289 "@executor/control-plane": "workspace:*",
289290 "@executor/executor-mcp": "workspace:*",
291+ "@opentelemetry/api": "^1.9.0",
292+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0",
293+ "@opentelemetry/exporter-trace-otlp-http": "^0.213.0",
294+ "@opentelemetry/resources": "^2.6.0",
295+ "@opentelemetry/sdk-trace-base": "^2.6.0",
296+ "@opentelemetry/sdk-trace-node": "^2.6.0",
297+ "@opentelemetry/semantic-conventions": "^1.40.0",
290298 "effect": "catalog:",
291299 },
292300 "devDependencies": {
@@ -375,6 +383,8 @@ · 82 unmodified lines
375383
376384 "@effect/experimental": ["@effect/experimental@0.58.0", "", { "dependencies": { "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.94.0", "effect": "^3.19.13", "ioredis": "^5", "lmdb": "^3" }, "optionalPeers": ["ioredis", "lmdb"] }, "sha512-IEP9sapjF6rFy5TkoqDPc86st/fnqUfjT7Xa3pWJrFGr1hzaMXHo+mWsYOZS9LAOVKnpHuVziDK97EP5qsCHVA=="],
377385
386+ "@effect/opentelemetry": ["@effect/opentelemetry@0.61.0", "", { "peerDependencies": { "@effect/platform": "^0.94.2", "@opentelemetry/api": "^1.9", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^3.19.15" } }, "sha512-aTg4qWzMxGDoUZKSyws/dMZqVNoSCQIvHaPDJnWitUAVuWDAPOXiaiHJDWO39cwDlySBTCSF6rEanm3+nR/2Mg=="],
387+
378388 "@effect/platform": ["@effect/platform@0.94.5", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.17" } }, "sha512-z05APUiDDPbodhTkH/RJqOLoCU11bU2IZLfcwLFrld03+ob1VeqRnELQlmueLIYm6NZifHAtjl32V+GRt34y4A=="],
379389
380390 "@effect/platform-node": ["@effect/platform-node@0.104.1", "", { "dependencies": { "@effect/platform-node-shared": "^0.57.1", "mime": "^3.0.0", "undici": "^7.10.0", "ws": "^8.18.2" }, "peerDependencies": { "@effect/cluster": "^0.56.1", "@effect/platform": "^0.94.2", "@effect/rpc": "^0.73.0", "@effect/sql": "^0.49.0", "effect": "^3.19.15" } }, "sha512-jT1a/z98niK6fnEU8pWHPPCdJMVDRCIdB65lolcOjse5rsTwVbczMjvKkhVQpF63mNWoOnol7OTRNkw5L54llg=="],
@@ -491,6 +501,10 @@ · 110 unmodified lines
491501
492502 "@executor/web": ["@executor/web@workspace:apps/web"],
493503
504+ "@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="],
505+
506+ "@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="],
507+
494508 "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="],
495509
496510 "@hono/vite-dev-server": ["@hono/vite-dev-server@0.25.0", "", { "dependencies": { "@hono/node-server": "^1.14.2", "minimatch": "^9.0.3" }, "peerDependencies": { "hono": "*", "miniflare": "*", "wrangler": "*" }, "optionalPeers": ["miniflare", "wrangler"] }, "sha512-4j5rs7yUTOObU2/yCEHeWbf3C8m95SD9lhdUUE6CJ462Emp9vS94yiza2dsA8+ly5pdaIQW0cd4nhNVcwYL5tA=="],
@@ -515,6 +529,8 @@ · 18 unmodified lines
515529
516530 "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
517531
532+ "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
533+
518534 "@medv/finder": ["@medv/finder@4.0.2", "", {}, "sha512-RraNY9SCcx4KZV0Dh6BEW6XEW2swkqYca74pkFFRw6hHItSHiy+O/xMnpbofjYbzXj0tSpBGthUF1hHTsr3vIQ=="],
519535
520536 "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
@@ -541,6 +557,36 @@ · 20 unmodified lines
541557
542558 "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
543559
560+ "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.213.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-zRM5/Qj6G84Ej3F1yt33xBVY/3tnMxtL1fiDIxYbDWYaZ/eudVw3/PBiZ8G7JwUxXxjW8gU4g6LnOyfGKYHYgw=="],
561+
562+ "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.6.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-L8UyDwqpTcbkIK5cgwDRDYDoEhQoj8wp8BwsO19w3LB1Z41yEQm2VJyNfAi9DrLP/YTqXqWpKHyZfR9/tFYo1Q=="],
563+
564+ "@opentelemetry/core": ["@opentelemetry/core@2.6.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-HLM1v2cbZ4TgYN6KEOj+Bbj8rAKriOdkF9Ed3tG25FoprSiQl7kYc+RRT6fUZGOvx0oMi5U67GoFdT+XUn8zEg=="],
565+
566+ "@opentelemetry/exporter-trace-otlp-grpc": ["@opentelemetry/exporter-trace-otlp-grpc@0.213.0", "", { "dependencies": { "@grpc/grpc-js": "^1.14.3", "@opentelemetry/core": "2.6.0", "@opentelemetry/otlp-exporter-base": "0.213.0", "@opentelemetry/otlp-grpc-exporter-base": "0.213.0", "@opentelemetry/otlp-transformer": "0.213.0", "@opentelemetry/resources": "2.6.0", "@opentelemetry/sdk-trace-base": "2.6.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-L8y6piP4jBIIx1Nv7/9hkx25ql6/Cro/kQrs+f9e8bPF0Ar5Dm991v7PnbtubKz6Q4fT872H56QXUWVnz/Cs4Q=="],
567+
568+ "@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.213.0", "", { "dependencies": { "@opentelemetry/core": "2.6.0", "@opentelemetry/otlp-exporter-base": "0.213.0", "@opentelemetry/otlp-transformer": "0.213.0", "@opentelemetry/resources": "2.6.0", "@opentelemetry/sdk-trace-base": "2.6.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-tnRmJD39aWrE/Sp7F6AbRNAjKHToDkAqBi6i0lESpGWz3G+f4bhVAV6mgSXH2o18lrDVJXo6jf9bAywQw43wRA=="],
569+
570+ "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.213.0", "", { "dependencies": { "@opentelemetry/core": "2.6.0", "@opentelemetry/otlp-transformer": "0.213.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-MegxAP1/n09Ob2dQvY5NBDVjAFkZRuKtWKxYev1R2M8hrsgXzQGkaMgoEKeUOyQ0FUyYcO29UOnYdQWmWa0PXg=="],
571+
572+ "@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.213.0", "", { "dependencies": { "@grpc/grpc-js": "^1.14.3", "@opentelemetry/core": "2.6.0", "@opentelemetry/otlp-exporter-base": "0.213.0", "@opentelemetry/otlp-transformer": "0.213.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-XgRGuLE9usFNlnw2lgMIM4HTwpcIyjdU/xPoJ8v3LbBLBfjaDkIugjc9HoWa7ZSJ/9Bhzgvm/aD0bGdYUFgnTw=="],
573+
574+ "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.213.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.213.0", "@opentelemetry/core": "2.6.0", "@opentelemetry/resources": "2.6.0", "@opentelemetry/sdk-logs": "0.213.0", "@opentelemetry/sdk-metrics": "2.6.0", "@opentelemetry/sdk-trace-base": "2.6.0", "protobufjs": "^7.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-RSuAlxFFPjeK4d5Y6ps8L2WhaQI6CXWllIjvo5nkAlBpmq2XdYWEBGiAbOF4nDs8CX4QblJDv5BbMUft3sEfDw=="],
575+
576+ "@opentelemetry/resources": ["@opentelemetry/resources@2.6.0", "", { "dependencies": { "@opentelemetry/core": "2.6.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-D4y/+OGe3JSuYUCBxtH5T9DSAWNcvCb/nQWIga8HNtXTVPQn59j0nTBAgaAXxUVBDl40mG3Tc76b46wPlZaiJQ=="],
577+
578+ "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.213.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.213.0", "@opentelemetry/core": "2.6.0", "@opentelemetry/resources": "2.6.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-00xlU3GZXo3kXKve4DLdrAL0NAFUaZ9appU/mn00S/5kSUdAvyYsORaDUfR04Mp2CLagAOhrzfUvYozY/EZX2g=="],
579+
580+ "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.6.0", "", { "dependencies": { "@opentelemetry/core": "2.6.0", "@opentelemetry/resources": "2.6.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-CicxWZxX6z35HR83jl+PLgtFgUrKRQ9LCXyxgenMnz5A1lgYWfAog7VtdOvGkJYyQgMNPhXQwkYrDLujk7z1Iw=="],
581+
582+ "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.6.0", "", { "dependencies": { "@opentelemetry/core": "2.6.0", "@opentelemetry/resources": "2.6.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-g/OZVkqlxllgFM7qMKqbPV9c1DUPhQ7d4n3pgZFcrnrNft9eJXZM2TNHTPYREJBrtNdRytYyvwjgL5geDKl3EQ=="],
583+
584+ "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.6.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.6.0", "@opentelemetry/core": "2.6.0", "@opentelemetry/sdk-trace-base": "2.6.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-YhswtasmsbIGEFvLGvR9p/y3PVRTfFf+mgY8van4Ygpnv4sA3vooAjvh+qAn9PNWxs4/IwGGqiQS0PPsaRJ0vQ=="],
585+
586+ "@opentelemetry/sdk-trace-web": ["@opentelemetry/sdk-trace-web@2.6.0", "", { "dependencies": { "@opentelemetry/core": "2.6.0", "@opentelemetry/sdk-trace-base": "2.6.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-xyYmLFatwUeYnB7NtQ2Ydl9Y8uiblN+EDo5YEjnk7ZRMhGFyt1wgPqb8EYvATLuDiRVtxid1fJsL6RH1fCQMIA=="],
587+
588+ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
589+
544590 "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.19.1", "", { "os": "android", "cpu": "arm" }, "sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg=="],
545591
546592 "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.19.1", "", { "os": "android", "cpu": "arm64" }, "sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA=="],
@@ -609,6 +655,26 @@ · 62 unmodified lines
609655
610656 "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="],
611657
658+ "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
659+
660+ "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
661+
662+ "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
663+
664+ "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
665+
666+ "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
667+
668+ "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
669+
670+ "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
671+
672+ "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
673+
674+ "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
675+
676+ "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
677+
612678 "@react-grab/cli": ["@react-grab/cli@0.1.26", "", { "dependencies": { "@antfu/ni": "^0.23.0", "commander": "^14.0.0", "ignore": "^7.0.5", "jsonc-parser": "^3.3.1", "ora": "^8.2.0", "picocolors": "^1.1.1", "prompts": "^2.4.2", "smol-toml": "^1.6.0" }, "bin": { "react-grab": "dist/cli.js" } }, "sha512-nNOBdrJ2K7jji6L/X9ARCpU1o/CQ33KYZUtW7vAtrfDM2hyvAIA224ps+Syl97FxhS7pO8b59Bnhc7YLG6h3nA=="],
613679
614680 "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="],
@@ -795,6 +861,8 @@ · 180 unmodified lines
795861
796862 "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
797863
864+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
865+
798866 "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
799867
800868 "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
@@ -849,8 +917,14 @@ · 48 unmodified lines
849917
850918 "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
851919
920+ "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
921+
852922 "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
853923
924+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
925+
926+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
927+
854928 "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
855929
856930 "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
@@ -977,6 +1051,8 @@ · 120 unmodified lines
9771051
9781052 "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
9791053
1054+ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
1055+
9801056 "get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
9811057
9821058 "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
@@ -1049,6 +1125,8 @@ · 66 unmodified lines
10491125
10501126 "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
10511127
1128+ "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
1129+
10521130 "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
10531131
10541132 "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
@@ -1121,8 +1199,12 @@ · 66 unmodified lines
11211199
11221200 "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="],
11231201
1202+ "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
1203+
11241204 "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="],
11251205
1206+ "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
1207+
11261208 "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
11271209
11281210 "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
@@ -1309,6 +1391,8 @@ · 180 unmodified lines
13091391
13101392 "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
13111393
1394+ "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
1395+
13121396 "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
13131397
13141398 "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
@@ -1357,6 +1441,8 @@ · 42 unmodified lines
13571441
13581442 "remend": ["remend@1.2.2", "", {}, "sha512-4ZJgIB9EG9fQE41mOJCRHMmnxDTKHWawQoJWZyUbZuj680wVyogu2ihnj8Edqm7vh2mo/TWHyEZpn2kqeDvS7w=="],
13591443
1444+ "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
1445+
13601446 "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
13611447
13621448 "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
@@ -1563,6 +1649,8 @@ · 200 unmodified lines
15631649
15641650 "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
15651651
1652+ "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
1653+
15661654 "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
15671655
15681656 "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
@@ -1571,10 +1659,16 @@ · 2 unmodified lines
15711659
15721660 "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="],
15731661
1662+ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
1663+
15741664 "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
15751665
15761666 "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
15771667
1668+ "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
1669+
1670+ "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
1671+
15781672 "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
15791673
15801674 "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
@@ -1599,6 +1693,10 @@ · 18 unmodified lines
15991693
16001694 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
16011695
1696+ "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
1697+
1698+ "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
1699+
16021700 "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="],
16031701
16041702 "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
@@ -1609,6 +1707,12 @@ · 4 unmodified lines
16091707
16101708 "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
16111709
1710+ "wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
1711+
1712+ "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
1713+
1714+ "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
1715+
16121716 "@streamdown/code/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="],
16131717
16141718 "@streamdown/code/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="],
@@ -1621,6 +1725,20 @@ · 6 unmodified lines
16211725
16221726 "@streamdown/code/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="],
16231727
1728+ "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
1729+
1730+ "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
1731+
16241732 "tough-cookie/tldts/tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="],
1733+
1734+ "wrap-ansi/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
1735+
1736+ "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
1737+
1738+ "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
1739+
1740+ "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
1741+
1742+ "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
16251743 }
16261744 }
compose.tracing.yaml+9−0
@@ -0,0 +1,9 @@
1+services:
2+ jaeger:
3+ image: jaegertracing/all-in-one:1.65.0
4+ ports:
5+ - "16686:16686"
6+ - "4317:4317"
7+ - "4318:4318"
8+ environment:
9+ COLLECTOR_OTLP_ENABLED: "true"
package.json+2−0
@@ -21,6 +21,8 @@ · 20 unmodified lines
2121 },
2222 "scripts": {
2323 "dev": "turbo run dev",
24+ "trace:up": "docker compose -f compose.tracing.yaml up -d",
25+ "trace:down": "docker compose -f compose.tracing.yaml down",
2426 "docs:dev": "turbo run dev --filter=@executor/docs",
2527 "test": "turbo run test",
2628 "executor": "./apps/executor/bin/executor",
packages/control-plane/package.json+2−2
@@ -24,10 +24,10 @@ · 23 unmodified lines
2424 "@executor/codemode-core": "workspace:*",
2525 "@executor/codemode-google-discovery": "workspace:*",
2626 "@executor/codemode-mcp": "workspace:*",
27− "@executor/runtime-quickjs": "workspace:*",
28− "@executor/runtime-ses": "workspace:*",
2927 "@executor/codemode-openapi": "workspace:*",
3028 "@executor/runtime-deno-subprocess": "workspace:*",
29+ "@executor/runtime-quickjs": "workspace:*",
30+ "@executor/runtime-ses": "workspace:*",
3131 "effect": "catalog:",
3232 "graphql": "^16.12.0",
3333 "jsonc-parser": "3.3.1",
packages/control-plane/src/runtime/index.ts+2−5
@@ -262,16 +262,13 @@ export const createControlPlaneRuntime = ( · 261 unmodified lines
262262 liveExecutionManager,
263263 });
264264 const managedRuntime = ManagedRuntime.make(concreteRuntimeLayer);
265− const runtime = yield* managedRuntime.runtimeEffect;
266− const runtimeLayer = Layer.succeedContext(
267− runtime.context,
268− ) as RuntimeControlPlaneLayer;
265+ yield* managedRuntime.runtimeEffect;
269266
270267 return {
271268 persistence,
272269 localInstallation,
273270 managedRuntime,
274− runtimeLayer: runtimeLayer as RuntimeControlPlaneLayer,
271+ runtimeLayer: concreteRuntimeLayer as RuntimeControlPlaneLayer,
275272 close: () => managedRuntime.dispose().catch(() => {}),
276273 };
277274 }).pipe(Effect.provide(NodeFileSystem.layer));
packages/control-plane/src/runtime/source-adapters/graphql.ts+59−11
@@ -270,6 +270,13 @@ export const graphqlSourceAdapter: SourceAdapter = { · 269 unmodified lines
270270 bodyValues: auth.bodyValues,
271271 },
272272 ).pipe(
273+ Effect.withSpan("graphql.introspection.fetch", {
274+ kind: "client",
275+ attributes: {
276+ "executor.source.id": source.id,
277+ "executor.source.endpoint": source.endpoint,
278+ },
279+ }),
273280 Effect.mapError(
274281 (cause) =>
275282 isSourceCredentialRequiredError(cause)
@@ -284,16 +291,44 @@ export const graphqlSourceAdapter: SourceAdapter = { · 8 unmodified lines
284291 source.name,
285292 graphqlDocument,
286293 ).pipe(
294+ Effect.withSpan("graphql.manifest.extract", {
295+ attributes: {
296+ "executor.source.id": source.id,
297+ },
298+ }),
287299 Effect.mapError((cause) =>
288300 cause instanceof Error ? cause : new Error(String(cause)),
289301 ),
290302 );
303+ yield* Effect.annotateCurrentSpan("graphql.tool.count", manifest.tools.length);
291304
292− const definitions = compileGraphqlToolDefinitions(manifest);
305+ const definitions = yield* Effect.sync(() => compileGraphqlToolDefinitions(manifest)).pipe(
306+ Effect.withSpan("graphql.definitions.compile", {
307+ attributes: {
308+ "executor.source.id": source.id,
309+ "graphql.tool.count": manifest.tools.length,
310+ },
311+ }),
312+ );
313+ yield* Effect.annotateCurrentSpan("graphql.definition.count", definitions.length);
314+ const operations = yield* Effect.sync(() =>
315+ definitions.map((definition) =>
316+ graphqlCatalogOperationFromDefinition({
317+ definition,
318+ manifest,
319+ })
320+ )
321+ ).pipe(
322+ Effect.withSpan("graphql.operations.build", {
323+ attributes: {
324+ "executor.source.id": source.id,
325+ "graphql.definition.count": definitions.length,
326+ },
327+ }),
328+ );
293329 const now = Date.now();
294−
295− return {
296− snapshot: createGraphqlCatalogSnapshot({
330+ const snapshot = yield* Effect.sync(() =>
331+ createGraphqlCatalogSnapshot({
297332 source,
298333 documents: [{
299334 documentKind: "graphql_introspection",
@@ -301,14 +336,27 @@ export const graphqlSourceAdapter: SourceAdapter = { · 1 unmodified line
301336 contentText: graphqlDocument,
302337 fetchedAt: now,
303338 }],
304− operations: definitions.map((definition) =>
305− graphqlCatalogOperationFromDefinition({
306− definition,
307− manifest,
308− })
309− ),
339+ operations,
340+ })
341+ ).pipe(
342+ Effect.withSpan("graphql.snapshot.build", {
343+ attributes: {
344+ "executor.source.id": source.id,
345+ "graphql.operation.count": operations.length,
346+ },
310347 }),
348+ );
349+
350+ return {
351+ snapshot,
311352 sourceHash: manifest.sourceHash,
312353 };
313− }),
354+ }).pipe(
355+ Effect.withSpan("graphql.syncCatalog", {
356+ attributes: {
357+ "executor.source.id": source.id,
358+ "executor.source.endpoint": source.endpoint,
359+ },
360+ }),
361+ ),
314362 };
packages/control-plane/src/runtime/source-auth-service.ts+19−3
@@ -360,7 +360,7 @@ const serializeJson = (value: unknown): string | null => { · 359 unmodified lines
360360 return JSON.stringify(value);
361361 };
362362
363−const updateSourceStatus = (sourceStore: RuntimeSourceStore, source: Source, input: {
363+const updateSourceStatus = Effect.fn("source.status.update")((sourceStore: RuntimeSourceStore, source: Source, input: {
364364 actorAccountId?: AccountId | null;
365365 status: Source["status"];
366366 lastError?: string | null;
@@ -384,7 +384,14 @@ const updateSourceStatus = (sourceStore: RuntimeSourceStore, source: Source, inp · 17 unmodified lines
384384 }, {
385385 actorAccountId: input.actorAccountId,
386386 });
387− });
387+ }).pipe(
388+ Effect.withSpan("source.status.update", {
389+ attributes: {
390+ "executor.source.id": source.id,
391+ "executor.source.status": input.status,
392+ },
393+ }),
394+ ));
388395
389396 export type ExecutorSourceAddResult =
390397 | {
@@ -1404,7 +1411,16 @@ const addExecutorHttpSource = (input: { · 1013 unmodified lines
14041411 ),
14051412 ),
14061413 });
1407− });
1414+ }).pipe(
1415+ Effect.withSpan("source.connect.http", {
1416+ attributes: {
1417+ "executor.source.kind": input.sourceInput.kind,
1418+ "executor.source.endpoint": input.sourceInput.endpoint,
1419+ ...(input.sourceInput.namespace ? { "executor.source.namespace": input.sourceInput.namespace } : {}),
1420+ ...(input.sourceInput.name ? { "executor.source.name": input.sourceInput.name } : {}),
1421+ },
1422+ }),
1423+ );
14081424
14091425 const addExecutorGoogleDiscoverySource = (input: {
14101426 rows: ControlPlaneStoreShape;
packages/control-plane/src/runtime/source-catalog-sync.ts+10−1
@@ -174,7 +174,16 @@ const syncSourceCatalogWithDeps = ( · 173 unmodified lines
174174 snapshot: syncResult.snapshot,
175175 });
176176 });
177− });
177+ }).pipe(
178+ Effect.withSpan("source.catalog.sync", {
179+ attributes: {
180+ "executor.source.id": input.source.id,
181+ "executor.source.kind": input.source.kind,
182+ "executor.source.namespace": input.source.namespace,
183+ "executor.source.endpoint": input.source.endpoint,
184+ },
185+ }),
186+ );
178187
179188 const persistMcpCatalogSnapshotFromManifestWithDeps = (
180189 deps: RuntimeSourceCatalogSyncDeps,
packages/control-plane/src/runtime/source-store.ts+48−10
@@ -436,7 +436,7 @@ const loadSourcesInWorkspaceWithDeps = ( · 435 unmodified lines
436436 Effect.gen(function* () {
437437 const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps(deps, workspaceId);
438438 const authArtifacts = yield* deps.rows.authArtifacts.listByWorkspaceId(workspaceId);
439− return yield* Effect.forEach(
439+ const sources = yield* Effect.forEach(
440440 Object.keys(localWorkspace.loadedConfig.config?.sources ?? {}),
441441 (sourceId) =>
442442 Effect.map(
@@ -451,7 +451,15 @@ const loadSourcesInWorkspaceWithDeps = ( · 8 unmodified lines
451451 ({ source }) => source,
452452 ),
453453 );
454− });
454+ yield* Effect.annotateCurrentSpan("executor.source.count", sources.length);
455+ return sources;
456+ }).pipe(
457+ Effect.withSpan("source.store.load_workspace", {
458+ attributes: {
459+ "executor.workspace.id": workspaceId,
460+ },
461+ }),
462+ );
455463
456464 const syncWorkspaceSourceTypeDeclarationsWithDeps = (
457465 deps: RuntimeSourceStoreDeps,
@@ -485,7 +493,19 @@ const syncWorkspaceSourceTypeDeclarationsWithDeps = ( · 27 unmodified lines
485493 entries: entries.filter((entry): entry is NonNullable<typeof entry> => entry !== null),
486494 });
487495 });
488− });
496+ }).pipe(
497+ Effect.withSpan("source.types.refresh_workspace.schedule", {
498+ attributes: {
499+ "executor.workspace.id": workspaceId,
500+ },
501+ }),
502+ );
503+
504+const shouldRefreshWorkspaceDeclarationsAfterPersist = (source: Source): boolean =>
505+ source.enabled === false
506+ || source.status === "auth_required"
507+ || source.status === "error"
508+ || source.status === "draft";
489509
490510 export const loadSourcesInWorkspace = (
491511 rows: ControlPlaneStoreShape,
@@ -621,7 +641,14 @@ const loadSourceByIdWithDeps = (deps: RuntimeSourceStoreDeps, input: { · 129 unmodified lines
621641 });
622642
623643 return localSource.source;
624− });
644+ }).pipe(
645+ Effect.withSpan("source.store.load_by_id", {
646+ attributes: {
647+ "executor.workspace.id": input.workspaceId,
648+ "executor.source.id": input.sourceId,
649+ },
650+ }),
651+ );
625652
626653 export const loadSourceById = (rows: ControlPlaneStoreShape, input: {
627654 workspaceId: WorkspaceId;
@@ -939,18 +966,29 @@ const persistSourceWithDeps = ( · 311 unmodified lines
939966 state: workspaceState,
940967 });
941968
942− yield* syncWorkspaceSourceTypeDeclarationsWithDeps(
943− deps,
944− nextSource.workspaceId,
945− options,
946− );
969+ if (shouldRefreshWorkspaceDeclarationsAfterPersist(nextSource)) {
970+ yield* syncWorkspaceSourceTypeDeclarationsWithDeps(
971+ deps,
972+ nextSource.workspaceId,
973+ options,
974+ );
975+ }
947976
948977 return yield* loadSourceByIdWithDeps(deps, {
949978 workspaceId: nextSource.workspaceId,
950979 sourceId: nextSource.id,
951980 actorAccountId: options.actorAccountId,
952981 });
953− });
982+ }).pipe(
983+ Effect.withSpan("source.store.persist", {
984+ attributes: {
985+ "executor.workspace.id": source.workspaceId,
986+ "executor.source.id": source.id,
987+ "executor.source.kind": source.kind,
988+ "executor.source.status": source.status,
989+ },
990+ }),
991+ );
954992
955993 export const persistSource = (
956994 rows: ControlPlaneStoreShape,
packages/control-plane/src/runtime/source-type-declarations.test.ts+48−1
@@ -46,6 +46,8 @@ const createSnapshot = (): ReturnType<typeof createCatalogSnapshotV1> => { · 45 unmodified lines
4646 const executableId = ExecutableIdSchema.make("exec_graphql_teams");
4747 const capabilityId = CapabilityIdSchema.make("cap_graphql_teams");
4848 const responseSetId = ResponseSetIdSchema.make("response_set_graphql_teams");
49+ const secondExecutableId = ExecutableIdSchema.make("exec_graphql_teams_search");
50+ const secondCapabilityId = CapabilityIdSchema.make("cap_graphql_teams_search");
4951
5052 put(catalog.documents as Record<typeof docId, CatalogV1["documents"][typeof docId]>, docId, {
5153 id: docId,
@@ -175,6 +177,47 @@ const createSnapshot = (): ReturnType<typeof createCatalogSnapshotV1> => { · 123 unmodified lines
175177 provenance: baseProvenance("#/query/teams"),
176178 });
177179
180+ put(catalog.capabilities as Record<typeof secondCapabilityId, CatalogV1["capabilities"][typeof secondCapabilityId]>, secondCapabilityId, {
181+ id: secondCapabilityId,
182+ serviceScopeId: scopeId,
183+ surface: {
184+ toolPath: ["linear", "teamsSearch"],
185+ title: "Teams Search",
186+ summary: "Search teams",
187+ },
188+ semantics: {
189+ effect: "read",
190+ safe: true,
191+ idempotent: true,
192+ destructive: false,
193+ },
194+ auth: { kind: "none" },
195+ interaction: {
196+ approval: { mayRequire: false },
197+ elicitation: { mayRequest: false },
198+ resume: { supported: false },
199+ },
200+ executableIds: [secondExecutableId],
201+ preferredExecutableId: secondExecutableId,
202+ synthetic: false,
203+ provenance: baseProvenance("#/query/teamsSearch"),
204+ });
205+
206+ put(catalog.executables as Record<typeof secondExecutableId, CatalogV1["executables"][typeof secondExecutableId]>, secondExecutableId, {
207+ id: secondExecutableId,
208+ protocol: "graphql",
209+ capabilityId: secondCapabilityId,
210+ scopeId,
211+ operationType: "query",
212+ rootField: "teamsSearch",
213+ argumentShapeId: callShapeId,
214+ resultShapeId,
215+ selectionMode: "fixed",
216+ responseSetId,
217+ synthetic: false,
218+ provenance: baseProvenance("#/query/teamsSearch"),
219+ });
220+
178221 return createCatalogSnapshotV1({
179222 import: {
180223 sourceKind: "graphql-schema",
@@ -252,8 +295,12 @@ describe("source-type-declarations", () => { · 71 unmodified lines
252295 );
253296
254297 expect(sourceDeclaration).toContain("export interface SourceTools_src_linear");
298+ expect(sourceDeclaration).toContain("type TeamConnection = {");
299+ expect(sourceDeclaration.match(/type TeamFilter =/g)?.length).toBe(1);
300+ expect(sourceDeclaration.match(/type Teams_call =/g)?.length).toBe(1);
255301 expect(sourceDeclaration).toContain("linear: {");
256− expect(sourceDeclaration).toContain("teams: (args?: { filter?: { name?: string } }) => Promise<{ nodes?: { name?: string } }>");
302+ expect(sourceDeclaration).toContain("teams: (args?: Teams_call) => Promise<TeamConnection>;");
303+ expect(sourceDeclaration).toContain("teamsSearch: (args?: Teams_call) => Promise<TeamConnection>;");
257304 expect(aggregateDeclaration).toContain('import type { SourceTools_src_linear } from "./sources/src_linear";');
258305 expect(aggregateDeclaration).not.toContain("src_hidden");
259306 expect(aggregateDeclaration).toContain("declare global {");
packages/control-plane/src/runtime/source-type-declarations.ts+561−69
@@ -1,20 +1,19 @@
11 import { join } from "node:path";
22 import { FileSystem } from "@effect/platform";
33 import { NodeFileSystem } from "@effect/platform-node";
4−import { typeSignatureFromSchemaWithOptions } from "@executor/codemode-core";
54 import type { Source } from "#schema";
5+import * as Cause from "effect/Cause";
66 import * as Effect from "effect/Effect";
7+import * as Fiber from "effect/Fiber";
78
89 import { projectCatalogForAgentSdk } from "../ir/catalog";
9−import type { CatalogSnapshotV1 } from "../ir/model";
10+import type { ShapeSymbolId } from "../ir/ids";
11+import type { CatalogSnapshotV1, CatalogV1, ShapeNode, ShapeSymbol } from "../ir/model";
1012 import type { ResolvedLocalWorkspaceContext } from "./local-config";
1113 import {
1214 LocalFileSystemError,
1315 unknownLocalErrorDetails,
1416 } from "./local-errors";
15−import { shapeToJsonSchema } from "./source-catalog-runtime";
16−
17−type JsonRecord = Record<string, unknown>;
1817
1918 type SourceDeclarationEntry = {
2019 source: Source;
@@ -28,6 +27,11 @@ type ToolMethodNode = { · 7 unmodified lines
2827 readonly argsOptional: boolean;
2928 };
3029
30+type SourceDeclarationModel = {
31+ readonly methods: readonly ToolMethodNode[];
32+ readonly supportingTypes: readonly string[];
33+};
34+
3135 type ToolTreeNode = {
3236 method: ToolMethodNode | null;
3337 children: Map<string, ToolTreeNode>;
@@ -40,11 +44,6 @@ type SourceDeclarationStub = { · 6 unmodified lines
4044 const GENERATED_TYPES_DIRECTORY = "types";
4145 const GENERATED_SOURCE_TYPES_DIRECTORY = "sources";
4246 const VALID_IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
43−const DECLARATION_RENDER_OPTIONS = {
44− maxLength: Number.POSITIVE_INFINITY,
45− maxDepth: 6,
46− maxProperties: 16,
47−} as const;
4847
4948 const mapFileSystemError = (path: string, action: string) => (cause: unknown) =>
5049 new LocalFileSystemError({
@@ -68,19 +67,18 @@ const sourceDeclarationPath = (context: ResolvedLocalWorkspaceContext, sourceId: · 17 unmodified lines
6867 const aggregateDeclarationPath = (context: ResolvedLocalWorkspaceContext): string =>
6968 join(declarationDirectory(context), "index.d.ts");
7069
71−const asRecord = (value: unknown): JsonRecord =>
72− typeof value === "object" && value !== null && !Array.isArray(value)
73− ? (value as JsonRecord)
74− : {};
75−
76−const asStringArray = (value: unknown): string[] =>
77− Array.isArray(value)
78− ? value.filter((entry): entry is string => typeof entry === "string")
79− : [];
80−
8170 const formatPropertyKey = (value: string): string =>
8271 VALID_IDENTIFIER_PATTERN.test(value) ? value : JSON.stringify(value);
8372
73+const formatTypeNameSegment = (value: string): string => {
74+ const sanitized = value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
75+ if (sanitized.length === 0) {
76+ return "Type";
77+ }
78+
79+ return /^[A-Za-z_$]/.test(sanitized) ? sanitized : `T_${sanitized}`;
80+};
81+
8482 const sourceTypeInterfaceName = (sourceId: string): string =>
8583 `SourceTools_${sourceId.replace(/[^A-Za-z0-9_$]+/g, "_")}`;
8684
@@ -92,15 +90,38 @@ const createToolTreeNode = (): ToolTreeNode => ({ · 5 unmodified lines
9290 children: new Map(),
9391 });
9492
95−const schemaAllowsOmittedArgs = (schema: unknown): boolean => {
96− const root = asRecord(schema);
97− if (root.type !== "object" && root.properties === undefined) {
98− return false;
93+const primitiveTypeName = (value: string): string => {
94+ switch (value) {
95+ case "string":
96+ case "boolean":
97+ return value;
98+ case "integer":
99+ case "number":
100+ return "number";
101+ case "null":
102+ return "null";
103+ case "object":
104+ return "Record<string, unknown>";
105+ case "array":
106+ return "Array<unknown>";
107+ default:
108+ throw new Error(`Unsupported JSON Schema primitive type: ${value}`);
99109 }
110+};
100111
101− return asStringArray(root.required).length === 0;
112+const jsonLiteral = (value: unknown): string => {
113+ const serialized = JSON.stringify(value);
114+ if (serialized === undefined) {
115+ throw new Error(`Unsupported literal value in declaration schema: ${String(value)}`);
116+ }
117+ return serialized;
102118 };
103119
120+const wrapCompositeType = (value: string): string =>
121+ value.includes(" | ") || value.includes(" & ")
122+ ? `(${value})`
123+ : value;
124+
104125 const objectTypeLiteral = (
105126 lines: readonly string[],
106127 indent: string,
@@ -158,45 +179,482 @@ const buildToolTree = (methods: readonly ToolMethodNode[]): ToolTreeNode => { · 51 unmodified lines
158179 return root;
159180 };
160181
161−const collectToolMethods = (snapshot: CatalogSnapshotV1): ToolMethodNode[] => {
182+const getShapeSymbol = (catalog: CatalogV1, shapeId: ShapeSymbolId): ShapeSymbol => {
183+ const symbol = catalog.symbols[shapeId];
184+ if (!symbol || symbol.kind !== "shape") {
185+ throw new Error(`Missing shape symbol for ${shapeId}`);
186+ }
187+
188+ return symbol;
189+};
190+
191+const isInlineShapeNode = (node: ShapeNode): boolean =>
192+ node.type === "unknown"
193+ || node.type === "scalar"
194+ || node.type === "const"
195+ || node.type === "enum";
196+
197+const isSyntheticShapeLabel = (value: string): boolean =>
198+ /^shape_[a-f0-9_]+$/i.test(value);
199+
200+const childShapeIds = (node: ShapeNode): ShapeSymbolId[] => {
201+ switch (node.type) {
202+ case "unknown":
203+ case "scalar":
204+ case "const":
205+ case "enum":
206+ return [];
207+ case "object":
208+ return [
209+ ...Object.values(node.fields).map((field) => field.shapeId),
210+ ...(typeof node.additionalProperties === "string" ? [node.additionalProperties] : []),
211+ ...Object.values(node.patternProperties ?? {}),
212+ ];
213+ case "array":
214+ return [node.itemShapeId];
215+ case "tuple":
216+ return [
217+ ...node.itemShapeIds,
218+ ...(typeof node.additionalItems === "string" ? [node.additionalItems] : []),
219+ ];
220+ case "map":
221+ return [node.valueShapeId];
222+ case "allOf":
223+ case "anyOf":
224+ case "oneOf":
225+ return [...node.items];
226+ case "nullable":
227+ return [node.itemShapeId];
228+ case "ref":
229+ return [node.target];
230+ case "not":
231+ return [node.itemShapeId];
232+ case "conditional":
233+ return [
234+ node.ifShapeId,
235+ ...(node.thenShapeId ? [node.thenShapeId] : []),
236+ ...(node.elseShapeId ? [node.elseShapeId] : []),
237+ ];
238+ case "graphqlInterface":
239+ return [
240+ ...Object.values(node.fields).map((field) => field.shapeId),
241+ ...node.possibleTypeIds,
242+ ];
243+ case "graphqlUnion":
244+ return [...node.memberTypeIds];
245+ }
246+};
247+
248+const createCatalogTypeRenderer = (input: {
249+ catalog: CatalogV1;
250+ rootShapeIds: readonly ShapeSymbolId[];
251+}) => {
252+ const { catalog, rootShapeIds } = input;
253+ const signatureCache = new Map<ShapeSymbolId, string>();
254+ const recursiveSignatures = new Set<string>();
255+ const shapeIdsBySignature = new Map<string, ShapeSymbolId[]>();
256+ const usageCountBySignature = new Map<string, number>();
257+ const aliasNameBySignature = new Map<string, string>();
258+ const usedAliasNames = new Set<string>();
259+ const usedAliasSignatures = new Set<string>();
260+
261+ const renderInlineShapeNode = (node: ShapeNode): string => {
262+ switch (node.type) {
263+ case "unknown":
264+ return "unknown";
265+ case "scalar":
266+ return primitiveTypeName(node.scalar);
267+ case "const":
268+ return jsonLiteral(node.value);
269+ case "enum":
270+ return node.values.map((value) => jsonLiteral(value)).join(" | ");
271+ default:
272+ throw new Error(`Cannot inline non-primitive shape node: ${node.type}`);
273+ }
274+ };
275+
276+ const shapeSignature = (shapeId: ShapeSymbolId, stack: readonly ShapeSymbolId[] = []): string => {
277+ const cached = signatureCache.get(shapeId);
278+ if (cached) {
279+ return cached;
280+ }
281+
282+ if (stack.includes(shapeId)) {
283+ return `cycle:${shapeId}`;
284+ }
285+
286+ const shape = getShapeSymbol(catalog, shapeId);
287+ const nextStack = [...stack, shapeId];
288+ const childSignatures = (shapeIds: readonly ShapeSymbolId[], sort: boolean): string[] => {
289+ const values = shapeIds.map((childShapeId) => shapeSignature(childShapeId, nextStack));
290+ return sort ? values.sort((left, right) => left.localeCompare(right)) : values;
291+ };
292+
293+ const signature = (() => {
294+ switch (shape.node.type) {
295+ case "unknown":
296+ return "unknown";
297+ case "scalar":
298+ return `scalar:${primitiveTypeName(shape.node.scalar)}`;
299+ case "const":
300+ return `const:${jsonLiteral(shape.node.value)}`;
301+ case "enum":
302+ return `enum:${shape.node.values.map((value) => jsonLiteral(value)).sort().join("|")}`;
303+ case "object": {
304+ const required = shape.node.required ?? [];
305+ const fields = Object.entries(shape.node.fields)
306+ .sort(([left], [right]) => left.localeCompare(right))
307+ .map(([key, field]) => `${key}${required.includes(key) ? "!" : "?"}:${shapeSignature(field.shapeId, nextStack)}`)
308+ .join(",");
309+ const additionalProperties =
310+ shape.node.additionalProperties === true
311+ ? "unknown"
312+ : typeof shape.node.additionalProperties === "string"
313+ ? shapeSignature(shape.node.additionalProperties, nextStack)
314+ : "none";
315+ const patternProperties = Object.entries(shape.node.patternProperties ?? {})
316+ .map(([, valueShapeId]) => shapeSignature(valueShapeId, nextStack))
317+ .sort((left, right) => left.localeCompare(right))
318+ .join("|");
319+ return `object:${fields}:index=${additionalProperties}:patterns=${patternProperties}`;
320+ }
321+ case "array":
322+ return `array:${shapeSignature(shape.node.itemShapeId, nextStack)}`;
323+ case "tuple":
324+ return `tuple:${childSignatures(shape.node.itemShapeIds, false).join(",")}:rest=${
325+ shape.node.additionalItems === true
326+ ? "unknown"
327+ : typeof shape.node.additionalItems === "string"
328+ ? shapeSignature(shape.node.additionalItems, nextStack)
329+ : "none"
330+ }`;
331+ case "map":
332+ return `map:${shapeSignature(shape.node.valueShapeId, nextStack)}`;
333+ case "allOf":
334+ return `allOf:${childSignatures(shape.node.items, true).join("&")}`;
335+ case "anyOf":
336+ return `anyOf:${childSignatures(shape.node.items, true).join("|")}`;
337+ case "oneOf":
338+ return `oneOf:${childSignatures(shape.node.items, true).join("|")}`;
339+ case "nullable":
340+ return `nullable:${shapeSignature(shape.node.itemShapeId, nextStack)}`;
341+ case "ref":
342+ return `ref:${shapeSignature(shape.node.target, nextStack)}`;
343+ case "not":
344+ return `not:${shapeSignature(shape.node.itemShapeId, nextStack)}`;
345+ case "conditional":
346+ return `conditional:${shapeSignature(shape.node.ifShapeId, nextStack)}:${
347+ shape.node.thenShapeId ? shapeSignature(shape.node.thenShapeId, nextStack) : "none"
348+ }:${shape.node.elseShapeId ? shapeSignature(shape.node.elseShapeId, nextStack) : "none"}`;
349+ case "graphqlInterface": {
350+ const fields = Object.entries(shape.node.fields)
351+ .sort(([left], [right]) => left.localeCompare(right))
352+ .map(([key, field]) => `${key}:${shapeSignature(field.shapeId, nextStack)}`)
353+ .join(",");
354+ const possibleTypes = childSignatures(shape.node.possibleTypeIds, true).join("|");
355+ return `graphqlInterface:${fields}:possible=${possibleTypes}`;
356+ }
357+ case "graphqlUnion":
358+ return `graphqlUnion:${childSignatures(shape.node.memberTypeIds, true).join("|")}`;
359+ }
360+ })();
361+
362+ if (signature.includes("cycle:")) {
363+ recursiveSignatures.add(signature);
364+ }
365+
366+ signatureCache.set(shapeId, signature);
367+ const group = shapeIdsBySignature.get(signature);
368+ if (group) {
369+ group.push(shapeId);
370+ } else {
371+ shapeIdsBySignature.set(signature, [shapeId]);
372+ }
373+ return signature;
374+ };
375+
376+ const reachableShapeIds = new Set<ShapeSymbolId>();
377+ const collectReachableShapes = (shapeId: ShapeSymbolId): void => {
378+ if (reachableShapeIds.has(shapeId)) {
379+ return;
380+ }
381+
382+ reachableShapeIds.add(shapeId);
383+ for (const childShapeId of childShapeIds(getShapeSymbol(catalog, shapeId).node)) {
384+ collectReachableShapes(childShapeId);
385+ }
386+ };
387+
388+ for (const rootShapeId of rootShapeIds) {
389+ collectReachableShapes(rootShapeId);
390+ }
391+
392+ for (const rootShapeId of rootShapeIds) {
393+ const rootShape = getShapeSymbol(catalog, rootShapeId);
394+ if (isInlineShapeNode(rootShape.node)) {
395+ continue;
396+ }
397+
398+ const signature = shapeSignature(rootShapeId);
399+ usageCountBySignature.set(signature, (usageCountBySignature.get(signature) ?? 0) + 1);
400+ }
401+
402+ for (const shapeId of reachableShapeIds) {
403+ for (const childShapeId of childShapeIds(getShapeSymbol(catalog, shapeId).node)) {
404+ const childShape = getShapeSymbol(catalog, childShapeId);
405+ if (isInlineShapeNode(childShape.node)) {
406+ continue;
407+ }
408+
409+ const signature = shapeSignature(childShapeId);
410+ usageCountBySignature.set(signature, (usageCountBySignature.get(signature) ?? 0) + 1);
411+ }
412+ }
413+
414+ const representativeShapeIdForSignature = (signature: string): ShapeSymbolId => {
415+ const group = shapeIdsBySignature.get(signature);
416+ if (!group || group.length === 0) {
417+ throw new Error(`Missing representative shape for signature ${signature}`);
418+ }
419+
420+ const sorted = [...group].sort((left, right) => {
421+ const leftShape = getShapeSymbol(catalog, left);
422+ const rightShape = getShapeSymbol(catalog, right);
423+ const leftTitle = leftShape.title ?? leftShape.id;
424+ const rightTitle = rightShape.title ?? rightShape.id;
425+ const leftSynthetic = isSyntheticShapeLabel(leftTitle) ? 1 : 0;
426+ const rightSynthetic = isSyntheticShapeLabel(rightTitle) ? 1 : 0;
427+ return leftSynthetic - rightSynthetic || leftTitle.localeCompare(rightTitle);
428+ });
429+
430+ return sorted[0]!;
431+ };
432+
433+ const aliasNameForSignature = (signature: string): string => {
434+ const existing = aliasNameBySignature.get(signature);
435+ if (existing) {
436+ return existing;
437+ }
438+
439+ const representative = getShapeSymbol(catalog, representativeShapeIdForSignature(signature));
440+ const baseName = formatTypeNameSegment(representative.title ?? representative.id);
441+ let candidate = baseName;
442+ let suffix = 2;
443+ while (usedAliasNames.has(candidate)) {
444+ candidate = `${baseName}_${String(suffix)}`;
445+ suffix += 1;
446+ }
447+
448+ aliasNameBySignature.set(signature, candidate);
449+ usedAliasNames.add(candidate);
450+ return candidate;
451+ };
452+
453+ const shouldAliasSignature = (signature: string): boolean =>
454+ recursiveSignatures.has(signature) || (usageCountBySignature.get(signature) ?? 0) > 1;
455+
456+ const renderShape = (shapeId: ShapeSymbolId, options: {
457+ position: "root" | "nested";
458+ stack?: readonly ShapeSymbolId[];
459+ }): string => {
460+ const shape = getShapeSymbol(catalog, shapeId);
461+ if (isInlineShapeNode(shape.node)) {
462+ return renderInlineShapeNode(shape.node);
463+ }
464+
465+ const signature = shapeSignature(shapeId);
466+ if (shouldAliasSignature(signature)) {
467+ usedAliasSignatures.add(signature);
468+ return aliasNameForSignature(signature);
469+ }
470+
471+ const stack = options.stack ?? [];
472+ if (stack.includes(shapeId)) {
473+ usedAliasSignatures.add(signature);
474+ return aliasNameForSignature(signature);
475+ }
476+
477+ return renderShapeBody(shapeId, [...stack, shapeId]);
478+ };
479+
480+ const renderIndexValueType = (shapeIds: readonly ShapeSymbolId[], allowUnknown: boolean): string => {
481+ const members = new Set<string>();
482+ if (allowUnknown) {
483+ members.add("unknown");
484+ }
485+
486+ for (const shapeId of shapeIds) {
487+ members.add(renderShape(shapeId, { position: "nested" }));
488+ }
489+
490+ return [...members].sort((left, right) => left.localeCompare(right)).join(" | ");
491+ };
492+
493+ const renderObjectFields = (
494+ node: Extract<ShapeNode, { type: "object" }> | Extract<ShapeNode, { type: "graphqlInterface" }>,
495+ stack: readonly ShapeSymbolId[],
496+ ): string => {
497+ const required = new Set(node.type === "object" ? (node.required ?? []) : []);
498+ const lines = Object.keys(node.fields)
499+ .sort((left, right) => left.localeCompare(right))
500+ .map((key) =>
501+ `${formatPropertyKey(key)}${required.has(key) ? "" : "?"}: ${renderShape(node.fields[key]!.shapeId, { position: "nested", stack })};`
502+ );
503+
504+ if (node.type === "object") {
505+ const patternShapeIds = Object.values(node.patternProperties ?? {});
506+ const hasUnknownIndex = node.additionalProperties === true;
507+ const additionalShapeIds = typeof node.additionalProperties === "string"
508+ ? [node.additionalProperties]
509+ : [];
510+ if (hasUnknownIndex || patternShapeIds.length > 0 || additionalShapeIds.length > 0) {
511+ lines.push(
512+ `[key: string]: ${renderIndexValueType([...patternShapeIds, ...additionalShapeIds], hasUnknownIndex)};`,
513+ );
514+ }
515+ }
516+
517+ return objectTypeLiteral(lines, " ");
518+ };
519+
520+ const renderShapeBody = (shapeId: ShapeSymbolId, stack: readonly ShapeSymbolId[]): string => {
521+ const shape = getShapeSymbol(catalog, shapeId);
522+ const node = shape.node;
523+
524+ switch (node.type) {
525+ case "unknown":
526+ case "scalar":
527+ case "const":
528+ case "enum":
529+ return renderInlineShapeNode(node);
530+ case "object":
531+ case "graphqlInterface":
532+ return renderObjectFields(node, stack);
533+ case "array":
534+ return `Array<${wrapCompositeType(renderShape(node.itemShapeId, { position: "nested", stack }))}>`;
535+ case "tuple": {
536+ const items = node.itemShapeIds.map((itemShapeId) => renderShape(itemShapeId, { position: "nested", stack }));
537+ const suffix = node.additionalItems === true
538+ ? ", ...unknown[]"
539+ : typeof node.additionalItems === "string"
540+ ? `, ...Array<${wrapCompositeType(renderShape(node.additionalItems, { position: "nested", stack }))}>`
541+ : "";
542+ return `[${items.join(", ")}${suffix}]`;
543+ }
544+ case "map":
545+ return `Record<string, ${renderShape(node.valueShapeId, { position: "nested", stack })}>`;
546+ case "allOf":
547+ return node.items.map((itemShapeId) => wrapCompositeType(renderShape(itemShapeId, { position: "nested", stack }))).join(" & ");
548+ case "anyOf":
549+ case "oneOf":
550+ return node.items.map((itemShapeId) => wrapCompositeType(renderShape(itemShapeId, { position: "nested", stack }))).join(" | ");
551+ case "nullable":
552+ return `${wrapCompositeType(renderShape(node.itemShapeId, { position: "nested", stack }))} | null`;
553+ case "ref":
554+ return renderShape(node.target, { position: "nested", stack });
555+ case "not":
556+ throw new Error(`Unsupported declaration shape node: ${node.type}`);
557+ case "conditional":
558+ throw new Error(`Unsupported declaration shape node: ${node.type}`);
559+ case "graphqlUnion":
560+ return node.memberTypeIds
561+ .map((memberTypeId) => wrapCompositeType(renderShape(memberTypeId, { position: "nested", stack })))
562+ .join(" | ");
563+ }
564+ };
565+
566+ const supportingTypes = (): string[] => {
567+ const pending = [...usedAliasSignatures];
568+ const emitted = new Set<string>();
569+
570+ while (pending.length > 0) {
571+ const signature = pending.pop()!;
572+ if (emitted.has(signature)) {
573+ continue;
574+ }
575+
576+ emitted.add(signature);
577+ const representativeShapeId = representativeShapeIdForSignature(signature);
578+ renderShapeBody(representativeShapeId, [representativeShapeId]);
579+
580+ for (const discoveredSignature of usedAliasSignatures) {
581+ if (!emitted.has(discoveredSignature)) {
582+ pending.push(discoveredSignature);
583+ }
584+ }
585+ }
586+
587+ const declarations = [...emitted]
588+ .sort((left, right) => aliasNameForSignature(left).localeCompare(aliasNameForSignature(right)))
589+ .map((signature) => {
590+ const representativeShapeId = representativeShapeIdForSignature(signature);
591+ return `type ${aliasNameForSignature(signature)} = ${renderShapeBody(representativeShapeId, [representativeShapeId])};`;
592+ });
593+
594+ return declarations;
595+ };
596+
597+ return {
598+ renderShape,
599+ supportingTypes,
600+ };
601+};
602+
603+const shapeAllowsOmittedArgs = (catalog: CatalogV1, shapeId: ShapeSymbolId): boolean => {
604+ const shape = getShapeSymbol(catalog, shapeId);
605+
606+ switch (shape.node.type) {
607+ case "ref":
608+ return shapeAllowsOmittedArgs(catalog, shape.node.target);
609+ case "object":
610+ return (shape.node.required ?? []).length === 0;
611+ default:
612+ return false;
613+ }
614+};
615+
616+const buildSourceDeclarationModel = (snapshot: CatalogSnapshotV1): SourceDeclarationModel => {
162617 const projected = projectCatalogForAgentSdk({
163618 catalog: snapshot.catalog,
164619 });
620+ const toolDescriptors = Object.values(projected.toolDescriptors)
621+ .sort((left, right) => left.toolPath.join(".").localeCompare(right.toolPath.join(".")));
622+ const renderer = createCatalogTypeRenderer({
623+ catalog: projected.catalog,
624+ rootShapeIds: toolDescriptors.flatMap((descriptor) => [
625+ descriptor.callShapeId,
626+ ...(descriptor.resultShapeId ? [descriptor.resultShapeId] : []),
627+ ]),
628+ });
165629
166− return Object.values(projected.toolDescriptors)
167− .sort((left, right) => left.toolPath.join(".").localeCompare(right.toolPath.join(".")))
168− .map((descriptor) => {
169− const inputSchema = shapeToJsonSchema(projected.catalog, descriptor.callShapeId);
170− const outputSchema = descriptor.resultShapeId
171− ? shapeToJsonSchema(projected.catalog, descriptor.resultShapeId)
172− : undefined;
173−
174− return {
175− segments: descriptor.toolPath,
176− inputType: typeSignatureFromSchemaWithOptions(
177− inputSchema,
178− "unknown",
179− DECLARATION_RENDER_OPTIONS,
180− ),
181− outputType: typeSignatureFromSchemaWithOptions(
182− outputSchema,
183− "unknown",
184− DECLARATION_RENDER_OPTIONS,
185− ),
186− argsOptional: schemaAllowsOmittedArgs(inputSchema),
187− } satisfies ToolMethodNode;
188− });
630+ const methods = toolDescriptors
631+ .map((descriptor) => ({
632+ segments: descriptor.toolPath,
633+ inputType: renderer.renderShape(descriptor.callShapeId, { position: "root" }),
634+ outputType: descriptor.resultShapeId
635+ ? renderer.renderShape(descriptor.resultShapeId, { position: "root" })
636+ : "unknown",
637+ argsOptional: shapeAllowsOmittedArgs(projected.catalog, descriptor.callShapeId),
638+ }) satisfies ToolMethodNode);
639+
640+ return {
641+ methods,
642+ supportingTypes: renderer.supportingTypes(),
643+ };
189644 };
190645
191646 const sourceDeclarationText = (entry: SourceDeclarationEntry): string => {
192647 const interfaceName = sourceTypeInterfaceName(entry.source.id);
193− const tree = buildToolTree(collectToolMethods(entry.snapshot));
648+ const declarationModel = buildSourceDeclarationModel(entry.snapshot);
649+ const tree = buildToolTree(declarationModel.methods);
194650 const body = renderToolTreeType(tree, 0);
195651
196652 return [
197653 "// Generated by executor. Do not edit by hand.",
198654 `// Source: ${entry.source.name} (${entry.source.id})`,
199655 "",
656+ ...declarationModel.supportingTypes,
657+ ...(declarationModel.supportingTypes.length > 0 ? [""] : []),
200658 `export interface ${interfaceName} ${body}`,
201659 "",
202660 `export declare const tools: ${interfaceName};`,
@@ -367,25 +825,46 @@ export const syncSourceTypeDeclarationNode = (input: { · 164 unmodified lines
367825 syncSourceTypeDeclaration(input).pipe(Effect.provide(NodeFileSystem.layer));
368826
369827 const logBackgroundDeclarationError = (label: string, cause: unknown): void => {
370− const message = cause instanceof Error ? cause.message : String(cause);
828+ const message = Cause.isCause(cause)
829+ ? Cause.pretty(cause)
830+ : cause instanceof Error
831+ ? cause.message
832+ : String(cause);
371833 console.warn(`[source-types] ${label} failed: ${message}`);
372834 };
373835
836+const BACKGROUND_DECLARATION_REFRESH_DELAY = "1500 millis";
837+const workspaceRefreshFibers = new Map<string, Fiber.RuntimeFiber<void, never>>();
838+const sourceRefreshFibers = new Map<string, Fiber.RuntimeFiber<void, never>>();
839+
374840 export const refreshWorkspaceSourceTypeDeclarationsInBackground = (input: {
375841 context: ResolvedLocalWorkspaceContext;
376842 entries: readonly SourceDeclarationEntry[];
377843 }): void => {
378− setTimeout(() => {
379− Effect.runFork(
380− syncWorkspaceSourceTypeDeclarationsNode(input).pipe(
381− Effect.catchAll((cause) =>
382− Effect.sync(() => {
383− logBackgroundDeclarationError("workspace declaration refresh", cause);
384− })
844+ const key = input.context.configDirectory;
845+ const existingFiber = workspaceRefreshFibers.get(key);
846+ if (existingFiber) {
847+ Effect.runFork(Fiber.interruptFork(existingFiber));
848+ }
849+
850+ const fiber = Effect.runFork(
851+ Effect.sleep(BACKGROUND_DECLARATION_REFRESH_DELAY).pipe(
852+ Effect.zipRight(
853+ syncWorkspaceSourceTypeDeclarationsNode(input).pipe(
854+ Effect.catchAllCause((cause) =>
855+ Effect.sync(() => {
856+ logBackgroundDeclarationError("workspace declaration refresh", cause);
857+ })
858+ ),
385859 ),
386860 ),
387− );
388− }, 0);
861+ ),
862+ );
863+
864+ workspaceRefreshFibers.set(key, fiber);
865+ fiber.addObserver(() => {
866+ workspaceRefreshFibers.delete(key);
867+ });
389868 };
390869
391870 export const refreshSourceTypeDeclarationInBackground = (input: {
@@ -393,15 +872,28 @@ export const refreshSourceTypeDeclarationInBackground = (input: { · 1 unmodified line
393872 source: Source;
394873 snapshot: CatalogSnapshotV1 | null;
395874 }): void => {
396− setTimeout(() => {
397− Effect.runFork(
398− syncSourceTypeDeclarationNode(input).pipe(
399− Effect.catchAll((cause) =>
400− Effect.sync(() => {
401− logBackgroundDeclarationError(`source ${input.source.id} declaration refresh`, cause);
402− })
875+ const key = `${input.context.configDirectory}:${input.source.id}`;
876+ const existingFiber = sourceRefreshFibers.get(key);
877+ if (existingFiber) {
878+ Effect.runFork(Fiber.interruptFork(existingFiber));
879+ }
880+
881+ const fiber = Effect.runFork(
882+ Effect.sleep(BACKGROUND_DECLARATION_REFRESH_DELAY).pipe(
883+ Effect.zipRight(
884+ syncSourceTypeDeclarationNode(input).pipe(
885+ Effect.catchAllCause((cause) =>
886+ Effect.sync(() => {
887+ logBackgroundDeclarationError(`source ${input.source.id} declaration refresh`, cause);
888+ })
889+ ),
403890 ),
404891 ),
405− );
406− }, 0);
892+ ),
893+ );
894+
895+ sourceRefreshFibers.set(key, fiber);
896+ fiber.addObserver(() => {
897+ sourceRefreshFibers.delete(key);
898+ });
407899 };
packages/server/package.json+8−0
@@ -9,9 +9,17 @@ · 8 unmodified lines
99 "typecheck": "bunx tsc --noEmit -p tsconfig.json"
1010 },
1111 "dependencies": {
12+ "@effect/opentelemetry": "^0.61.0",
1213 "@effect/platform": "catalog:",
1314 "@executor/control-plane": "workspace:*",
1415 "@executor/executor-mcp": "workspace:*",
16+ "@opentelemetry/api": "^1.9.0",
17+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0",
18+ "@opentelemetry/exporter-trace-otlp-http": "^0.213.0",
19+ "@opentelemetry/resources": "^2.6.0",
20+ "@opentelemetry/sdk-trace-base": "^2.6.0",
21+ "@opentelemetry/sdk-trace-node": "^2.6.0",
22+ "@opentelemetry/semantic-conventions": "^1.40.0",
1523 "effect": "catalog:"
1624 },
1725 "devDependencies": {
packages/server/src/config.ts+5−0
@@ -11,6 +11,11 @@ export const EXECUTOR_LOCAL_DATA_DIR_ENV = "EXECUTOR_LOCAL_DATA_DIR"; · 10 unmodified lines
1111 export const EXECUTOR_SERVER_PID_FILE_ENV = "EXECUTOR_SERVER_PID_FILE";
1212 export const EXECUTOR_SERVER_LOG_FILE_ENV = "EXECUTOR_SERVER_LOG_FILE";
1313 export const EXECUTOR_WEB_ASSETS_DIR_ENV = "EXECUTOR_WEB_ASSETS_DIR";
14+export const EXECUTOR_TRACE_ENABLED_ENV = "EXECUTOR_TRACE_ENABLED";
15+export const EXECUTOR_TRACE_OTLP_ENDPOINT_ENV = "EXECUTOR_TRACE_OTLP_ENDPOINT";
16+export const EXECUTOR_TRACE_OTLP_HTTP_ENDPOINT_ENV = "EXECUTOR_TRACE_OTLP_HTTP_ENDPOINT";
17+export const EXECUTOR_TRACE_SERVICE_NAME_ENV = "EXECUTOR_TRACE_SERVICE_NAME";
18+export const EXECUTOR_TRACE_QUERY_BASE_URL_ENV = "EXECUTOR_TRACE_QUERY_BASE_URL";
1419
1520 const trim = (value: string | undefined): string | undefined => {
1621 const candidate = value?.trim();
packages/server/src/index.ts+36−3
@@ -30,7 +30,16 @@ import { · 29 unmodified lines
3030 DEFAULT_SERVER_PID_FILE,
3131 DEFAULT_SERVER_HOST,
3232 DEFAULT_SERVER_PORT,
33+ EXECUTOR_TRACE_ENABLED_ENV,
34+ EXECUTOR_TRACE_OTLP_ENDPOINT_ENV,
35+ EXECUTOR_TRACE_OTLP_HTTP_ENDPOINT_ENV,
36+ EXECUTOR_TRACE_QUERY_BASE_URL_ENV,
37+ EXECUTOR_TRACE_SERVICE_NAME_ENV,
3338 } from "./config";
39+import {
40+ createLocalTracingRuntimeFromEnv,
41+ tracingSearchUrl,
42+} from "./tracing";
3443
3544 export {
3645 DEFAULT_EXECUTOR_DATA_DIR,
@@ -47,6 +56,11 @@ export { · 10 unmodified lines
4756 EXECUTOR_SERVER_PID_FILE_ENV,
4857 EXECUTOR_SERVER_LOG_FILE_ENV,
4958 EXECUTOR_WEB_ASSETS_DIR_ENV,
59+ EXECUTOR_TRACE_ENABLED_ENV,
60+ EXECUTOR_TRACE_OTLP_ENDPOINT_ENV,
61+ EXECUTOR_TRACE_OTLP_HTTP_ENDPOINT_ENV,
62+ EXECUTOR_TRACE_QUERY_BASE_URL_ENV,
63+ EXECUTOR_TRACE_SERVICE_NAME_ENV,
5064 SERVER_POLL_INTERVAL_MS,
5165 SERVER_START_TIMEOUT_MS,
5266 } from "./config";
@@ -205,13 +219,20 @@ const createRuntimeWithLegacyMigration = ( · 152 unmodified lines
205219 );
206220 });
207221
208−const createControlPlaneWebHandler = (runtime: ControlPlaneRuntime) =>
222+const createControlPlaneWebHandler = (
223+ runtime: ControlPlaneRuntime,
224+ tracingRuntime: ReturnType<typeof createLocalTracingRuntimeFromEnv>,
225+) =>
209226 Effect.acquireRelease(
210227 Effect.sync(() =>
211228 HttpApiBuilder.toWebHandler(
212229 Layer.merge(
213230 HttpApiBuilder.middlewareOpenApi({ path: "/v1/openapi.json" }).pipe(
214− Layer.provideMerge(createControlPlaneApiLayer(runtime.runtimeLayer))
231+ Layer.provideMerge(
232+ createControlPlaneApiLayer(runtime.runtimeLayer).pipe(
233+ Layer.provideMerge(tracingRuntime?.layer ?? Layer.empty),
234+ ),
235+ )
215236 ),
216237 HttpServer.layerContext,
217238 ),
@@ -390,6 +411,18 @@ export const createLocalExecutorRequestHandler = ( · 172 unmodified lines
390411 }
391412
392413 let baseUrlRef: string | undefined;
414+ const tracingRuntime = createLocalTracingRuntimeFromEnv();
415+
416+ if (tracingRuntime) {
417+ yield* Effect.sync(() => {
418+ console.info(
419+ `[executor] tracing enabled -> ${tracingSearchUrl({
420+ queryBaseUrl: tracingRuntime.queryBaseUrl,
421+ serviceName: tracingRuntime.serviceName,
422+ })}`,
423+ );
424+ });
425+ }
393426
394427 const runtime = yield* Effect.acquireRelease(
395428 createRuntimeWithLegacyMigration(
@@ -401,7 +434,7 @@ export const createLocalExecutorRequestHandler = ( · 5 unmodified lines
401434 disposeRuntime,
402435 );
403436
404− const apiHandler = yield* createControlPlaneWebHandler(runtime);
437+ const apiHandler = yield* createControlPlaneWebHandler(runtime, tracingRuntime);
405438 const mcpHandler = yield* Effect.acquireRelease(
406439 Effect.sync(() => createExecutorMcpRequestHandler(runtime)),
407440 (handler: ExecutorMcpHandler) =>
packages/server/src/tracing.ts+66−0
@@ -0,0 +1,66 @@
1+import * as NodeSdk from "@effect/opentelemetry/NodeSdk";
2+import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
3+import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
4+import * as Layer from "effect/Layer";
5+
6+import {
7+ EXECUTOR_TRACE_ENABLED_ENV,
8+ EXECUTOR_TRACE_OTLP_ENDPOINT_ENV,
9+ EXECUTOR_TRACE_OTLP_HTTP_ENDPOINT_ENV,
10+ EXECUTOR_TRACE_QUERY_BASE_URL_ENV,
11+ EXECUTOR_TRACE_SERVICE_NAME_ENV,
12+} from "./config";
13+
14+const DEFAULT_TRACE_OTLP_ENDPOINT = "http://127.0.0.1:4317";
15+const DEFAULT_TRACE_QUERY_BASE_URL = "http://127.0.0.1:16686";
16+const DEFAULT_TRACE_SERVICE_NAME = "executor-local";
17+
18+export type LocalTracingRuntime = {
19+ readonly layer: Layer.Layer<never>;
20+ readonly otlpEndpoint: string;
21+ readonly queryBaseUrl: string;
22+ readonly serviceName: string;
23+};
24+
25+const trim = (value: string | undefined): string | undefined => {
26+ const candidate = value?.trim();
27+ return candidate && candidate.length > 0 ? candidate : undefined;
28+};
29+
30+const envFlag = (value: string | undefined): boolean => {
31+ const normalized = value?.trim().toLowerCase();
32+ return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
33+};
34+
35+export const createLocalTracingRuntimeFromEnv = (): LocalTracingRuntime | null => {
36+ if (!envFlag(process.env[EXECUTOR_TRACE_ENABLED_ENV])) {
37+ return null;
38+ }
39+
40+ const otlpEndpoint =
41+ trim(process.env[EXECUTOR_TRACE_OTLP_ENDPOINT_ENV])
42+ ?? trim(process.env[EXECUTOR_TRACE_OTLP_HTTP_ENDPOINT_ENV])
43+ ?? DEFAULT_TRACE_OTLP_ENDPOINT;
44+ const serviceName = trim(process.env[EXECUTOR_TRACE_SERVICE_NAME_ENV]) ?? DEFAULT_TRACE_SERVICE_NAME;
45+ const queryBaseUrl = trim(process.env[EXECUTOR_TRACE_QUERY_BASE_URL_ENV]) ?? DEFAULT_TRACE_QUERY_BASE_URL;
46+
47+ return {
48+ layer: NodeSdk.layer(() => ({
49+ resource: {
50+ serviceName,
51+ attributes: {
52+ "service.namespace": "executor",
53+ },
54+ },
55+ spanProcessor: [new SimpleSpanProcessor(new OTLPTraceExporter({ url: otlpEndpoint }))],
56+ })),
57+ otlpEndpoint,
58+ queryBaseUrl,
59+ serviceName,
60+ };
61+};
62+
63+export const tracingSearchUrl = (input: {
64+ queryBaseUrl: string;
65+ serviceName: string;
66+}): string => `${input.queryBaseUrl.replace(/\/$/, "")}/search?service=${encodeURIComponent(input.serviceName)}`;
personal-notes/otel-tracing-for-source-connect.md+50−0
@@ -0,0 +1,50 @@
1+# OTel Tracing For Source Connect
2+
3+## Setup
4+
5+- Use the Effect-native OTel layer from `@effect/opentelemetry`, not a hand-rolled tracer bridge.
6+- Start Jaeger with `bun run trace:up`.
7+- Run the dev server with:
8+
9+```bash
10+EXECUTOR_TRACE_ENABLED=1 \
11+EXECUTOR_TRACE_SERVICE_NAME=executor-local \
12+EXECUTOR_TRACE_OTLP_ENDPOINT=http://127.0.0.1:4317 \
13+bun run dev
14+```
15+
16+- Query traces via:
17+
18+```bash
19+curl "http://127.0.0.1:16686/api/traces?service=executor-local&limit=20"
20+```
21+
22+## Useful spans
23+
24+- `source.connect.http`
25+- `source.catalog.sync`
26+- `source.store.persist`
27+- `source.store.load_by_id`
28+- `source.status.update`
29+- `graphql.syncCatalog`
30+- `graphql.introspection.fetch`
31+- `graphql.manifest.extract`
32+- `graphql.definitions.compile`
33+- `graphql.operations.build`
34+- `graphql.snapshot.build`
35+
36+## What the traces showed
37+
38+- Raw Linear introspection fetch is fast, around sub-second to about one second.
39+- The biggest remaining cost in the GraphQL import path is `graphql.operations.build`, around 5s on Linear.
40+- A large earlier regression was not GraphQL import itself. It was declaration refresh churn interfering with the connect flow.
41+- The fix was:
42+ - keep `.d.ts` generation backgrounded
43+ - debounce it with Effect fibers / `Effect.sleep`
44+ - avoid scheduling workspace-wide declaration refresh during normal `probing` / `connected` source persists
45+
46+## Current state
47+
48+- Fresh Linear `connect` is back down to about 7-8s instead of ~70s.
49+- `linear.d.ts` is much smaller now because source-level shared shapes are deduped.
50+- The next performance target is `graphql.operations.build`, not tracing or `.d.ts` scheduling.