dotdo 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/cli/README.md +238 -0
  2. package/cli/agent.ts +72 -0
  3. package/cli/bin.js +44 -0
  4. package/cli/bin.ts +38 -0
  5. package/cli/build.ts +157 -0
  6. package/cli/commands/auth/login.ts +14 -0
  7. package/cli/commands/auth/logout.ts +6 -0
  8. package/cli/commands/auth/whoami.ts +16 -0
  9. package/cli/commands/deploy-multi.ts +245 -0
  10. package/cli/commands/dev/deploy.ts +100 -0
  11. package/cli/commands/dev/dev.ts +95 -0
  12. package/cli/commands/dev/logs.ts +91 -0
  13. package/cli/commands/dev-local.ts +88 -0
  14. package/cli/commands/do-ops.ts +314 -0
  15. package/cli/commands/index.ts +100 -0
  16. package/cli/commands/init.ts +247 -0
  17. package/cli/commands/introspect/emitter.ts +315 -0
  18. package/cli/commands/introspect/index.ts +193 -0
  19. package/cli/commands/link.ts +598 -0
  20. package/cli/commands/snippets.ts +415 -0
  21. package/cli/commands/tunnel.ts +239 -0
  22. package/cli/device-auth.ts +289 -0
  23. package/cli/fallback.ts +12 -0
  24. package/cli/index.ts +121 -0
  25. package/cli/main.ts +246 -0
  26. package/cli/mcp-stdio.ts +790 -0
  27. package/cli/package.json +62 -0
  28. package/cli/runtime/do-registry.ts +193 -0
  29. package/cli/runtime/embedded-db.ts +344 -0
  30. package/cli/runtime/index.ts +9 -0
  31. package/cli/runtime/miniflare-adapter.ts +162 -0
  32. package/cli/sandbox.ts +82 -0
  33. package/cli/src/args.ts +174 -0
  34. package/cli/src/auth.ts +55 -0
  35. package/cli/src/commands/call.ts +84 -0
  36. package/cli/src/commands/charge.ts +96 -0
  37. package/cli/src/commands/config.ts +115 -0
  38. package/cli/src/commands/email.ts +112 -0
  39. package/cli/src/commands/llm.ts +115 -0
  40. package/cli/src/commands/queue.ts +134 -0
  41. package/cli/src/commands/text.ts +86 -0
  42. package/cli/src/config.ts +185 -0
  43. package/cli/src/output.ts +246 -0
  44. package/cli/src/rpc.ts +192 -0
  45. package/cli/utils/config.ts +282 -0
  46. package/cli/utils/detect.ts +73 -0
  47. package/cli/utils/index.ts +15 -0
  48. package/cli/utils/logger.ts +232 -0
  49. package/dist/ai/template-literals.js +2 -2
  50. package/dist/ai/template-literals.js.map +1 -1
  51. package/dist/api/middleware/auth.js +3 -2
  52. package/dist/api/middleware/auth.js.map +1 -1
  53. package/dist/db/iceberg/inverted-index.js +1 -1
  54. package/dist/db/iceberg/inverted-index.js.map +1 -1
  55. package/dist/db/iceberg/puffin.js.map +1 -1
  56. package/dist/db/json-indexes.js.map +1 -1
  57. package/dist/db/objects.js.map +1 -1
  58. package/dist/db/primitives/dag-scheduler/index.js +1 -1
  59. package/dist/db/primitives/dag-scheduler/index.js.map +1 -1
  60. package/dist/db/primitives/observability.js.map +1 -1
  61. package/dist/db/primitives/schema-evolution.js.map +1 -1
  62. package/dist/db/primitives/temporal-store.js.map +1 -1
  63. package/dist/db/primitives/typed-column-store.js.map +1 -1
  64. package/dist/db/primitives/utils/duration.js.map +1 -1
  65. package/dist/db/primitives/utils/murmur3.js +12 -14
  66. package/dist/db/primitives/utils/murmur3.js.map +1 -1
  67. package/dist/db/primitives/window-manager.js.map +1 -1
  68. package/dist/db/stores.js.map +1 -1
  69. package/dist/db/things.js.map +1 -1
  70. package/dist/lib/DODispatcher.js +2 -2
  71. package/dist/lib/DODispatcher.js.map +1 -1
  72. package/dist/lib/auto-wiring.js.map +1 -1
  73. package/dist/lib/channels/email.js +1 -1
  74. package/dist/lib/channels/email.js.map +1 -1
  75. package/dist/lib/channels/slack-blockkit.js.map +1 -1
  76. package/dist/lib/cloudflare/ai.js +1 -1
  77. package/dist/lib/cloudflare/ai.js.map +1 -1
  78. package/dist/lib/cloudflare/kv.js +1 -1
  79. package/dist/lib/cloudflare/kv.js.map +1 -1
  80. package/dist/lib/cloudflare/r2.js +3 -3
  81. package/dist/lib/cloudflare/r2.js.map +1 -1
  82. package/dist/lib/cloudflare/vectorize.js.map +1 -1
  83. package/dist/lib/cloudflare/workflows.js.map +1 -1
  84. package/dist/lib/executors/AgenticFunctionExecutor.js.map +1 -1
  85. package/dist/lib/executors/CodeFunctionExecutor.js.map +1 -1
  86. package/dist/lib/executors/GenerativeFunctionExecutor.js.map +1 -1
  87. package/dist/lib/executors/HumanFunctionExecutor.js +1 -1
  88. package/dist/lib/executors/HumanFunctionExecutor.js.map +1 -1
  89. package/dist/lib/executors/ParallelStepExecutor.js.map +1 -1
  90. package/dist/lib/experiments.js.map +1 -1
  91. package/dist/lib/flags/store.js.map +1 -1
  92. package/dist/lib/functions/FunctionComposition.js.map +1 -1
  93. package/dist/lib/functions/FunctionMiddleware.js.map +1 -1
  94. package/dist/lib/functions/FunctionRegistry.js.map +1 -1
  95. package/dist/lib/humans/templates.js.map +1 -1
  96. package/dist/lib/identity.js +2 -2
  97. package/dist/lib/identity.js.map +1 -1
  98. package/dist/lib/logging/index.js.map +1 -1
  99. package/dist/lib/mixins/bash.js +1 -73
  100. package/dist/lib/mixins/bash.js.map +1 -1
  101. package/dist/lib/mixins/git.js +0 -5
  102. package/dist/lib/mixins/git.js.map +1 -1
  103. package/dist/lib/mixins/npm.js.map +1 -1
  104. package/dist/lib/noun-id.js.map +1 -1
  105. package/dist/lib/rate-limit/sliding-window.js.map +1 -1
  106. package/dist/lib/rpc/bindings.js.map +1 -1
  107. package/dist/lib/safe-stringify.js.map +1 -1
  108. package/dist/lib/sandbox/miniflare-sandbox.js.map +1 -1
  109. package/dist/lib/sqids.js.map +1 -1
  110. package/dist/lib/sql/adapters/node-sql-parser.js.map +1 -1
  111. package/dist/lib/sql/adapters/pgsql-parser.js +19 -18
  112. package/dist/lib/sql/adapters/pgsql-parser.js.map +1 -1
  113. package/dist/metrics/hunch.js.map +1 -1
  114. package/dist/objects/API.js +1 -1
  115. package/dist/objects/API.js.map +1 -1
  116. package/dist/objects/Agent.js.map +1 -1
  117. package/dist/objects/Browser.js.map +1 -1
  118. package/dist/objects/CLI.js.map +1 -1
  119. package/dist/objects/DOBase.js.map +1 -1
  120. package/dist/objects/DOCache.js +153 -0
  121. package/dist/objects/DOCache.js.map +1 -0
  122. package/dist/objects/DOFull.js.map +1 -1
  123. package/dist/objects/Entity.js.map +1 -1
  124. package/dist/objects/Human.js.map +1 -1
  125. package/dist/objects/IcebergMetadataDO.js.map +1 -1
  126. package/dist/objects/IntegrationsDO.js.map +1 -1
  127. package/dist/objects/ObservabilityBroadcaster.js.map +1 -1
  128. package/dist/objects/Package.js.map +1 -1
  129. package/dist/objects/Product.js +1 -1
  130. package/dist/objects/Product.js.map +1 -1
  131. package/dist/objects/SaaS.js.map +1 -1
  132. package/dist/objects/SandboxDO.js.map +1 -1
  133. package/dist/objects/Service.js.map +1 -1
  134. package/dist/objects/VectorShardDO.js +9 -7
  135. package/dist/objects/VectorShardDO.js.map +1 -1
  136. package/dist/objects/Workflow.js.map +1 -1
  137. package/dist/objects/WorkflowFactory.js.map +1 -1
  138. package/dist/objects/WorkflowRuntime.js.map +1 -1
  139. package/dist/objects/lifecycle/Branch.js.map +1 -1
  140. package/dist/objects/lifecycle/Clone.js +1 -1
  141. package/dist/objects/lifecycle/Clone.js.map +1 -1
  142. package/dist/objects/lifecycle/Compact.js.map +1 -1
  143. package/dist/objects/lifecycle/Shard.js.map +1 -1
  144. package/dist/objects/persistence/checkpoint-manager.js.map +1 -1
  145. package/dist/objects/persistence/migration-runner.js.map +1 -1
  146. package/dist/objects/persistence/replication-manager.js +2 -2
  147. package/dist/objects/persistence/replication-manager.js.map +1 -1
  148. package/dist/objects/persistence/tiered-storage-manager.js.map +1 -1
  149. package/dist/objects/persistence/wal-manager.js.map +1 -1
  150. package/dist/objects/transport/auth-layer.js.map +1 -1
  151. package/dist/objects/transport/chain.js.map +1 -1
  152. package/dist/objects/transport/mcp-server.js +7 -6
  153. package/dist/objects/transport/mcp-server.js.map +1 -1
  154. package/dist/objects/transport/rest-autowire.js +3 -2
  155. package/dist/objects/transport/rest-autowire.js.map +1 -1
  156. package/dist/objects/transport/rest-router.js.map +1 -1
  157. package/dist/objects/transport/rpc-server.js +18 -15
  158. package/dist/objects/transport/rpc-server.js.map +1 -1
  159. package/dist/objects/transport/shared.js +2 -1
  160. package/dist/objects/transport/shared.js.map +1 -1
  161. package/dist/snippets/artifacts-ingest.js.map +1 -1
  162. package/dist/snippets/artifacts-serve.js.map +1 -1
  163. package/dist/snippets/search.js.map +1 -1
  164. package/dist/workflows/ScheduleManager.js.map +1 -1
  165. package/dist/workflows/StepResultStorage.js.map +1 -1
  166. package/dist/workflows/WaitForEventManager.js.map +1 -1
  167. package/dist/workflows/compat/backends/cloudflare-workflows.js.map +1 -1
  168. package/dist/workflows/compat/inngest/index.js.map +1 -1
  169. package/dist/workflows/compat/qstash/index.js.map +1 -1
  170. package/dist/workflows/compat/temporal/client.js.map +1 -1
  171. package/dist/workflows/compat/temporal/index.js.map +1 -1
  172. package/dist/workflows/compat/trigger/index.js.map +1 -1
  173. package/dist/workflows/compat/utils/index.js.map +1 -1
  174. package/dist/workflows/context/correlation.js +2 -2
  175. package/dist/workflows/context/correlation.js.map +1 -1
  176. package/dist/workflows/context/experiment.js +1 -1
  177. package/dist/workflows/context/experiment.js.map +1 -1
  178. package/dist/workflows/context/flag.js +1 -1
  179. package/dist/workflows/context/flag.js.map +1 -1
  180. package/dist/workflows/context/measure.js +1 -1
  181. package/dist/workflows/context/measure.js.map +1 -1
  182. package/dist/workflows/context/rate-limit.js.map +1 -1
  183. package/dist/workflows/data/entity-events/entity-events.js.map +1 -1
  184. package/dist/workflows/data/experiment/index.js.map +1 -1
  185. package/dist/workflows/data/goal/context.js +1 -1
  186. package/dist/workflows/data/goal/context.js.map +1 -1
  187. package/dist/workflows/data/measure/index.js +1 -1
  188. package/dist/workflows/data/measure/index.js.map +1 -1
  189. package/dist/workflows/data/stream/index.js +10 -76
  190. package/dist/workflows/data/stream/index.js.map +1 -1
  191. package/dist/workflows/data/track/context.js.map +1 -1
  192. package/dist/workflows/data/view/context.js.map +1 -1
  193. package/dist/workflows/domain.js.map +1 -1
  194. package/dist/workflows/flags.js +1 -1
  195. package/dist/workflows/flags.js.map +1 -1
  196. package/dist/workflows/hash.js.map +1 -1
  197. package/dist/workflows/on.js +1 -1
  198. package/dist/workflows/on.js.map +1 -1
  199. package/dist/workflows/schedule-builder.js.map +1 -1
  200. package/dist/workflows/visibility/index.js +0 -2
  201. package/dist/workflows/visibility/index.js.map +1 -1
  202. package/dist/workflows/visibility/query-parser.js.map +1 -1
  203. package/package.json +18 -3
  204. package/dist/api/analytics/router.js +0 -601
  205. package/dist/api/analytics/router.js.map +0 -1
  206. package/dist/api/index.js +0 -158
  207. package/dist/api/index.js.map +0 -1
  208. package/dist/api/middleware/error-handling.js +0 -176
  209. package/dist/api/middleware/error-handling.js.map +0 -1
  210. package/dist/api/middleware/request-id.js +0 -21
  211. package/dist/api/middleware/request-id.js.map +0 -1
  212. package/dist/api/pages.js +0 -1180
  213. package/dist/api/pages.js.map +0 -1
  214. package/dist/api/routes/api.js +0 -612
  215. package/dist/api/routes/api.js.map +0 -1
  216. package/dist/api/routes/browsers.js +0 -471
  217. package/dist/api/routes/browsers.js.map +0 -1
  218. package/dist/api/routes/do.js +0 -188
  219. package/dist/api/routes/do.js.map +0 -1
  220. package/dist/api/routes/mcp.js +0 -459
  221. package/dist/api/routes/mcp.js.map +0 -1
  222. package/dist/api/routes/obs.js +0 -445
  223. package/dist/api/routes/obs.js.map +0 -1
  224. package/dist/api/routes/openapi.js +0 -794
  225. package/dist/api/routes/openapi.js.map +0 -1
  226. package/dist/api/routes/rpc.js +0 -1103
  227. package/dist/api/routes/rpc.js.map +0 -1
  228. package/dist/api/routes/sandboxes.js +0 -389
  229. package/dist/api/routes/sandboxes.js.map +0 -1
  230. package/dist/api/test-do.js +0 -38
  231. package/dist/api/test-do.js.map +0 -1
  232. package/dist/api/types.js +0 -11
  233. package/dist/api/types.js.map +0 -1
  234. package/dist/cli/bin.js +0 -2
  235. package/dist/cli/main.js +0 -52342
  236. package/dist/do/bash.js +0 -35
  237. package/dist/do/bash.js.map +0 -1
  238. package/dist/do/fs.js +0 -25
  239. package/dist/do/fs.js.map +0 -1
  240. package/dist/do/full.js +0 -61
  241. package/dist/do/full.js.map +0 -1
  242. package/dist/do/git.js +0 -28
  243. package/dist/do/git.js.map +0 -1
  244. package/dist/do/index.js +0 -52
  245. package/dist/do/index.js.map +0 -1
  246. package/dist/lib/agent/tools/bash.js +0 -336
  247. package/dist/lib/agent/tools/bash.js.map +0 -1
  248. package/dist/lib/agent/tools/edit.js +0 -157
  249. package/dist/lib/agent/tools/edit.js.map +0 -1
  250. package/dist/lib/agent/tools/glob.js +0 -137
  251. package/dist/lib/agent/tools/glob.js.map +0 -1
  252. package/dist/lib/agent/tools/grep.js +0 -315
  253. package/dist/lib/agent/tools/grep.js.map +0 -1
  254. package/dist/lib/agent/tools/index.js +0 -71
  255. package/dist/lib/agent/tools/index.js.map +0 -1
  256. package/dist/lib/agent/tools/read.js +0 -212
  257. package/dist/lib/agent/tools/read.js.map +0 -1
  258. package/dist/lib/agent/tools/types.js +0 -197
  259. package/dist/lib/agent/tools/types.js.map +0 -1
  260. package/dist/lib/agent/tools/write.js +0 -159
  261. package/dist/lib/agent/tools/write.js.map +0 -1
  262. package/dist/lib/mixins/index.js +0 -29
  263. package/dist/lib/mixins/index.js.map +0 -1
  264. package/dist/primitives/bashx/src/ast/analyze.js +0 -1472
  265. package/dist/primitives/bashx/src/ast/analyze.js.map +0 -1
  266. package/dist/primitives/bashx/src/ast/parser.js +0 -1488
  267. package/dist/primitives/bashx/src/ast/parser.js.map +0 -1
  268. package/dist/primitives/bashx/src/do/commands/crypto.js +0 -1954
  269. package/dist/primitives/bashx/src/do/commands/crypto.js.map +0 -1
  270. package/dist/primitives/bashx/src/do/commands/data-processing.js +0 -1812
  271. package/dist/primitives/bashx/src/do/commands/data-processing.js.map +0 -1
  272. package/dist/primitives/bashx/src/do/commands/extended-utils.js +0 -804
  273. package/dist/primitives/bashx/src/do/commands/extended-utils.js.map +0 -1
  274. package/dist/primitives/bashx/src/do/commands/math-control.js +0 -1122
  275. package/dist/primitives/bashx/src/do/commands/math-control.js.map +0 -1
  276. package/dist/primitives/bashx/src/do/commands/posix-utils.js +0 -1015
  277. package/dist/primitives/bashx/src/do/commands/posix-utils.js.map +0 -1
  278. package/dist/primitives/bashx/src/do/commands/system-utils.js +0 -687
  279. package/dist/primitives/bashx/src/do/commands/system-utils.js.map +0 -1
  280. package/dist/primitives/bashx/src/do/commands/test-command.js +0 -523
  281. package/dist/primitives/bashx/src/do/commands/test-command.js.map +0 -1
  282. package/dist/primitives/bashx/src/do/commands/text-processing.js +0 -1550
  283. package/dist/primitives/bashx/src/do/commands/text-processing.js.map +0 -1
  284. package/dist/primitives/bashx/src/do/container-executor.js +0 -429
  285. package/dist/primitives/bashx/src/do/container-executor.js.map +0 -1
  286. package/dist/primitives/bashx/src/do/index.js +0 -668
  287. package/dist/primitives/bashx/src/do/index.js.map +0 -1
  288. package/dist/primitives/bashx/src/do/tiered-executor.js +0 -2647
  289. package/dist/primitives/bashx/src/do/tiered-executor.js.map +0 -1
  290. package/dist/primitives/bashx/src/do/worker.js +0 -352
  291. package/dist/primitives/bashx/src/do/worker.js.map +0 -1
  292. package/dist/primitives/bashx/src/types.js +0 -10
  293. package/dist/primitives/bashx/src/types.js.map +0 -1
  294. package/dist/primitives/fsx/core/backend.js +0 -480
  295. package/dist/primitives/fsx/core/backend.js.map +0 -1
  296. package/dist/primitives/fsx/core/constants.js +0 -140
  297. package/dist/primitives/fsx/core/constants.js.map +0 -1
  298. package/dist/primitives/fsx/core/fsx.js +0 -1184
  299. package/dist/primitives/fsx/core/fsx.js.map +0 -1
  300. package/dist/primitives/fsx/core/glob/glob.js +0 -438
  301. package/dist/primitives/fsx/core/glob/glob.js.map +0 -1
  302. package/dist/primitives/fsx/core/glob/index.js +0 -8
  303. package/dist/primitives/fsx/core/glob/index.js.map +0 -1
  304. package/dist/primitives/fsx/core/glob/match.js +0 -392
  305. package/dist/primitives/fsx/core/glob/match.js.map +0 -1
  306. package/dist/primitives/fsx/core/types.js +0 -307
  307. package/dist/primitives/fsx/core/types.js.map +0 -1
  308. package/dist/sdk/capnweb-compat.js +0 -42
  309. package/dist/sdk/capnweb-compat.js.map +0 -1
  310. package/dist/sdk/client.js +0 -20
  311. package/dist/sdk/client.js.map +0 -1
  312. package/dist/sdk/index.js +0 -17
  313. package/dist/sdk/index.js.map +0 -1
@@ -0,0 +1,790 @@
1
+ /**
2
+ * MCP stdio Transport Module
3
+ *
4
+ * Implements MCP (Model Context Protocol) communication over stdin/stdout
5
+ * using JSON-RPC 2.0 with newline-delimited message framing.
6
+ *
7
+ * @see https://modelcontextprotocol.io/docs/concepts/transports
8
+ * @see https://www.jsonrpc.org/specification
9
+ */
10
+
11
+ import { EventEmitter } from 'events'
12
+
13
+ // ============================================================================
14
+ // Type Exports
15
+ // ============================================================================
16
+
17
+ export interface JsonRpcMessage {
18
+ jsonrpc: '2.0'
19
+ id?: string | number | null
20
+ method?: string
21
+ params?: Record<string, unknown>
22
+ result?: unknown
23
+ error?: {
24
+ code: number
25
+ message: string
26
+ data?: unknown
27
+ }
28
+ }
29
+
30
+ export interface JsonRpcRequest extends JsonRpcMessage {
31
+ id: string | number
32
+ method: string
33
+ params?: Record<string, unknown>
34
+ }
35
+
36
+ export interface JsonRpcResponse extends JsonRpcMessage {
37
+ id: string | number | null
38
+ result?: unknown
39
+ error?: {
40
+ code: number
41
+ message: string
42
+ data?: unknown
43
+ }
44
+ }
45
+
46
+ export interface JsonRpcNotification extends JsonRpcMessage {
47
+ method: string
48
+ params?: Record<string, unknown>
49
+ }
50
+
51
+ export interface TransportOptions {
52
+ stdin: NodeJS.ReadableStream | { on: Function; pipe: Function; destroy: Function }
53
+ stdout: NodeJS.WritableStream | { write: Function; end: Function; destroy: Function }
54
+ stderr?: NodeJS.WritableStream | { write: Function; end: Function; destroy: Function }
55
+ delimiter?: string
56
+ encoding?: BufferEncoding
57
+ maxMessageSize?: number
58
+ }
59
+
60
+ export interface MessageHandler {
61
+ (message: JsonRpcMessage, send: (response: JsonRpcMessage) => Promise<void>): Promise<void> | void
62
+ }
63
+
64
+ export interface StdioSession {
65
+ id: string
66
+ createdAt: number
67
+ lastActivity: number
68
+ state: 'created' | 'initialized' | 'running' | 'closed'
69
+ clientInfo?: { name: string; version: string }
70
+ transport?: McpStdioTransport
71
+ inactivityTimeout?: number
72
+
73
+ initialize(params: {
74
+ protocolVersion: string
75
+ clientInfo: { name: string; version: string }
76
+ capabilities: Record<string, unknown>
77
+ }): void
78
+ start(): void
79
+ close(): void
80
+ touch(): void
81
+ attachTransport(transport: McpStdioTransport): void
82
+ respond(id: string | number, result: unknown): Promise<void>
83
+ notify(method: string, params?: Record<string, unknown>): Promise<void>
84
+ registerTool(tool: McpTool): void
85
+ unregisterTool(name: string): void
86
+ getTools(): McpTool[]
87
+ on(event: string, callback: (...args: unknown[]) => void): void
88
+ startTimeoutTimer(): void
89
+ }
90
+
91
+ export interface McpTool {
92
+ name: string
93
+ description: string
94
+ inputSchema: Record<string, unknown>
95
+ }
96
+
97
+ export interface MessageFramer {
98
+ push(data: string): void
99
+ getMessages(): string[]
100
+ reset(): void
101
+ bufferSize(): number
102
+ }
103
+
104
+ export interface MessageFramerOptions {
105
+ delimiter?: string
106
+ maxBufferSize?: number
107
+ }
108
+
109
+ // ============================================================================
110
+ // MCP Bridge Types (for HTTP proxy)
111
+ // ============================================================================
112
+
113
+ export interface McpBridgeOptions {
114
+ targetUrl?: string
115
+ fetch?: typeof fetch
116
+ retries?: number
117
+ }
118
+
119
+ export interface McpBridge {
120
+ targetUrl: string
121
+ proxy(message: JsonRpcMessage): Promise<JsonRpcResponse>
122
+ }
123
+
124
+ export interface McpServerOptions {
125
+ stdin: NodeJS.ReadableStream | { on: Function; pipe: Function; destroy: Function }
126
+ stdout: NodeJS.WritableStream | { write: Function; end: Function; destroy: Function }
127
+ fetch?: typeof fetch
128
+ targetUrl?: string
129
+ }
130
+
131
+ export interface McpServer {
132
+ isRunning(): boolean
133
+ stop(): Promise<void>
134
+ }
135
+
136
+ // ============================================================================
137
+ // JSON-RPC Message Parsing
138
+ // ============================================================================
139
+
140
+ /**
141
+ * Parse a JSON-RPC message from string
142
+ */
143
+ export function parseJsonRpcMessage(input: string): JsonRpcMessage {
144
+ let parsed: Record<string, unknown>
145
+ try {
146
+ parsed = JSON.parse(input)
147
+ } catch {
148
+ throw new Error('Failed to parse JSON')
149
+ }
150
+
151
+ // Validate jsonrpc version
152
+ if (parsed.jsonrpc !== '2.0') {
153
+ throw new Error('Invalid or missing jsonrpc version')
154
+ }
155
+
156
+ // Validate id type if present
157
+ if (parsed.id !== undefined && parsed.id !== null) {
158
+ if (typeof parsed.id !== 'string' && typeof parsed.id !== 'number') {
159
+ throw new Error('Invalid id type: must be string, number, or null')
160
+ }
161
+ if (typeof parsed.id === 'object') {
162
+ throw new Error('Invalid id type: must be string, number, or null')
163
+ }
164
+ }
165
+
166
+ // Validate params if present (MCP requires object, not array)
167
+ if (parsed.params !== undefined) {
168
+ if (Array.isArray(parsed.params)) {
169
+ throw new Error('MCP requires params to be an object, not an array')
170
+ }
171
+ }
172
+
173
+ return parsed as JsonRpcMessage
174
+ }
175
+
176
+ /**
177
+ * Serialize a JSON-RPC message to string
178
+ */
179
+ export function serializeJsonRpcMessage(message: JsonRpcMessage): string {
180
+ return JSON.stringify(message)
181
+ }
182
+
183
+ // ============================================================================
184
+ // Message Framing
185
+ // ============================================================================
186
+
187
+ /**
188
+ * Create a message framer for handling newline-delimited JSON
189
+ */
190
+ export function createMessageFramer(options?: MessageFramerOptions): MessageFramer {
191
+ const delimiter = options?.delimiter ?? '\n'
192
+ const maxBufferSize = options?.maxBufferSize ?? Infinity
193
+ let buffer = ''
194
+
195
+ return {
196
+ push(data: string): void {
197
+ if (buffer.length + data.length > maxBufferSize) {
198
+ throw new Error('Buffer size limit exceeded')
199
+ }
200
+ buffer += data
201
+ },
202
+
203
+ getMessages(): string[] {
204
+ const messages: string[] = []
205
+ let delimiterIndex: number
206
+
207
+ while ((delimiterIndex = buffer.indexOf(delimiter)) !== -1) {
208
+ const message = buffer.slice(0, delimiterIndex)
209
+ buffer = buffer.slice(delimiterIndex + delimiter.length)
210
+ if (message.length > 0) {
211
+ messages.push(message)
212
+ }
213
+ }
214
+
215
+ return messages
216
+ },
217
+
218
+ reset(): void {
219
+ buffer = ''
220
+ },
221
+
222
+ bufferSize(): number {
223
+ return buffer.length
224
+ },
225
+ }
226
+ }
227
+
228
+ // ============================================================================
229
+ // McpStdioTransport Class
230
+ // ============================================================================
231
+
232
+ export class McpStdioTransport extends EventEmitter {
233
+ private options: TransportOptions
234
+ private connected: boolean = false
235
+ private messageHandler?: MessageHandler
236
+ private framer: MessageFramer
237
+ private writeQueue: Promise<void> = Promise.resolve()
238
+
239
+ constructor(options: TransportOptions) {
240
+ super()
241
+ this.options = options
242
+ this.framer = createMessageFramer({
243
+ delimiter: options.delimiter ?? '\n',
244
+ maxBufferSize: options.maxMessageSize,
245
+ })
246
+ }
247
+
248
+ isConnected(): boolean {
249
+ return this.connected
250
+ }
251
+
252
+ async start(): Promise<void> {
253
+ if (this.connected) {
254
+ throw new Error('Transport is already connected')
255
+ }
256
+
257
+ this.connected = true
258
+ this.emit('connected')
259
+
260
+ // Listen for data from stdin
261
+ this.options.stdin.on('data', (data: Buffer | string) => {
262
+ this.handleIncomingData(data)
263
+ })
264
+
265
+ // Handle stdin close
266
+ this.options.stdin.on('close', () => {
267
+ this.handleDisconnect()
268
+ })
269
+
270
+ // Handle stdin error
271
+ this.options.stdin.on('error', (error: Error) => {
272
+ this.emit('error', error)
273
+ })
274
+ }
275
+
276
+ private handleIncomingData(data: Buffer | string): void {
277
+ const str = typeof data === 'string' ? data : data.toString(this.options.encoding ?? 'utf-8')
278
+
279
+ try {
280
+ this.framer.push(str)
281
+ } catch {
282
+ this.emit('error', new Error('Message size limit exceeded'))
283
+ return
284
+ }
285
+
286
+ const messages = this.framer.getMessages()
287
+ for (const raw of messages) {
288
+ this.processMessage(raw)
289
+ }
290
+ }
291
+
292
+ private processMessage(raw: string): void {
293
+ // Handle empty messages gracefully
294
+ if (!raw.trim()) {
295
+ return
296
+ }
297
+
298
+ let message: JsonRpcMessage
299
+ try {
300
+ message = parseJsonRpcMessage(raw)
301
+ } catch (error) {
302
+ const parseError = error as Error
303
+ // Only emit error if there are listeners (avoid unhandled error events)
304
+ if (this.listenerCount('error') > 0) {
305
+ this.emit('error', new Error(`JSON parse error: ${parseError.message}`))
306
+ }
307
+ // Send parse error response
308
+ this.sendErrorResponse(null, -32700, 'Parse error')
309
+ return
310
+ }
311
+
312
+ // Validate that it's a request (has method) or response (has result/error)
313
+ const isRequest = 'method' in message
314
+ const isResponse = 'result' in message || 'error' in message
315
+
316
+ if (!isRequest && !isResponse && message.id !== undefined) {
317
+ // Has id but no method and no result/error - invalid request
318
+ // Only emit error if there are listeners (avoid unhandled error events)
319
+ if (this.listenerCount('error') > 0) {
320
+ this.emit('error', new Error('Invalid request: missing method'))
321
+ }
322
+ this.sendErrorResponse(message.id as string | number | null, -32600, 'Invalid Request')
323
+ return
324
+ }
325
+
326
+ // Emit message event
327
+ this.emit('message', message)
328
+
329
+ // Call message handler if registered
330
+ if (this.messageHandler) {
331
+ const sendFn = (response: JsonRpcMessage) => this.send(response)
332
+ Promise.resolve(this.messageHandler(message, sendFn)).catch((err) => {
333
+ if (this.listenerCount('error') > 0) {
334
+ this.emit('error', err)
335
+ }
336
+ })
337
+ }
338
+ }
339
+
340
+ private async sendErrorResponse(id: string | number | null, code: number, message: string): Promise<void> {
341
+ try {
342
+ await this.send({
343
+ jsonrpc: '2.0',
344
+ id,
345
+ error: { code, message },
346
+ })
347
+ } catch {
348
+ // Ignore errors when sending error response
349
+ }
350
+ }
351
+
352
+ private handleDisconnect(): void {
353
+ if (this.connected) {
354
+ this.connected = false
355
+ this.emit('disconnected')
356
+ }
357
+ }
358
+
359
+ async close(): Promise<void> {
360
+ if (this.connected) {
361
+ this.connected = false
362
+ this.emit('disconnected')
363
+ }
364
+ }
365
+
366
+ async send(message: JsonRpcMessage): Promise<void> {
367
+ if (!this.connected) {
368
+ throw new Error('Transport is closed')
369
+ }
370
+
371
+ const serialized = serializeJsonRpcMessage(message) + (this.options.delimiter ?? '\n')
372
+
373
+ // Queue writes to preserve order
374
+ this.writeQueue = this.writeQueue.then(() => {
375
+ return new Promise<void>((resolve) => {
376
+ try {
377
+ const stdout = this.options.stdout as { write: Function }
378
+ const result = stdout.write(serialized, () => {
379
+ resolve()
380
+ })
381
+ // If write returns false (backpressure), resolve immediately anyway
382
+ // The callback will still fire later, but we don't wait for it
383
+ if (result === false) {
384
+ resolve()
385
+ }
386
+ } catch (error) {
387
+ this.emit('error', error)
388
+ resolve()
389
+ }
390
+ })
391
+ })
392
+
393
+ await this.writeQueue
394
+ }
395
+
396
+ setMessageHandler(handler: MessageHandler): void {
397
+ this.messageHandler = handler
398
+ }
399
+ }
400
+
401
+ // ============================================================================
402
+ // Factory Functions
403
+ // ============================================================================
404
+
405
+ /**
406
+ * Create a new MCP stdio transport
407
+ */
408
+ export function createStdioTransport(options: TransportOptions): McpStdioTransport {
409
+ return new McpStdioTransport(options)
410
+ }
411
+
412
+ // ============================================================================
413
+ // Session Management
414
+ // ============================================================================
415
+
416
+ /**
417
+ * Create a new MCP stdio session
418
+ */
419
+ export function createStdioSession(options?: { inactivityTimeout?: number }): StdioSession {
420
+ const emitter = new EventEmitter()
421
+ let state: 'created' | 'initialized' | 'running' | 'closed' = 'created'
422
+ let clientInfo: { name: string; version: string } | undefined
423
+ let transport: McpStdioTransport | undefined
424
+ let lastActivity = Date.now()
425
+ let timeoutTimer: ReturnType<typeof setTimeout> | undefined
426
+ const tools: Map<string, McpTool> = new Map()
427
+
428
+ const session: StdioSession = {
429
+ id: generateSessionId(),
430
+ createdAt: Date.now(),
431
+ get lastActivity() {
432
+ return lastActivity
433
+ },
434
+ get state() {
435
+ return state
436
+ },
437
+ get clientInfo() {
438
+ return clientInfo
439
+ },
440
+ get transport() {
441
+ return transport
442
+ },
443
+ get inactivityTimeout() {
444
+ return options?.inactivityTimeout
445
+ },
446
+
447
+ initialize(params: {
448
+ protocolVersion: string
449
+ clientInfo: { name: string; version: string }
450
+ capabilities: Record<string, unknown>
451
+ }): void {
452
+ if (state === 'closed') {
453
+ throw new Error('Cannot initialize closed session')
454
+ }
455
+ clientInfo = params.clientInfo
456
+ state = 'initialized'
457
+ this.touch()
458
+ },
459
+
460
+ start(): void {
461
+ if (state === 'initialized') {
462
+ state = 'running'
463
+ this.touch()
464
+ }
465
+ },
466
+
467
+ close(): void {
468
+ if (timeoutTimer) {
469
+ clearTimeout(timeoutTimer)
470
+ }
471
+ state = 'closed'
472
+ if (transport) {
473
+ transport.close()
474
+ }
475
+ },
476
+
477
+ touch(): void {
478
+ lastActivity = Date.now()
479
+ if (timeoutTimer && options?.inactivityTimeout) {
480
+ clearTimeout(timeoutTimer)
481
+ timeoutTimer = setTimeout(() => {
482
+ emitter.emit('timeout')
483
+ }, options.inactivityTimeout)
484
+ }
485
+ },
486
+
487
+ attachTransport(t: McpStdioTransport): void {
488
+ transport = t
489
+
490
+ // Listen for messages and handle MCP protocol
491
+ transport.on('message', async (msg: JsonRpcMessage) => {
492
+ this.touch()
493
+
494
+ // Handle initialize request
495
+ if (msg.method === 'initialize' && msg.params && msg.id !== undefined) {
496
+ this.initialize(msg.params as {
497
+ protocolVersion: string
498
+ clientInfo: { name: string; version: string }
499
+ capabilities: Record<string, unknown>
500
+ })
501
+ // Send initialize response
502
+ await this.respond(msg.id as string | number, {
503
+ protocolVersion: '2024-11-05',
504
+ serverInfo: { name: 'dotdo', version: '1.0.0' },
505
+ capabilities: { tools: {}, resources: {} },
506
+ })
507
+ return
508
+ }
509
+
510
+ // Handle tools/list request
511
+ if (msg.method === 'tools/list' && msg.id !== undefined) {
512
+ await this.respond(msg.id as string | number, {
513
+ tools: this.getTools(),
514
+ })
515
+ return
516
+ }
517
+
518
+ // Handle tools/call request - echo the tool name for now
519
+ if (msg.method === 'tools/call' && msg.id !== undefined && msg.params) {
520
+ const toolName = (msg.params as { name: string }).name
521
+ const args = (msg.params as { arguments?: Record<string, unknown> }).arguments ?? {}
522
+ const tool = tools.get(toolName)
523
+ if (tool) {
524
+ // Simple echo implementation for testing
525
+ await this.respond(msg.id as string | number, {
526
+ content: [{ type: 'text', text: `Tool ${toolName} called with: ${JSON.stringify(args)}` }],
527
+ })
528
+ } else {
529
+ await transport.send({
530
+ jsonrpc: '2.0',
531
+ id: msg.id,
532
+ error: { code: -32601, message: `Tool not found: ${toolName}` },
533
+ })
534
+ }
535
+ return
536
+ }
537
+ })
538
+ },
539
+
540
+ async respond(id: string | number, result: unknown): Promise<void> {
541
+ if (transport) {
542
+ await transport.send({
543
+ jsonrpc: '2.0',
544
+ id,
545
+ result,
546
+ })
547
+ }
548
+ },
549
+
550
+ async notify(method: string, params?: Record<string, unknown>): Promise<void> {
551
+ if (transport) {
552
+ await transport.send({
553
+ jsonrpc: '2.0',
554
+ method,
555
+ params,
556
+ })
557
+ }
558
+ },
559
+
560
+ registerTool(tool: McpTool): void {
561
+ tools.set(tool.name, tool)
562
+ // Send notification if initialized
563
+ if (state === 'initialized' || state === 'running') {
564
+ this.notify('notifications/tools/list_changed', {})
565
+ }
566
+ },
567
+
568
+ unregisterTool(name: string): void {
569
+ tools.delete(name)
570
+ // Send notification if initialized
571
+ if (state === 'initialized' || state === 'running') {
572
+ this.notify('notifications/tools/list_changed', {})
573
+ }
574
+ },
575
+
576
+ getTools(): McpTool[] {
577
+ return Array.from(tools.values())
578
+ },
579
+
580
+ on(event: string, callback: (...args: unknown[]) => void): void {
581
+ emitter.on(event, callback)
582
+ },
583
+
584
+ startTimeoutTimer(): void {
585
+ if (options?.inactivityTimeout) {
586
+ timeoutTimer = setTimeout(() => {
587
+ emitter.emit('timeout')
588
+ }, options.inactivityTimeout)
589
+ }
590
+ },
591
+ }
592
+
593
+ return session
594
+ }
595
+
596
+ function generateSessionId(): string {
597
+ return `session-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`
598
+ }
599
+
600
+ // ============================================================================
601
+ // Helper Functions
602
+ // ============================================================================
603
+
604
+ /**
605
+ * Normalize a URL by removing trailing slashes and /mcp suffix
606
+ */
607
+ function normalizeTargetUrl(url: string): string {
608
+ let normalized = url
609
+ // Remove trailing slash
610
+ if (normalized.endsWith('/')) {
611
+ normalized = normalized.slice(0, -1)
612
+ }
613
+ // Remove /mcp suffix if present
614
+ if (normalized.endsWith('/mcp')) {
615
+ normalized = normalized.slice(0, -4)
616
+ }
617
+ return normalized
618
+ }
619
+
620
+ /**
621
+ * Validate URL format
622
+ */
623
+ function isValidUrl(url: string): boolean {
624
+ try {
625
+ new URL(url)
626
+ return true
627
+ } catch {
628
+ return false
629
+ }
630
+ }
631
+
632
+ // ============================================================================
633
+ // MCP HTTP Bridge Functions
634
+ // ============================================================================
635
+
636
+ /**
637
+ * Create a new MCP HTTP bridge that proxies stdio to DO's /mcp endpoint
638
+ */
639
+ export function createMcpBridge(options?: McpBridgeOptions): McpBridge {
640
+ const rawUrl = options?.targetUrl ?? process.env.DO_URL
641
+
642
+ // Throw if no URL configured
643
+ if (!rawUrl) {
644
+ throw new Error('DO_URL not configured: provide targetUrl option or set DO_URL environment variable')
645
+ }
646
+
647
+ // Validate URL format
648
+ if (!isValidUrl(rawUrl)) {
649
+ throw new Error('Invalid URL format')
650
+ }
651
+
652
+ const targetUrl = normalizeTargetUrl(rawUrl)
653
+ const fetchFn = options?.fetch ?? fetch
654
+ const maxRetries = options?.retries ?? 0
655
+
656
+ return {
657
+ targetUrl,
658
+ async proxy(message: JsonRpcMessage): Promise<JsonRpcResponse> {
659
+ let lastError: Error | undefined
660
+
661
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
662
+ try {
663
+ const res = await fetchFn(`${targetUrl}/mcp`, {
664
+ method: 'POST',
665
+ headers: { 'Content-Type': 'application/json' },
666
+ body: JSON.stringify(message),
667
+ })
668
+
669
+ // Handle HTTP errors
670
+ if (!res.ok) {
671
+ const status = res.status
672
+ const statusText = res.statusText || ''
673
+ let errorMessage: string
674
+
675
+ if (status === 401) {
676
+ errorMessage = `401 Unauthorized`
677
+ } else if (status === 404) {
678
+ errorMessage = `404 Not Found`
679
+ } else if (status >= 500) {
680
+ errorMessage = `${status} Internal Server Error`
681
+ } else {
682
+ errorMessage = `HTTP ${status}: ${statusText}`
683
+ }
684
+
685
+ return {
686
+ jsonrpc: '2.0',
687
+ id: message.id ?? null,
688
+ error: {
689
+ code: -32603,
690
+ message: errorMessage,
691
+ },
692
+ }
693
+ }
694
+
695
+ try {
696
+ return (await res.json()) as JsonRpcResponse
697
+ } catch {
698
+ // JSON parse error
699
+ return {
700
+ jsonrpc: '2.0',
701
+ id: message.id ?? null,
702
+ error: {
703
+ code: -32700,
704
+ message: 'Parse error: Invalid JSON response from server',
705
+ },
706
+ }
707
+ }
708
+ } catch (error) {
709
+ lastError = error as Error
710
+ // If we have more retries, continue
711
+ if (attempt < maxRetries) {
712
+ continue
713
+ }
714
+ }
715
+ }
716
+
717
+ // Return error response for network/connection errors
718
+ return {
719
+ jsonrpc: '2.0',
720
+ id: message.id ?? null,
721
+ error: {
722
+ code: -32603,
723
+ message: `Connection failed: ${lastError?.message ?? 'Unknown error'}`,
724
+ },
725
+ }
726
+ },
727
+ }
728
+ }
729
+
730
+ /**
731
+ * Start MCP stdio server that proxies to DO
732
+ */
733
+ export async function startMcpServer(options: McpServerOptions): Promise<McpServer> {
734
+ // Validate DO_URL is configured
735
+ const targetUrl = options.targetUrl ?? process.env.DO_URL
736
+ if (!targetUrl) {
737
+ throw new Error('DO_URL not configured: provide targetUrl option or set DO_URL environment variable')
738
+ }
739
+
740
+ const transport = createStdioTransport({
741
+ stdin: options.stdin,
742
+ stdout: options.stdout,
743
+ })
744
+
745
+ const bridge = createMcpBridge({
746
+ targetUrl,
747
+ fetch: options.fetch,
748
+ })
749
+
750
+ let running = true
751
+
752
+ transport.setMessageHandler(async (message, send) => {
753
+ const response = await bridge.proxy(message)
754
+ await send(response)
755
+ })
756
+
757
+ await transport.start()
758
+
759
+ return {
760
+ isRunning(): boolean {
761
+ return running
762
+ },
763
+ async stop(): Promise<void> {
764
+ running = false
765
+ await transport.close()
766
+ },
767
+ }
768
+ }
769
+
770
+ /**
771
+ * MCP command handler for CLI
772
+ */
773
+ export async function mcpCommand(args?: { url?: string }): Promise<void> {
774
+ const server = await startMcpServer({
775
+ stdin: process.stdin,
776
+ stdout: process.stdout,
777
+ targetUrl: args?.url ?? process.env.DO_URL,
778
+ })
779
+
780
+ // Handle process signals
781
+ process.on('SIGINT', async () => {
782
+ await server.stop()
783
+ process.exit(0)
784
+ })
785
+
786
+ process.on('SIGTERM', async () => {
787
+ await server.stop()
788
+ process.exit(0)
789
+ })
790
+ }