t3code-cli 0.10.0 → 0.12.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 (328) hide show
  1. package/README.md +75 -11
  2. package/dist/application.js +2 -2
  3. package/dist/auth.js +1 -1
  4. package/dist/bin.js +86754 -2675
  5. package/dist/cli.js +15 -2
  6. package/dist/config.js +120 -2
  7. package/dist/connection.js +2 -12
  8. package/dist/index.js +1 -1
  9. package/dist/node.js +6 -0
  10. package/dist/orchestration.js +2 -2
  11. package/dist/rpc.js +2 -2
  12. package/dist/runtime.js +2 -2
  13. package/dist/shared.js +43850 -38040
  14. package/dist/src/application/actions.d.ts +25 -0
  15. package/dist/src/application/index.d.ts +3 -2
  16. package/dist/src/application/layer.d.ts +99 -842
  17. package/dist/src/application/model-selection.d.ts +17 -2
  18. package/dist/src/application/models.d.ts +1 -87
  19. package/dist/src/application/project-commands.d.ts +31 -2
  20. package/dist/src/application/projects.d.ts +15 -137
  21. package/dist/src/application/service.d.ts +147 -5
  22. package/dist/src/application/shell-sequence.d.ts +5 -4
  23. package/dist/src/application/terminals.d.ts +66 -0
  24. package/dist/src/application/thread-commands.d.ts +48 -1
  25. package/dist/src/application/thread-update.d.ts +8 -0
  26. package/dist/src/application/thread-wait.d.ts +4 -6
  27. package/dist/src/application/threads.d.ts +36 -14
  28. package/dist/src/auth/error.d.ts +2 -1
  29. package/dist/src/auth/index.d.ts +1 -1
  30. package/dist/src/auth/layer.d.ts +34 -6
  31. package/dist/src/auth/local-base-dir.d.ts +5 -2
  32. package/dist/src/auth/local-origin.d.ts +3 -3
  33. package/dist/src/auth/local-token.d.ts +3 -3
  34. package/dist/src/auth/service.d.ts +15 -3
  35. package/dist/src/auth/type.d.ts +40 -1
  36. package/dist/src/cli/env/flag.d.ts +2 -0
  37. package/dist/src/cli/env/index.d.ts +2 -0
  38. package/dist/src/cli/env/selection-layer.d.ts +3 -0
  39. package/dist/src/cli/flags.d.ts +5 -0
  40. package/dist/src/cli/format/index.d.ts +1 -0
  41. package/dist/src/cli/format/output.d.ts +12 -0
  42. package/dist/src/cli/index.d.ts +4 -0
  43. package/dist/src/cli/runtime/index.d.ts +1 -0
  44. package/dist/src/cli/runtime/service.d.ts +16 -0
  45. package/dist/src/{scope → cli/scope}/resolve.d.ts +5 -4
  46. package/dist/src/config/config.d.ts +49 -0
  47. package/dist/src/config/credential/cipher-node.d.ts +3 -0
  48. package/dist/src/config/credential/cipher-web.d.ts +3 -0
  49. package/dist/src/config/credential/cipher.d.ts +29 -0
  50. package/dist/src/config/credential/env.d.ts +3 -0
  51. package/dist/src/config/credential/error.d.ts +8 -0
  52. package/dist/src/config/credential/index.d.ts +6 -0
  53. package/dist/src/config/credential/service.d.ts +34 -0
  54. package/dist/src/config/env/env.d.ts +55 -0
  55. package/dist/src/config/env/index.d.ts +2 -0
  56. package/dist/src/{layout/base-dir.d.ts → config/env/layout.d.ts} +0 -1
  57. package/dist/src/config/environment-name/index.d.ts +1 -0
  58. package/dist/src/config/environment-name/name.d.ts +10 -0
  59. package/dist/src/config/error.d.ts +4 -11
  60. package/dist/src/config/index.d.ts +10 -5
  61. package/dist/src/config/keystore/error.d.ts +8 -0
  62. package/dist/src/config/keystore/file.d.ts +9 -0
  63. package/dist/src/config/keystore/index.d.ts +4 -0
  64. package/dist/src/config/keystore/keyring-node.d.ts +4 -0
  65. package/dist/src/config/keystore/service.d.ts +33 -0
  66. package/dist/src/config/paths/index.d.ts +1 -0
  67. package/dist/src/config/paths/paths.d.ts +6 -0
  68. package/dist/src/config/persist/file-mode.d.ts +4 -0
  69. package/dist/src/config/persist/migration.d.ts +68 -0
  70. package/dist/src/config/persist/persist.d.ts +40 -0
  71. package/dist/src/config/persist/schema.d.ts +68 -0
  72. package/dist/src/config/resolve/resolve.d.ts +40 -0
  73. package/dist/src/config/selection/index.d.ts +1 -0
  74. package/dist/src/config/selection/resolve.d.ts +4 -0
  75. package/dist/src/config/selection/service.d.ts +10 -0
  76. package/dist/src/config/types.d.ts +36 -0
  77. package/dist/src/config/url/error.d.ts +10 -0
  78. package/dist/src/config/url/index.d.ts +2 -0
  79. package/dist/src/connection/index.d.ts +0 -1
  80. package/dist/src/contracts/index.d.ts +1 -1
  81. package/dist/src/domain/error.d.ts +27 -1
  82. package/dist/src/domain/helpers.d.ts +8 -2
  83. package/dist/src/domain/model-config.d.ts +30 -1
  84. package/dist/src/domain/thread-activities.d.ts +1 -1
  85. package/dist/src/domain/thread-lifecycle.d.ts +3 -2
  86. package/dist/src/index.d.ts +1 -2
  87. package/dist/src/{connection/node.d.ts → node/connection.d.ts} +1 -1
  88. package/dist/src/node/index.d.ts +1 -0
  89. package/dist/src/orchestration/index.d.ts +1 -1
  90. package/dist/src/orchestration/layer.d.ts +120 -829
  91. package/dist/src/orchestration/service.d.ts +2 -1
  92. package/dist/src/rpc/error.d.ts +5 -3
  93. package/dist/src/rpc/index.d.ts +5 -1
  94. package/dist/src/rpc/layer.d.ts +1166 -40
  95. package/dist/src/rpc/operation.d.ts +6280 -0
  96. package/dist/src/rpc/ws-group.d.ts +372 -27
  97. package/dist/src/runtime/index.d.ts +1 -2
  98. package/dist/src/runtime/layer.d.ts +12 -8
  99. package/dist/src/t3tools/index.d.ts +1 -1
  100. package/dist/t3tools.js +2 -2
  101. package/package.json +27 -19
  102. package/src/application/actions.test.ts +328 -0
  103. package/src/application/actions.ts +475 -0
  104. package/src/application/index.ts +32 -2
  105. package/src/application/layer.ts +43 -5
  106. package/src/application/model-selection.ts +70 -4
  107. package/src/application/models.ts +14 -14
  108. package/src/application/project-commands.ts +38 -5
  109. package/src/application/projects.ts +49 -20
  110. package/src/application/service.ts +275 -78
  111. package/src/application/shell-sequence.ts +6 -8
  112. package/src/application/terminals.ts +208 -0
  113. package/src/application/thread-commands.test.ts +43 -0
  114. package/src/application/thread-commands.ts +82 -1
  115. package/src/application/thread-update.ts +72 -0
  116. package/src/application/thread-wait.ts +47 -48
  117. package/src/application/threads.test.ts +172 -0
  118. package/src/application/threads.ts +128 -45
  119. package/src/auth/error.ts +2 -1
  120. package/src/auth/index.ts +7 -0
  121. package/src/auth/layer.test-utils.ts +34 -0
  122. package/src/auth/layer.ts +116 -34
  123. package/src/auth/local-base-dir.ts +13 -6
  124. package/src/auth/local-origin.ts +10 -7
  125. package/src/auth/local-token.ts +28 -23
  126. package/src/auth/service.test.ts +200 -0
  127. package/src/auth/service.ts +27 -3
  128. package/src/auth/transport.ts +2 -2
  129. package/src/auth/type.ts +47 -1
  130. package/src/bin.ts +26 -6
  131. package/src/cli/action-format.ts +110 -0
  132. package/src/cli/action.ts +398 -0
  133. package/src/cli/app.ts +8 -0
  134. package/src/cli/auth.ts +76 -23
  135. package/src/cli/env/flag.ts +8 -0
  136. package/src/cli/env/index.ts +2 -0
  137. package/src/cli/env/selection-layer.test.ts +49 -0
  138. package/src/cli/env/selection-layer.ts +27 -0
  139. package/src/cli/env.ts +107 -0
  140. package/src/cli/error.ts +26 -3
  141. package/src/cli/extra-args.test.ts +32 -0
  142. package/src/cli/extra-args.ts +22 -0
  143. package/src/cli/flags.ts +26 -1
  144. package/src/cli/format/auth.ts +92 -0
  145. package/src/cli/format/human.ts +131 -0
  146. package/src/cli/format/index.ts +12 -0
  147. package/src/cli/format/model.ts +29 -0
  148. package/src/cli/format/output.ts +51 -0
  149. package/src/cli/format/project.ts +32 -0
  150. package/src/cli/format/terminal.ts +87 -0
  151. package/src/cli/format/thread.test.ts +34 -0
  152. package/src/cli/format/thread.ts +185 -0
  153. package/src/cli/index.ts +20 -0
  154. package/src/cli/input/layer.ts +20 -0
  155. package/src/cli/input/service.ts +1 -0
  156. package/src/cli/interaction/confirm.ts +64 -0
  157. package/src/cli/interaction/self-action.ts +36 -0
  158. package/src/cli/message-input.ts +1 -1
  159. package/src/cli/model.ts +9 -5
  160. package/src/cli/project.ts +15 -8
  161. package/src/cli/projects/delete.ts +51 -0
  162. package/src/cli/require.ts +9 -7
  163. package/src/cli/runtime/index.ts +1 -0
  164. package/src/cli/runtime/service.ts +28 -0
  165. package/src/{scope → cli/scope}/resolve.ts +11 -9
  166. package/src/cli/terminal/attach.ts +29 -0
  167. package/src/cli/terminal/create.ts +55 -0
  168. package/src/cli/terminal/destroy.ts +62 -0
  169. package/src/cli/terminal/encoding.test.ts +63 -0
  170. package/src/cli/terminal/encoding.ts +23 -0
  171. package/src/cli/terminal/error.ts +14 -0
  172. package/src/cli/terminal/io-node-layer.ts +82 -0
  173. package/src/cli/terminal/io-service.ts +25 -0
  174. package/src/cli/terminal/list.ts +36 -0
  175. package/src/cli/terminal/read.test-utils.ts +48 -0
  176. package/src/cli/terminal/read.test.ts +35 -0
  177. package/src/cli/terminal/read.ts +99 -0
  178. package/src/cli/terminal/scope.ts +35 -0
  179. package/src/cli/terminal/shared.ts +250 -0
  180. package/src/cli/terminal/stream.ts +61 -0
  181. package/src/cli/terminal/wait.test.ts +148 -0
  182. package/src/cli/terminal/wait.ts +223 -0
  183. package/src/cli/terminal/write.test.ts +34 -0
  184. package/src/cli/terminal/write.ts +133 -0
  185. package/src/cli/terminal.ts +26 -0
  186. package/src/cli/thread.ts +8 -0
  187. package/src/cli/threads/approve.ts +11 -7
  188. package/src/cli/threads/archive.ts +23 -25
  189. package/src/cli/threads/callback.ts +6 -4
  190. package/src/cli/threads/delete.ts +67 -0
  191. package/src/cli/threads/interrupt.ts +57 -0
  192. package/src/cli/threads/list.test.ts +71 -0
  193. package/src/cli/threads/list.ts +25 -13
  194. package/src/cli/threads/messages.ts +11 -7
  195. package/src/cli/threads/respond.ts +11 -7
  196. package/src/cli/threads/send.ts +39 -9
  197. package/src/cli/threads/show.ts +11 -7
  198. package/src/cli/threads/start.ts +18 -13
  199. package/src/cli/threads/unarchive.ts +48 -0
  200. package/src/cli/threads/update.ts +155 -0
  201. package/src/cli/threads/wait.ts +11 -7
  202. package/src/cli/wait-events.ts +1 -1
  203. package/src/config/config.ts +260 -0
  204. package/src/config/credential/cipher-node.ts +40 -0
  205. package/src/config/credential/cipher-web.ts +82 -0
  206. package/src/config/credential/cipher.ts +39 -0
  207. package/src/config/credential/env.ts +13 -0
  208. package/src/config/credential/error.ts +9 -0
  209. package/src/config/credential/index.ts +6 -0
  210. package/src/config/credential/service.test-utils.ts +25 -0
  211. package/src/config/credential/service.test.ts +116 -0
  212. package/src/config/credential/service.ts +205 -0
  213. package/src/config/env/env.test-utils.ts +14 -0
  214. package/src/config/env/env.ts +81 -0
  215. package/src/config/env/index.ts +2 -0
  216. package/src/{layout/base-dir.ts → config/env/layout.ts} +0 -9
  217. package/src/config/environment-name/index.ts +7 -0
  218. package/src/config/environment-name/name.test.ts +66 -0
  219. package/src/config/environment-name/name.ts +49 -0
  220. package/src/config/error.ts +6 -8
  221. package/src/config/index.ts +16 -5
  222. package/src/config/keystore/error.ts +9 -0
  223. package/src/config/keystore/file.ts +96 -0
  224. package/src/config/keystore/index.ts +4 -0
  225. package/src/config/keystore/keyring-node.test.ts +12 -0
  226. package/src/config/keystore/keyring-node.ts +100 -0
  227. package/src/config/keystore/service.test-utils.ts +15 -0
  228. package/src/config/keystore/service.test.ts +18 -0
  229. package/src/config/keystore/service.ts +29 -0
  230. package/src/config/layer.test-utils.ts +45 -0
  231. package/src/config/layer.test.ts +209 -0
  232. package/src/config/paths/index.ts +1 -0
  233. package/src/config/paths/paths.ts +36 -0
  234. package/src/config/persist/file-mode.ts +21 -0
  235. package/src/config/persist/migration.test.ts +71 -0
  236. package/src/config/persist/migration.ts +82 -0
  237. package/src/config/persist/persist.test.ts +113 -0
  238. package/src/config/persist/persist.ts +65 -0
  239. package/src/config/persist/schema.ts +37 -0
  240. package/src/config/platform.test-utils.ts +20 -0
  241. package/src/config/resolve/resolve.test-utils.ts +20 -0
  242. package/src/config/resolve/resolve.test.ts +137 -0
  243. package/src/config/resolve/resolve.ts +104 -0
  244. package/src/config/selection/index.ts +1 -0
  245. package/src/config/selection/resolve.ts +14 -0
  246. package/src/config/selection/service.ts +29 -0
  247. package/src/config/temp-home.test-utils.ts +7 -0
  248. package/src/config/types.ts +49 -0
  249. package/src/config/url/error.ts +8 -0
  250. package/src/config/url/index.ts +7 -0
  251. package/src/config/url/url.test.ts +31 -0
  252. package/src/connection/index.ts +0 -1
  253. package/src/contracts/index.ts +1 -1
  254. package/src/domain/error.ts +39 -1
  255. package/src/domain/helpers.test.ts +1 -1
  256. package/src/domain/helpers.ts +1 -1
  257. package/src/domain/model-config.ts +5 -1
  258. package/src/domain/thread-activities.test.ts +1 -1
  259. package/src/domain/thread-activities.ts +1 -1
  260. package/src/domain/thread-lifecycle.test.ts +1 -1
  261. package/src/domain/thread-lifecycle.ts +15 -1
  262. package/src/effect.test-utils.ts +25 -0
  263. package/src/index.ts +1 -1
  264. package/src/{connection/node.ts → node/connection.ts} +1 -1
  265. package/src/node/index.ts +1 -0
  266. package/src/orchestration/index.ts +1 -1
  267. package/src/orchestration/layer.ts +34 -86
  268. package/src/orchestration/service.ts +5 -1
  269. package/src/rpc/error.ts +12 -2
  270. package/src/rpc/index.ts +5 -1
  271. package/src/rpc/layer.ts +1 -1
  272. package/src/rpc/operation.ts +83 -0
  273. package/src/rpc/ws-group.ts +25 -1
  274. package/src/runtime/index.ts +2 -1
  275. package/src/runtime/layer.test-utils.ts +32 -0
  276. package/src/runtime/layer.test.ts +66 -0
  277. package/src/runtime/layer.ts +22 -11
  278. package/src/t3tools/index.ts +1 -1
  279. package/src/types/marked-terminal.d.ts +11 -0
  280. package/dist/layout.js +0 -2
  281. package/dist/scope.js +0 -2
  282. package/dist/src/cli/output-format.d.ts +0 -9
  283. package/dist/src/config/layer.d.ts +0 -22
  284. package/dist/src/config/service.d.ts +0 -22
  285. package/dist/src/environment/layer.d.ts +0 -3
  286. package/dist/src/environment/service.d.ts +0 -12
  287. package/dist/src/layout/index.d.ts +0 -1
  288. package/dist/upstream-t3code/packages/contracts/src/auth.d.ts +0 -441
  289. package/dist/upstream-t3code/packages/contracts/src/baseSchemas.d.ts +0 -38
  290. package/dist/upstream-t3code/packages/contracts/src/desktopBootstrap.d.ts +0 -14
  291. package/dist/upstream-t3code/packages/contracts/src/editor.d.ts +0 -124
  292. package/dist/upstream-t3code/packages/contracts/src/environment.d.ts +0 -64
  293. package/dist/upstream-t3code/packages/contracts/src/environmentHttp.d.ts +0 -785
  294. package/dist/upstream-t3code/packages/contracts/src/filesystem.d.ts +0 -26
  295. package/dist/upstream-t3code/packages/contracts/src/git.d.ts +0 -500
  296. package/dist/upstream-t3code/packages/contracts/src/index.d.ts +0 -25
  297. package/dist/upstream-t3code/packages/contracts/src/ipc.d.ts +0 -496
  298. package/dist/upstream-t3code/packages/contracts/src/keybindings.d.ts +0 -133
  299. package/dist/upstream-t3code/packages/contracts/src/model.d.ts +0 -112
  300. package/dist/upstream-t3code/packages/contracts/src/orchestration.d.ts +0 -7338
  301. package/dist/upstream-t3code/packages/contracts/src/project.d.ts +0 -45
  302. package/dist/upstream-t3code/packages/contracts/src/provider.d.ts +0 -130
  303. package/dist/upstream-t3code/packages/contracts/src/providerInstance.d.ts +0 -99
  304. package/dist/upstream-t3code/packages/contracts/src/providerRuntime.d.ts +0 -4450
  305. package/dist/upstream-t3code/packages/contracts/src/relay.d.ts +0 -1262
  306. package/dist/upstream-t3code/packages/contracts/src/relayClient.d.ts +0 -48
  307. package/dist/upstream-t3code/packages/contracts/src/remoteAccess.d.ts +0 -45
  308. package/dist/upstream-t3code/packages/contracts/src/review.d.ts +0 -37
  309. package/dist/upstream-t3code/packages/contracts/src/rpc.d.ts +0 -8710
  310. package/dist/upstream-t3code/packages/contracts/src/server.d.ts +0 -2336
  311. package/dist/upstream-t3code/packages/contracts/src/settings.d.ts +0 -292
  312. package/dist/upstream-t3code/packages/contracts/src/sourceControl.d.ts +0 -177
  313. package/dist/upstream-t3code/packages/contracts/src/terminal.d.ts +0 -333
  314. package/dist/upstream-t3code/packages/contracts/src/vcs.d.ts +0 -148
  315. package/src/cli/auth-format.ts +0 -45
  316. package/src/cli/model-format.ts +0 -18
  317. package/src/cli/output-format.ts +0 -41
  318. package/src/cli/project-format.ts +0 -11
  319. package/src/cli/thread-format.ts +0 -121
  320. package/src/config/layer.ts +0 -105
  321. package/src/config/service.ts +0 -26
  322. package/src/environment/layer.ts +0 -12
  323. package/src/environment/service.ts +0 -13
  324. package/src/layout/index.ts +0 -1
  325. /package/dist/src/{scope → cli/scope}/index.d.ts +0 -0
  326. /package/dist/src/config/{url.d.ts → url/url.d.ts} +0 -0
  327. /package/src/{scope → cli/scope}/index.ts +0 -0
  328. /package/src/config/{url.ts → url/url.ts} +0 -0
@@ -0,0 +1,137 @@
1
+ import "vite-plus/test/config";
2
+
3
+ import { assert, describe, it } from "@effect/vitest";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Exit from "effect/Exit";
6
+
7
+ import { resolveConfiguredEnvironment } from "../selection/resolve.ts";
8
+ import { sampleEncrypted, sampleEncryptedToken } from "./resolve.test-utils.ts";
9
+ import {
10
+ buildResolvedConfigFromEnv,
11
+ resolveDefaultForUpsert,
12
+ selectEnvironmentName,
13
+ validateCredentialEnvVars,
14
+ } from "./resolve.ts";
15
+
16
+ describe("resolveConfiguredEnvironment", () => {
17
+ it("prefers cli flag over T3CLI_ENV", () => {
18
+ assert.equal(
19
+ resolveConfiguredEnvironment({
20
+ cliFlag: "cli",
21
+ t3cliEnv: "env",
22
+ }),
23
+ "cli",
24
+ );
25
+ });
26
+
27
+ it("falls back to T3CLI_ENV when cli flag is absent", () => {
28
+ assert.equal(
29
+ resolveConfiguredEnvironment({
30
+ t3cliEnv: "env",
31
+ }),
32
+ "env",
33
+ );
34
+ });
35
+ });
36
+
37
+ describe("selectEnvironmentName", () => {
38
+ it("prefers selected environment over config default", () => {
39
+ const selected = selectEnvironmentName({
40
+ selectedEnvironment: "cli",
41
+ defaultEnvironment: "default",
42
+ });
43
+ assert.equal(selected, "cli");
44
+ });
45
+
46
+ it("falls back to config default", () => {
47
+ assert.equal(
48
+ selectEnvironmentName({
49
+ defaultEnvironment: "default",
50
+ }),
51
+ "default",
52
+ );
53
+ });
54
+ });
55
+
56
+ describe("validateCredentialEnvVars", () => {
57
+ it.effect("requires both T3CODE_URL and T3CODE_TOKEN together", () =>
58
+ Effect.gen(function* () {
59
+ const onlyUrl = yield* validateCredentialEnvVars({ envUrl: "https://example.com" }).pipe(
60
+ Effect.exit,
61
+ );
62
+ assert.equal(Exit.isFailure(onlyUrl), true);
63
+
64
+ const onlyToken = yield* validateCredentialEnvVars({ envToken: "token" }).pipe(Effect.exit);
65
+ assert.equal(Exit.isFailure(onlyToken), true);
66
+
67
+ yield* validateCredentialEnvVars({
68
+ envUrl: "https://example.com",
69
+ envToken: "token",
70
+ });
71
+ }),
72
+ );
73
+ });
74
+
75
+ describe("buildResolvedConfigFromEnv", () => {
76
+ it.effect(
77
+ "does not inherit local=true from selected stored environment when env vars override",
78
+ () =>
79
+ Effect.gen(function* () {
80
+ const resolved = yield* buildResolvedConfigFromEnv({
81
+ envUrl: "https://remote.example",
82
+ envToken: "env-token",
83
+ });
84
+ assert.equal(resolved.source, "env");
85
+ assert.equal(resolved.local, false);
86
+ assert.equal(resolved.url, "https://remote.example");
87
+ assert.equal(resolved.token, "env-token");
88
+ }),
89
+ );
90
+ });
91
+
92
+ describe("resolveDefaultForUpsert", () => {
93
+ it("sets default only for the first environment", () => {
94
+ assert.equal(resolveDefaultForUpsert(sampleEncrypted({}), "first"), "first");
95
+ assert.equal(
96
+ resolveDefaultForUpsert(
97
+ sampleEncrypted({
98
+ default: "home",
99
+ environments: {
100
+ home: {
101
+ url: "https://home.example",
102
+ local: false,
103
+ token: sampleEncryptedToken(),
104
+ },
105
+ },
106
+ }),
107
+ "work",
108
+ ),
109
+ "home",
110
+ );
111
+ });
112
+
113
+ it("promotes default when makeDefault is true", () => {
114
+ assert.equal(
115
+ resolveDefaultForUpsert(
116
+ sampleEncrypted({
117
+ default: "home",
118
+ environments: {
119
+ home: {
120
+ url: "https://home.example",
121
+ local: false,
122
+ token: sampleEncryptedToken(),
123
+ },
124
+ work: {
125
+ url: "https://work.example",
126
+ local: false,
127
+ token: { ...sampleEncryptedToken(), nonce: "n2", ciphertext: "c2", tag: "t2" },
128
+ },
129
+ },
130
+ }),
131
+ "work",
132
+ true,
133
+ ),
134
+ "work",
135
+ );
136
+ });
137
+ });
@@ -0,0 +1,104 @@
1
+ import * as Effect from "effect/Effect";
2
+
3
+ import { validateEnvironmentName } from "../environment-name/name.ts";
4
+ import { ConfigError } from "../error.ts";
5
+ import type { EncryptedConfig, ResolvedConfig } from "../types.ts";
6
+ import { normalizeHttpBaseUrl } from "../url/url.ts";
7
+
8
+ export type ResolveSelectionInput = {
9
+ readonly selectedEnvironment?: string | undefined;
10
+ readonly defaultEnvironment?: string | undefined;
11
+ };
12
+
13
+ export function selectEnvironmentName(input: ResolveSelectionInput) {
14
+ if (input.selectedEnvironment !== undefined && input.selectedEnvironment.length > 0) {
15
+ return input.selectedEnvironment;
16
+ }
17
+ return input.defaultEnvironment;
18
+ }
19
+
20
+ export type ResolveCredentialInput = {
21
+ readonly envUrl?: string | undefined;
22
+ readonly envToken?: string | undefined;
23
+ };
24
+
25
+ export function validateCredentialEnvVars(input: ResolveCredentialInput) {
26
+ const hasEnvUrl = input.envUrl !== undefined && input.envUrl.length > 0;
27
+ const hasEnvToken = input.envToken !== undefined && input.envToken.length > 0;
28
+ if (hasEnvUrl !== hasEnvToken) {
29
+ return Effect.fail(
30
+ new ConfigError({
31
+ message: "T3CODE_URL and T3CODE_TOKEN must both be set together",
32
+ }),
33
+ );
34
+ }
35
+ return Effect.void;
36
+ }
37
+
38
+ export function resolveDefaultForUpsert(
39
+ encrypted: EncryptedConfig,
40
+ environmentName: string,
41
+ makeDefault?: boolean,
42
+ ): string | undefined {
43
+ if (makeDefault === true) {
44
+ return environmentName;
45
+ }
46
+ if (Object.keys(encrypted.environments).length === 0) {
47
+ return environmentName;
48
+ }
49
+ return encrypted.default;
50
+ }
51
+
52
+ export function summarizeEnvironments(encrypted: EncryptedConfig) {
53
+ return Object.entries(encrypted.environments)
54
+ .map(([name, environmentConfig]) => ({
55
+ name,
56
+ url: environmentConfig.url,
57
+ local: environmentConfig.local,
58
+ default: encrypted.default === name,
59
+ }))
60
+ .toSorted((left, right) => left.name.localeCompare(right.name));
61
+ }
62
+
63
+ export function buildResolvedConfigFromEnv(input: {
64
+ readonly envUrl: string;
65
+ readonly envToken: string;
66
+ }) {
67
+ return normalizeHttpBaseUrl(input.envUrl).pipe(
68
+ Effect.mapError((error) => new ConfigError({ message: error.message, cause: error.cause })),
69
+ Effect.map((normalizedUrl) => {
70
+ return {
71
+ url: normalizedUrl,
72
+ token: input.envToken,
73
+ source: "env" as const,
74
+ local: false,
75
+ } satisfies ResolvedConfig;
76
+ }),
77
+ );
78
+ }
79
+
80
+ export function buildResolvedConfigFromStored(input: {
81
+ readonly selectedName: string;
82
+ readonly token: string;
83
+ readonly encrypted: EncryptedConfig;
84
+ }) {
85
+ return Effect.gen(function* () {
86
+ yield* validateEnvironmentName(input.selectedName);
87
+ const selectedEnvironment = input.encrypted.environments[input.selectedName];
88
+ if (selectedEnvironment === undefined) {
89
+ return yield* Effect.fail(
90
+ new ConfigError({ message: `environment not found: ${input.selectedName}` }),
91
+ );
92
+ }
93
+ const normalizedUrl = yield* normalizeHttpBaseUrl(selectedEnvironment.url).pipe(
94
+ Effect.mapError((error) => new ConfigError({ message: error.message, cause: error.cause })),
95
+ );
96
+ return {
97
+ url: normalizedUrl,
98
+ token: input.token,
99
+ source: "config" as const,
100
+ local: selectedEnvironment.local,
101
+ environment: input.selectedName,
102
+ } satisfies ResolvedConfig;
103
+ });
104
+ }
@@ -0,0 +1 @@
1
+ export * as Service from "./service.ts";
@@ -0,0 +1,14 @@
1
+ export function resolveConfiguredEnvironment(input: {
2
+ readonly cliFlag?: string | undefined;
3
+ readonly t3cliEnv?: string | undefined;
4
+ }): string | undefined {
5
+ const fromFlag = input.cliFlag?.trim();
6
+ if (fromFlag !== undefined && fromFlag.length > 0) {
7
+ return fromFlag;
8
+ }
9
+ const fromEnv = input.t3cliEnv?.trim();
10
+ if (fromEnv !== undefined && fromEnv.length > 0) {
11
+ return fromEnv;
12
+ }
13
+ return undefined;
14
+ }
@@ -0,0 +1,29 @@
1
+ import * as Context from "effect/Context";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ import * as Option from "effect/Option";
5
+
6
+ import { loadT3CliEnv } from "../env/env.ts";
7
+ import { resolveConfiguredEnvironment } from "./resolve.ts";
8
+
9
+ export class T3ConfigSelection extends Context.Service<
10
+ T3ConfigSelection,
11
+ {
12
+ readonly getSelectedEnvironment: () => Effect.Effect<string | undefined>;
13
+ }
14
+ >()("t3cli/T3ConfigSelection") {}
15
+
16
+ export const layer = Layer.effect(
17
+ T3ConfigSelection,
18
+ Effect.gen(function* () {
19
+ const t3CliEnv = yield* loadT3CliEnv;
20
+ return {
21
+ getSelectedEnvironment: () =>
22
+ Effect.sync(() =>
23
+ resolveConfiguredEnvironment({
24
+ t3cliEnv: Option.getOrUndefined(t3CliEnv.t3cliEnv),
25
+ }),
26
+ ),
27
+ };
28
+ }),
29
+ );
@@ -0,0 +1,7 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as FileSystem from "effect/FileSystem";
3
+
4
+ export const makeTempHomeScoped = Effect.fn("makeTempHomeScoped")(function* (prefix: string) {
5
+ const fs = yield* FileSystem.FileSystem;
6
+ return yield* fs.makeTempDirectoryScoped({ prefix });
7
+ });
@@ -0,0 +1,49 @@
1
+ import type {
2
+ EncryptedToken,
3
+ StoredConfigV2File,
4
+ StoredEnvironmentFile,
5
+ } from "./persist/schema.ts";
6
+
7
+ export type EncryptedEnvironment = StoredEnvironmentFile;
8
+
9
+ export type EncryptedConfig = StoredConfigV2File;
10
+
11
+ export type DecryptedEnvironment = {
12
+ readonly url: string;
13
+ readonly local: boolean;
14
+ readonly token: string;
15
+ };
16
+
17
+ export type DecryptedConfig = {
18
+ readonly version: 2;
19
+ readonly default?: string;
20
+ readonly environments: Readonly<Record<string, DecryptedEnvironment>>;
21
+ };
22
+
23
+ export type EnvironmentSummary = {
24
+ readonly name: string;
25
+ readonly url: string;
26
+ readonly local: boolean;
27
+ readonly default: boolean;
28
+ };
29
+
30
+ export type ResolvedConfig = {
31
+ readonly url: string;
32
+ readonly token: string;
33
+ readonly source: "env" | "config";
34
+ readonly local: boolean;
35
+ readonly environment?: string;
36
+ };
37
+
38
+ export type UpsertEnvironmentInput = {
39
+ readonly name: string;
40
+ readonly url: string;
41
+ readonly token: string;
42
+ readonly local: boolean;
43
+ readonly makeDefault?: boolean;
44
+ };
45
+
46
+ export type StoredConfig = DecryptedConfig;
47
+ export type StoredEnvironment = DecryptedEnvironment;
48
+
49
+ export type { EncryptedToken };
@@ -0,0 +1,8 @@
1
+ import * as Cause from "effect/Cause";
2
+ import * as Schema from "effect/Schema";
3
+
4
+ export class UrlError extends Schema.TaggedErrorClass<UrlError>()("UrlError", {
5
+ message: Schema.String,
6
+ protocol: Schema.optionalKey(Schema.String),
7
+ cause: Schema.optionalKey(Schema.instanceOf(Cause.IllegalArgumentError)),
8
+ }) {}
@@ -0,0 +1,7 @@
1
+ export {
2
+ normalizeHttpBaseUrl,
3
+ toWebSocketBaseUrl,
4
+ toHttpEndpointUrl,
5
+ toWebSocketEndpointUrl,
6
+ } from "./url.ts";
7
+ export { UrlError } from "./error.ts";
@@ -0,0 +1,31 @@
1
+ import "vite-plus/test/config";
2
+
3
+ import * as Effect from "effect/Effect";
4
+ import { assert, describe, it } from "@effect/vitest";
5
+
6
+ import { expectFailWithMessage } from "../../effect.test-utils.ts";
7
+ import { UrlError } from "./error.ts";
8
+ import { normalizeHttpBaseUrl, toWebSocketEndpointUrl } from "./url.ts";
9
+
10
+ describe("normalizeHttpBaseUrl", () => {
11
+ it.effect("strips query and trailing slash from urls with paths", () =>
12
+ Effect.gen(function* () {
13
+ const normalized = yield* normalizeHttpBaseUrl("https://app.example.com/api/?q=1");
14
+ assert.equal(normalized, "https://app.example.com/api");
15
+ }),
16
+ );
17
+
18
+ it.effect("maps invalid urls to UrlError", () =>
19
+ expectFailWithMessage(normalizeHttpBaseUrl("not-a-url"), UrlError, "invalid url"),
20
+ );
21
+ });
22
+
23
+ describe("toWebSocketEndpointUrl", () => {
24
+ it.effect("rejects unsupported protocols with UrlError", () =>
25
+ expectFailWithMessage(
26
+ toWebSocketEndpointUrl("ftp://example.com", "/ws"),
27
+ UrlError,
28
+ "unsupported server url protocol: ftp:",
29
+ ),
30
+ );
31
+ });
@@ -1,6 +1,5 @@
1
1
  export { T3CodeConnectionError } from "./error.ts";
2
2
  export { T3CodeRpcLayer, makeT3CodeRpcLayer } from "./layer.ts";
3
- export { T3CodeNodeRpcLayer } from "./node.ts";
4
3
  export {
5
4
  T3CodeConnectionProvider,
6
5
  T3CodeConnectionProviderLive,
@@ -5,4 +5,4 @@ export type {
5
5
  OrchestrationThread,
6
6
  OrchestrationThreadShell,
7
7
  ServerProvider,
8
- } from "#t3tools/contracts";
8
+ } from "@t3tools/contracts";
@@ -30,6 +30,14 @@ export class ThreadSessionError extends Schema.TaggedErrorClass<ThreadSessionErr
30
30
  },
31
31
  ) {}
32
32
 
33
+ export class ThreadLookupError extends Schema.TaggedErrorClass<ThreadLookupError>()(
34
+ "ThreadLookupError",
35
+ {
36
+ message: Schema.String,
37
+ threadId: Schema.String,
38
+ },
39
+ ) {}
40
+
33
41
  export class ProjectCreateVisibilityError extends Schema.TaggedErrorClass<ProjectCreateVisibilityError>()(
34
42
  "ProjectCreateVisibilityError",
35
43
  {
@@ -38,9 +46,39 @@ export class ProjectCreateVisibilityError extends Schema.TaggedErrorClass<Projec
38
46
  },
39
47
  ) {}
40
48
 
49
+ export class TerminalLookupError extends Schema.TaggedErrorClass<TerminalLookupError>()(
50
+ "TerminalLookupError",
51
+ {
52
+ message: Schema.String,
53
+ threadId: Schema.String,
54
+ terminalId: Schema.String,
55
+ },
56
+ ) {}
57
+
58
+ export class ProjectActionLookupError extends Schema.TaggedErrorClass<ProjectActionLookupError>()(
59
+ "ProjectActionLookupError",
60
+ {
61
+ message: Schema.String,
62
+ projectId: Schema.String,
63
+ selector: Schema.String,
64
+ },
65
+ ) {}
66
+
67
+ export class ProjectActionValidationError extends Schema.TaggedErrorClass<ProjectActionValidationError>()(
68
+ "ProjectActionValidationError",
69
+ {
70
+ message: Schema.String,
71
+ projectId: Schema.String,
72
+ },
73
+ ) {}
74
+
41
75
  export type DomainError =
42
76
  | ProjectLookupError
43
77
  | ModelSelectionError
44
78
  | ThreadEventError
45
79
  | ThreadSessionError
46
- | ProjectCreateVisibilityError;
80
+ | ThreadLookupError
81
+ | ProjectCreateVisibilityError
82
+ | TerminalLookupError
83
+ | ProjectActionLookupError
84
+ | ProjectActionValidationError;
@@ -5,7 +5,7 @@ import * as NodeServices from "@effect/platform-node/NodeServices";
5
5
  import { assert, describe, it } from "@effect/vitest";
6
6
  import { fromPartial } from "@total-typescript/shoehorn";
7
7
 
8
- import type { OrchestrationShellSnapshot } from "#t3tools/contracts";
8
+ import type { OrchestrationShellSnapshot } from "@t3tools/contracts";
9
9
 
10
10
  import { resolveProjectScope } from "./helpers.ts";
11
11
 
@@ -1,6 +1,6 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import * as Path from "effect/Path";
3
- import type { OrchestrationProjectShell, OrchestrationShellSnapshot } from "#t3tools/contracts";
3
+ import type { OrchestrationProjectShell, OrchestrationShellSnapshot } from "@t3tools/contracts";
4
4
 
5
5
  export type ResolvedProjectScope = {
6
6
  readonly project: OrchestrationProjectShell;
@@ -1,4 +1,4 @@
1
- import type { ServerProvider } from "#t3tools/contracts";
1
+ import type { ServerProvider } from "@t3tools/contracts";
2
2
 
3
3
  export function isSelectableProvider(provider: ServerProvider) {
4
4
  return provider.status === "ready" && provider.models.length > 0;
@@ -12,6 +12,10 @@ export function firstSelectableModel(provider: ServerProvider) {
12
12
  return provider.models[0];
13
13
  }
14
14
 
15
+ export function findSelectableModel(provider: ServerProvider, slug: string) {
16
+ return provider.models.find((model) => model.slug === slug);
17
+ }
18
+
15
19
  export function findSelectableProvider(
16
20
  providers: ReadonlyArray<ServerProvider>,
17
21
  instanceId: string,
@@ -1,6 +1,6 @@
1
1
  import "vite-plus/test/config";
2
2
 
3
- import { EventId, TurnId, type OrchestrationThreadActivity } from "#t3tools/contracts";
3
+ import { EventId, TurnId, type OrchestrationThreadActivity } from "@t3tools/contracts";
4
4
  import { describe, expect, it } from "vite-plus/test";
5
5
 
6
6
  import { derivePendingApprovals, derivePendingUserInputs } from "./thread-activities.ts";
@@ -2,7 +2,7 @@ import {
2
2
  ApprovalRequestId,
3
3
  type OrchestrationThreadActivity,
4
4
  type UserInputQuestion,
5
- } from "#t3tools/contracts";
5
+ } from "@t3tools/contracts";
6
6
 
7
7
  export type PendingApproval = {
8
8
  readonly requestId: ApprovalRequestId;
@@ -3,7 +3,7 @@ import "vite-plus/test/config";
3
3
  import { describe, expect, it } from "vite-plus/test";
4
4
  import { fromPartial } from "@total-typescript/shoehorn";
5
5
 
6
- import type { OrchestrationThread } from "#t3tools/contracts";
6
+ import type { OrchestrationThread } from "@t3tools/contracts";
7
7
 
8
8
  import { isThreadActive, isThreadCompleteEnough } from "./thread-lifecycle.ts";
9
9
 
@@ -2,11 +2,25 @@ import {
2
2
  OrchestrationMessage,
3
3
  type OrchestrationEvent,
4
4
  type OrchestrationMessage as OrchestrationMessageType,
5
+ type OrchestrationSession,
5
6
  type OrchestrationThread,
6
7
  type OrchestrationThreadShell,
7
- } from "#t3tools/contracts";
8
+ } from "@t3tools/contracts";
8
9
  import * as Schema from "effect/Schema";
9
10
 
11
+ type SessionStatusForDelete = OrchestrationSession["status"] | "closed";
12
+
13
+ function isClosedSessionStatus(status: SessionStatusForDelete): status is "closed" {
14
+ return status === "closed";
15
+ }
16
+
17
+ export function sessionNeedsStopBeforeDelete(session: OrchestrationSession | null) {
18
+ if (session === null) {
19
+ return false;
20
+ }
21
+ return !isClosedSessionStatus(session.status);
22
+ }
23
+
10
24
  export function isThreadActive(thread: OrchestrationThreadShell | OrchestrationThread) {
11
25
  if (thread.session?.status === "starting" || thread.session?.status === "running") {
12
26
  return true;
@@ -0,0 +1,25 @@
1
+ import { assert } from "@effect/vitest";
2
+ import { assertInstanceOf } from "@effect/vitest/utils";
3
+ import * as Cause from "effect/Cause";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Exit from "effect/Exit";
6
+ import * as Option from "effect/Option";
7
+
8
+ export const expectFailWithMessage = <E extends { readonly message: string }, R>(
9
+ effect: Effect.Effect<unknown, E, R>,
10
+ ErrorClass: new (...args: never) => E,
11
+ message: string,
12
+ ): Effect.Effect<void, never, R> =>
13
+ Effect.gen(function* () {
14
+ const exit = yield* effect.pipe(Effect.exit);
15
+ if (!Exit.isFailure(exit)) {
16
+ return yield* Effect.die("expected failure");
17
+ }
18
+ const error = Cause.findErrorOption(exit.cause);
19
+ if (Option.isNone(error)) {
20
+ return yield* Effect.die("expected failure cause");
21
+ }
22
+ assertInstanceOf(error.value, ErrorClass);
23
+ assert.equal(error.value.message, message);
24
+ return yield* Effect.void;
25
+ });
package/src/index.ts CHANGED
@@ -4,6 +4,6 @@ export type {
4
4
  StartThreadInput,
5
5
  StartThreadPolicy,
6
6
  WaitEvent,
7
+ ApplicationError,
7
8
  } from "./application/index.ts";
8
- export type { ApplicationError } from "./application/index.ts";
9
9
  export { AppLayer, AuthAppLayer } from "./runtime/index.ts";
@@ -2,7 +2,7 @@ import * as Layer from "effect/Layer";
2
2
  import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient";
3
3
  import * as NodeSocket from "@effect/platform-node/NodeSocket";
4
4
 
5
- import { T3CodeRpcLayer } from "./layer.ts";
5
+ import { T3CodeRpcLayer } from "../connection/index.ts";
6
6
 
7
7
  const NodeConnectionPlatformLayer = Layer.mergeAll(
8
8
  NodeHttpClient.layerUndici,
@@ -0,0 +1 @@
1
+ export { T3CodeNodeRpcLayer } from "./connection.ts";
@@ -4,4 +4,4 @@ export {
4
4
  type Orchestration,
5
5
  type OrchestrationError,
6
6
  } from "./service.ts";
7
- export { T3OrchestrationLayer } from "../runtime/layer.ts";
7
+ export { makeT3Orchestration, T3OrchestrationLive } from "./layer.ts";