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,289 @@
1
+ /**
2
+ * CLI Device Authorization Module
3
+ *
4
+ * Uses oauth.do/node for OAuth 2.0 Device Authorization Grant (RFC 8628)
5
+ *
6
+ * @see https://oauth.do/node
7
+ * @see https://datatracker.ietf.org/doc/html/rfc8628
8
+ */
9
+
10
+ import { readFile, writeFile, unlink, mkdir, access } from 'fs/promises'
11
+ import { homedir } from 'os'
12
+ import { join } from 'path'
13
+
14
+ // ============================================================================
15
+ // Constants
16
+ // ============================================================================
17
+
18
+ const OAUTH_BASE_URL = 'https://oauth.do'
19
+ const DEFAULT_CLIENT_ID = 'org.ai-cli'
20
+ const CONFIG_DIR_NAME = '.org.ai'
21
+ const CREDENTIALS_FILE_NAME = 'credentials.json'
22
+
23
+ // ============================================================================
24
+ // Type Exports
25
+ // ============================================================================
26
+
27
+ export interface DeviceCodeResponse {
28
+ device_code: string
29
+ user_code: string
30
+ verification_uri: string
31
+ verification_uri_complete?: string
32
+ expires_in: number
33
+ interval: number
34
+ }
35
+
36
+ export interface TokenResponse {
37
+ access_token: string
38
+ token_type: string
39
+ expires_in?: number
40
+ refresh_token?: string
41
+ scope?: string
42
+ }
43
+
44
+ export interface PollError extends Error {
45
+ code: 'authorization_pending' | 'slow_down' | 'access_denied' | 'expired_token' | 'timeout'
46
+ }
47
+
48
+ export interface PollOptions {
49
+ interval: number
50
+ maxAttempts: number
51
+ onProgress?: (status: { attempt: number; maxAttempts: number }) => void
52
+ }
53
+
54
+ export interface RequestDeviceCodeOptions {
55
+ scope?: string
56
+ clientId?: string
57
+ }
58
+
59
+ export interface StoredToken {
60
+ access_token: string
61
+ token_type: string
62
+ refresh_token?: string
63
+ expires_at?: number
64
+ scope?: string
65
+ }
66
+
67
+ // ============================================================================
68
+ // Helper Functions
69
+ // ============================================================================
70
+
71
+ function createPollError(code: PollError['code'], message: string): PollError {
72
+ const error = new Error(message) as PollError
73
+ error.code = code
74
+ return error
75
+ }
76
+
77
+ function sleep(ms: number): Promise<void> {
78
+ return new Promise((resolve) => setTimeout(resolve, ms))
79
+ }
80
+
81
+ // ============================================================================
82
+ // Config Path
83
+ // ============================================================================
84
+
85
+ /**
86
+ * Get the config file path
87
+ */
88
+ export function getConfigPath(): string {
89
+ return join(homedir(), CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME)
90
+ }
91
+
92
+ /**
93
+ * Get the config directory path
94
+ */
95
+ function getConfigDir(): string {
96
+ return join(homedir(), CONFIG_DIR_NAME)
97
+ }
98
+
99
+ // ============================================================================
100
+ // Device Code Request
101
+ // ============================================================================
102
+
103
+ /**
104
+ * Request a device code from the OAuth server
105
+ */
106
+ export async function requestDeviceCode(options?: RequestDeviceCodeOptions): Promise<DeviceCodeResponse> {
107
+ const clientId = options?.clientId ?? DEFAULT_CLIENT_ID
108
+ const scope = options?.scope ?? 'openid profile email'
109
+
110
+ const body = new URLSearchParams({
111
+ client_id: clientId,
112
+ scope: scope,
113
+ })
114
+
115
+ const response = await fetch(`${OAUTH_BASE_URL}/device/code`, {
116
+ method: 'POST',
117
+ headers: {
118
+ 'Content-Type': 'application/x-www-form-urlencoded',
119
+ },
120
+ body: body.toString(),
121
+ })
122
+
123
+ if (!response.ok) {
124
+ const errorData = await response.json().catch(() => ({}))
125
+ const errorCode = errorData.error || 'unknown_error'
126
+ const errorDescription = errorData.error_description || `Request failed with status ${response.status}`
127
+ throw new Error(`${errorCode}: ${errorDescription}`)
128
+ }
129
+
130
+ return response.json()
131
+ }
132
+
133
+ // ============================================================================
134
+ // Token Polling
135
+ // ============================================================================
136
+
137
+ /**
138
+ * Poll the OAuth server for a token
139
+ */
140
+ export async function pollForToken(deviceCode: string, options: PollOptions): Promise<TokenResponse> {
141
+ const { maxAttempts, onProgress } = options
142
+ let interval = options.interval
143
+ let attempt = 0
144
+
145
+ while (attempt < maxAttempts) {
146
+ attempt++
147
+
148
+ // Call onProgress before attempting (for first attempt)
149
+ if (onProgress && attempt > 1) {
150
+ onProgress({ attempt: attempt - 1, maxAttempts })
151
+ }
152
+
153
+ const body = new URLSearchParams({
154
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
155
+ device_code: deviceCode,
156
+ client_id: DEFAULT_CLIENT_ID,
157
+ })
158
+
159
+ const response = await fetch(`${OAUTH_BASE_URL}/token`, {
160
+ method: 'POST',
161
+ headers: {
162
+ 'Content-Type': 'application/x-www-form-urlencoded',
163
+ },
164
+ body: body.toString(),
165
+ })
166
+
167
+ if (response.ok) {
168
+ return response.json()
169
+ }
170
+
171
+ const errorData = await response.json().catch(() => ({ error: 'unknown_error' }))
172
+ const errorCode = errorData.error
173
+
174
+ if (errorCode === 'authorization_pending') {
175
+ // Wait and continue polling
176
+ await sleep(interval * 1000)
177
+ continue
178
+ }
179
+
180
+ if (errorCode === 'slow_down') {
181
+ // Increase interval by 5 seconds and continue polling
182
+ interval += 5
183
+ await sleep(interval * 1000)
184
+ continue
185
+ }
186
+
187
+ if (errorCode === 'access_denied') {
188
+ throw createPollError('access_denied', 'access_denied: User denied access')
189
+ }
190
+
191
+ if (errorCode === 'expired_token') {
192
+ throw createPollError('expired_token', 'expired_token: Device code expired')
193
+ }
194
+
195
+ // Unknown error
196
+ throw new Error(errorData.error_description || errorCode || 'Token request failed')
197
+ }
198
+
199
+ // Max attempts reached
200
+ throw createPollError('timeout', 'timeout: Polling timed out waiting for authorization')
201
+ }
202
+
203
+ // ============================================================================
204
+ // Token Storage
205
+ // ============================================================================
206
+
207
+ /**
208
+ * Store a token to the local config file
209
+ */
210
+ export async function storeToken(token: TokenResponse): Promise<void> {
211
+ const configDir = getConfigDir()
212
+ const configPath = getConfigPath()
213
+
214
+ // Check if directory exists, create if not
215
+ try {
216
+ await access(configDir)
217
+ } catch {
218
+ await mkdir(configDir, { recursive: true, mode: 0o700 })
219
+ }
220
+
221
+ // Build stored token data
222
+ const storedToken: StoredToken = {
223
+ access_token: token.access_token,
224
+ token_type: token.token_type,
225
+ }
226
+
227
+ if (token.refresh_token) {
228
+ storedToken.refresh_token = token.refresh_token
229
+ }
230
+
231
+ if (token.expires_in) {
232
+ storedToken.expires_at = Date.now() + token.expires_in * 1000
233
+ }
234
+
235
+ if (token.scope) {
236
+ storedToken.scope = token.scope
237
+ }
238
+
239
+ // Write token file with secure permissions
240
+ await writeFile(configPath, JSON.stringify(storedToken, null, 2), { mode: 0o600 })
241
+ }
242
+
243
+ /**
244
+ * Get the stored token from the local config file
245
+ */
246
+ export async function getStoredToken(): Promise<StoredToken | null> {
247
+ const configPath = getConfigPath()
248
+
249
+ try {
250
+ const content = await readFile(configPath, 'utf-8')
251
+ const data = JSON.parse(content)
252
+
253
+ // Validate token structure
254
+ if (!data.access_token || !data.token_type) {
255
+ return null
256
+ }
257
+
258
+ // Check expiration (only if no refresh token available)
259
+ if (data.expires_at && data.expires_at < Date.now() && !data.refresh_token) {
260
+ return null
261
+ }
262
+
263
+ return data as StoredToken
264
+ } catch (error: unknown) {
265
+ // File doesn't exist or other error
266
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
267
+ return null
268
+ }
269
+ // JSON parse error or other issues
270
+ return null
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Clear the stored token
276
+ */
277
+ export async function clearToken(): Promise<boolean> {
278
+ const configPath = getConfigPath()
279
+
280
+ try {
281
+ await unlink(configPath)
282
+ return true
283
+ } catch (error: unknown) {
284
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
285
+ return false
286
+ }
287
+ throw error
288
+ }
289
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * AI Fallback Handler
3
+ *
4
+ * Handles unrecognized commands by passing them to AI for processing.
5
+ */
6
+
7
+ /**
8
+ * Process natural language input through AI
9
+ */
10
+ export async function fallback(_input: string[]): Promise<void> {
11
+ // Placeholder - AI integration will be implemented
12
+ }
package/cli/index.ts ADDED
@@ -0,0 +1,121 @@
1
+ /**
2
+ * CLI Router
3
+ *
4
+ * Routes CLI commands to appropriate handlers or AI fallback.
5
+ */
6
+
7
+ import { commands } from './commands/index'
8
+ import pkg from '../package.json'
9
+
10
+ export type RouteResult =
11
+ | { type: 'help' }
12
+ | { type: 'version' }
13
+ | { type: 'command'; name: string; args: string[] }
14
+ | { type: 'fallback'; input: string[] }
15
+
16
+ /** Known command names */
17
+ const knownCommands = [
18
+ // Auth commands
19
+ 'login',
20
+ 'logout',
21
+ // Dev commands
22
+ 'dev',
23
+ 'build',
24
+ 'deploy',
25
+ 'init',
26
+ 'introspect',
27
+ // Service commands (cli.do)
28
+ 'call',
29
+ 'text',
30
+ 'email',
31
+ 'charge',
32
+ 'queue',
33
+ 'llm',
34
+ 'config',
35
+ ]
36
+
37
+ /** Version from package.json */
38
+ export const version = pkg.version
39
+
40
+ /** Help text for CLI */
41
+ export const helpText = `
42
+ Usage: do [command] [options]
43
+
44
+ Service Commands (cli.do):
45
+ call Make voice calls via calls.do
46
+ do call +15551234567 "Your appointment is tomorrow"
47
+
48
+ text Send SMS/MMS via texts.do
49
+ do text +15551234567 "Reply YES to confirm"
50
+
51
+ email Send emails via emails.do
52
+ do email user@example.com --template=welcome
53
+
54
+ charge Create charges via payments.do
55
+ do charge cus_123 --amount=9900
56
+
57
+ queue Queue operations via queue.do
58
+ do queue publish my-queue '{"event": "user.signup"}'
59
+
60
+ llm LLM requests via llm.do (with streaming)
61
+ do llm "Summarize this document" --model=claude-sonnet
62
+
63
+ config Manage CLI configuration
64
+ do config set json_output true
65
+
66
+ Auth Commands:
67
+ login Log in to your account (id.org.ai OAuth)
68
+ logout Log out of your account
69
+
70
+ Dev Commands:
71
+ dev Start development server
72
+ build Build the project
73
+ deploy Deploy to production
74
+ init Initialize a new project
75
+ introspect Generate .do/types.d.ts from DB.mdx schemas
76
+
77
+ Options:
78
+ -h, --help Show help
79
+ -v, --version Show version
80
+ --json Output JSON (for service commands)
81
+
82
+ Any unrecognized commands will be passed to the AI fallback for natural language processing.
83
+ `
84
+
85
+ /**
86
+ * Parse process.argv to extract CLI arguments
87
+ * Strips the runtime and script path
88
+ */
89
+ export function parseArgv(argv: string[]): string[] {
90
+ return argv.slice(2)
91
+ }
92
+
93
+ /**
94
+ * Route CLI input to the appropriate handler
95
+ */
96
+ export function route(argv: string[]): RouteResult {
97
+ const [command, ...args] = argv
98
+
99
+ // No command or help flag
100
+ if (!command || command === '--help' || command === '-h') {
101
+ return { type: 'help' }
102
+ }
103
+
104
+ // Version flag
105
+ if (command === '--version' || command === '-v') {
106
+ return { type: 'version' }
107
+ }
108
+
109
+ // help command with no additional args
110
+ if (command === 'help' && args.length === 0) {
111
+ return { type: 'help' }
112
+ }
113
+
114
+ // Known commands
115
+ if (knownCommands.includes(command)) {
116
+ return { type: 'command', name: command, args }
117
+ }
118
+
119
+ // AI fallback for unknown commands
120
+ return { type: 'fallback', input: argv }
121
+ }
package/cli/main.ts ADDED
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * dotdo CLI Entry Point
4
+ *
5
+ * Self-contained CLI for Durable Objects development.
6
+ * Includes embedded runtime with full DO support for local development.
7
+ *
8
+ * Usage:
9
+ * dotdo dev - Start local development server
10
+ * dotdo do:list - List Durable Objects
11
+ * dotdo deploy - Deploy to production
12
+ * dotdo tunnel - Expose local server via CF Tunnel
13
+ */
14
+
15
+ import { Command } from 'commander'
16
+ import { devCommand } from './commands/dev-local'
17
+ import { doCommand } from './commands/do-ops'
18
+ import { tunnelCommand } from './commands/tunnel'
19
+ import { deployCommand } from './commands/deploy-multi'
20
+ import { createLogger } from './utils/logger'
21
+
22
+ const logger = createLogger('cli')
23
+
24
+ // Package info
25
+ const pkg = {
26
+ name: 'dotdo',
27
+ version: '0.1.0',
28
+ description: 'Self-contained CLI for Durable Objects development',
29
+ }
30
+
31
+ const program = new Command()
32
+ .name('dotdo')
33
+ .description(pkg.description)
34
+ .version(pkg.version, '-v, --version', 'Show version number')
35
+ .option('--debug', 'Enable debug output')
36
+
37
+ // Handle debug flag at parse time
38
+ const originalParse = program.parse.bind(program)
39
+ program.parse = function (argv?: readonly string[], options?: { from: 'node' | 'electron' | 'user' }) {
40
+ // Pre-parse to check for --debug flag
41
+ const args = argv ?? process.argv
42
+ if (args.includes('--debug')) {
43
+ process.env.DEBUG = '1'
44
+ }
45
+ return originalParse(argv, options)
46
+ }
47
+
48
+ // Add commands
49
+ program.addCommand(devCommand)
50
+ program.addCommand(doCommand)
51
+ program.addCommand(tunnelCommand)
52
+ program.addCommand(deployCommand)
53
+
54
+ // Init command
55
+ program
56
+ .command('init')
57
+ .description('Initialize a new dotdo project')
58
+ .option('-t, --template <template>', 'Project template', 'default')
59
+ .option('--no-git', 'Skip git initialization')
60
+ .action(async (options) => {
61
+ logger.info('Initializing new dotdo project...')
62
+
63
+ const fs = await import('fs')
64
+ const path = await import('path')
65
+
66
+ const cwd = process.cwd()
67
+
68
+ // Create basic structure
69
+ const dirs = ['.dotdo', 'objects', 'api']
70
+ for (const dir of dirs) {
71
+ const dirPath = path.join(cwd, dir)
72
+ if (!fs.existsSync(dirPath)) {
73
+ fs.mkdirSync(dirPath, { recursive: true })
74
+ logger.debug(`Created: ${dir}/`)
75
+ }
76
+ }
77
+
78
+ // Create dotdo.config.ts
79
+ const configPath = path.join(cwd, 'dotdo.config.ts')
80
+ if (!fs.existsSync(configPath)) {
81
+ const configContent = `/**
82
+ * dotdo configuration
83
+ */
84
+
85
+ import type { DotdoConfig } from 'dotdo/cli'
86
+
87
+ export default {
88
+ port: 8787,
89
+ entryPoint: 'index.ts',
90
+ compatibilityDate: '2024-01-01',
91
+ compatibilityFlags: ['nodejs_compat'],
92
+ } satisfies DotdoConfig
93
+ `
94
+ fs.writeFileSync(configPath, configContent)
95
+ logger.debug('Created: dotdo.config.ts')
96
+ }
97
+
98
+ // Create example DO
99
+ const examplePath = path.join(cwd, 'objects', 'Counter.ts')
100
+ if (!fs.existsSync(examplePath)) {
101
+ const exampleContent = `/**
102
+ * Example Durable Object: Counter
103
+ */
104
+
105
+ export class Counter extends DurableObject {
106
+ private count: number = 0
107
+
108
+ constructor(ctx: DurableObjectState, env: Env) {
109
+ super(ctx, env)
110
+ }
111
+
112
+ async fetch(request: Request): Promise<Response> {
113
+ const url = new URL(request.url)
114
+
115
+ if (url.pathname === '/increment') {
116
+ this.count++
117
+ await this.ctx.storage.put('count', this.count)
118
+ return new Response(JSON.stringify({ count: this.count }))
119
+ }
120
+
121
+ if (url.pathname === '/decrement') {
122
+ this.count--
123
+ await this.ctx.storage.put('count', this.count)
124
+ return new Response(JSON.stringify({ count: this.count }))
125
+ }
126
+
127
+ return new Response(JSON.stringify({ count: this.count }))
128
+ }
129
+ }
130
+ `
131
+ fs.writeFileSync(examplePath, exampleContent)
132
+ logger.debug('Created: objects/Counter.ts')
133
+ }
134
+
135
+ // Create index.ts
136
+ const indexPath = path.join(cwd, 'index.ts')
137
+ if (!fs.existsSync(indexPath)) {
138
+ const indexContent = `/**
139
+ * Worker entry point
140
+ */
141
+
142
+ import { Counter } from './objects/Counter'
143
+
144
+ export { Counter }
145
+
146
+ export default {
147
+ async fetch(request: Request, env: Env): Promise<Response> {
148
+ const url = new URL(request.url)
149
+
150
+ // Route to Counter DO
151
+ if (url.pathname.startsWith('/counter')) {
152
+ const id = env.COUNTER.idFromName('default')
153
+ const counter = env.COUNTER.get(id)
154
+ return counter.fetch(request)
155
+ }
156
+
157
+ return new Response('Hello from dotdo!')
158
+ },
159
+ }
160
+
161
+ interface Env {
162
+ COUNTER: DurableObjectNamespace
163
+ }
164
+ `
165
+ fs.writeFileSync(indexPath, indexContent)
166
+ logger.debug('Created: index.ts')
167
+ }
168
+
169
+ // Initialize git
170
+ if (options.git) {
171
+ const { execSync } = await import('child_process')
172
+ try {
173
+ if (!fs.existsSync(path.join(cwd, '.git'))) {
174
+ execSync('git init', { cwd, stdio: 'ignore' })
175
+ logger.debug('Initialized git repository')
176
+ }
177
+ } catch {
178
+ // Git not available
179
+ }
180
+ }
181
+
182
+ console.log()
183
+ logger.success('Project initialized!')
184
+ console.log()
185
+ console.log(' Next steps:')
186
+ console.log(' 1. Run: dotdo dev')
187
+ console.log(' 2. Visit: http://localhost:8787')
188
+ console.log(' 3. Try: http://localhost:8787/counter/increment')
189
+ console.log()
190
+ })
191
+
192
+ // Logs command
193
+ program
194
+ .command('logs')
195
+ .description('Stream logs from deployed workers')
196
+ .option('-f, --follow', 'Follow log output', true)
197
+ .option('--format <format>', 'Output format (json, pretty)', 'pretty')
198
+ .action(async (options) => {
199
+ const { spawn } = await import('child_process')
200
+
201
+ const args = ['wrangler', 'tail']
202
+ if (options.format === 'json') {
203
+ args.push('--format', 'json')
204
+ }
205
+
206
+ const proc = spawn('bunx', args, {
207
+ stdio: 'inherit',
208
+ })
209
+
210
+ proc.on('error', (error) => {
211
+ logger.error('Failed to start wrangler tail', { error: error.message })
212
+ process.exit(1)
213
+ })
214
+ })
215
+
216
+ // Build command
217
+ program
218
+ .command('build')
219
+ .description('Build the project')
220
+ .option('-w, --watch', 'Watch for changes')
221
+ .action(async (options) => {
222
+ const { spawn } = await import('child_process')
223
+
224
+ logger.info('Building project...')
225
+
226
+ const args = ['tsc', '-p', 'tsconfig.json']
227
+ if (options.watch) {
228
+ args.push('--watch')
229
+ }
230
+
231
+ const proc = spawn('bunx', args, {
232
+ stdio: 'inherit',
233
+ })
234
+
235
+ proc.on('exit', (code) => {
236
+ if (code === 0) {
237
+ logger.success('Build complete')
238
+ } else {
239
+ logger.error('Build failed')
240
+ process.exit(code ?? 1)
241
+ }
242
+ })
243
+ })
244
+
245
+ // Parse arguments
246
+ program.parse()