t3code-cli 0.11.0 → 0.13.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 (330) hide show
  1. package/README.md +42 -10
  2. package/dist/application.js +2 -2
  3. package/dist/auth.js +1 -1
  4. package/dist/bin.js +85549 -2668
  5. package/dist/cli.js +15 -2
  6. package/dist/config.js +120 -2
  7. package/dist/connection.js +2 -2
  8. package/dist/index.js +1 -1
  9. package/dist/node.js +6 -2
  10. package/dist/orchestration.js +2 -2
  11. package/dist/rolldown-runtime.js +1 -1
  12. package/dist/rpc.js +1 -1
  13. package/dist/runtime.js +2 -2
  14. package/dist/shared.js +23313 -8022
  15. package/dist/src/application/actions.d.ts +25 -0
  16. package/dist/src/application/index.d.ts +3 -3
  17. package/dist/src/application/layer.d.ts +66 -50
  18. package/dist/src/application/model-selection.d.ts +2 -2
  19. package/dist/src/application/models.d.ts +1 -1
  20. package/dist/src/application/project-commands.d.ts +23 -3
  21. package/dist/src/application/projects.d.ts +7 -7
  22. package/dist/src/application/service.d.ts +65 -2
  23. package/dist/src/application/shell-sequence.d.ts +25 -19
  24. package/dist/src/application/terminals.d.ts +8 -8
  25. package/dist/src/application/thread-commands.d.ts +19 -19
  26. package/dist/src/application/thread-update.d.ts +1 -1
  27. package/dist/src/application/thread-wait.d.ts +19 -15
  28. package/dist/src/application/threads.d.ts +134 -108
  29. package/dist/src/auth/error.d.ts +3 -3
  30. package/dist/src/auth/index.d.ts +1 -1
  31. package/dist/src/auth/layer.d.ts +36 -8
  32. package/dist/src/auth/local-base-dir.d.ts +5 -2
  33. package/dist/src/auth/local-origin.d.ts +3 -3
  34. package/dist/src/auth/local-token.d.ts +3 -3
  35. package/dist/src/auth/remote-error.d.ts +3 -0
  36. package/dist/src/auth/service.d.ts +15 -3
  37. package/dist/src/auth/type.d.ts +40 -1
  38. package/dist/src/cli/env/flag.d.ts +2 -0
  39. package/dist/src/cli/env/index.d.ts +2 -0
  40. package/dist/src/cli/env/selection-layer.d.ts +3 -0
  41. package/dist/src/cli/flags.d.ts +2 -0
  42. package/dist/src/cli/format/index.d.ts +1 -0
  43. package/dist/src/cli/format/output.d.ts +12 -0
  44. package/dist/src/cli/index.d.ts +4 -0
  45. package/dist/src/cli/runtime/index.d.ts +1 -0
  46. package/dist/src/cli/runtime/service.d.ts +16 -0
  47. package/dist/src/{scope → cli/scope}/resolve.d.ts +5 -4
  48. package/dist/src/config/config.d.ts +49 -0
  49. package/dist/src/config/credential/cipher-node.d.ts +3 -0
  50. package/dist/src/config/credential/cipher-web.d.ts +3 -0
  51. package/dist/src/config/credential/cipher.d.ts +29 -0
  52. package/dist/src/config/credential/env.d.ts +3 -0
  53. package/dist/src/config/credential/error.d.ts +8 -0
  54. package/dist/src/config/credential/index.d.ts +6 -0
  55. package/dist/src/config/credential/service.d.ts +34 -0
  56. package/dist/src/config/env/env.d.ts +55 -0
  57. package/dist/src/config/env/index.d.ts +2 -0
  58. package/dist/src/{layout/base-dir.d.ts → config/env/layout.d.ts} +0 -1
  59. package/dist/src/config/environment-name/index.d.ts +1 -0
  60. package/dist/src/config/environment-name/name.d.ts +10 -0
  61. package/dist/src/config/error.d.ts +4 -11
  62. package/dist/src/config/index.d.ts +10 -5
  63. package/dist/src/config/keystore/error.d.ts +8 -0
  64. package/dist/src/config/keystore/file.d.ts +9 -0
  65. package/dist/src/config/keystore/index.d.ts +4 -0
  66. package/dist/src/config/keystore/keyring-node.d.ts +4 -0
  67. package/dist/src/config/keystore/service.d.ts +33 -0
  68. package/dist/src/config/paths/index.d.ts +1 -0
  69. package/dist/src/config/paths/paths.d.ts +6 -0
  70. package/dist/src/config/persist/file-mode.d.ts +4 -0
  71. package/dist/src/config/persist/migration.d.ts +68 -0
  72. package/dist/src/config/persist/persist.d.ts +40 -0
  73. package/dist/src/config/persist/schema.d.ts +68 -0
  74. package/dist/src/config/resolve/resolve.d.ts +40 -0
  75. package/dist/src/config/selection/index.d.ts +1 -0
  76. package/dist/src/config/selection/resolve.d.ts +4 -0
  77. package/dist/src/config/selection/service.d.ts +10 -0
  78. package/dist/src/config/types.d.ts +36 -0
  79. package/dist/src/config/url/error.d.ts +10 -0
  80. package/dist/src/config/url/index.d.ts +2 -0
  81. package/dist/src/connection/error.d.ts +3 -1
  82. package/dist/src/connection/index.d.ts +0 -1
  83. package/dist/src/connection/prepared.d.ts +14 -0
  84. package/dist/src/contracts/index.d.ts +1 -1
  85. package/dist/src/domain/error.d.ts +14 -1
  86. package/dist/src/domain/helpers.d.ts +53 -43
  87. package/dist/src/domain/model-config.d.ts +92 -84
  88. package/dist/src/domain/thread-activities.d.ts +1 -1
  89. package/dist/src/domain/thread-lifecycle.d.ts +24 -20
  90. package/dist/src/index.d.ts +1 -2
  91. package/dist/src/{connection/node.d.ts → node/connection.d.ts} +1 -1
  92. package/dist/src/node/index.d.ts +1 -2
  93. package/dist/src/orchestration/index.d.ts +0 -1
  94. package/dist/src/orchestration/layer.d.ts +321 -390
  95. package/dist/src/orchestration/service.d.ts +1 -1
  96. package/dist/src/rpc/error.d.ts +4 -6
  97. package/dist/src/rpc/layer.d.ts +2036 -1917
  98. package/dist/src/rpc/operation.d.ts +4 -5732
  99. package/dist/src/rpc/service.d.ts +1 -1
  100. package/dist/src/rpc/session.d.ts +22 -0
  101. package/dist/src/rpc/ws-group.d.ts +456 -1032
  102. package/dist/src/runtime/index.d.ts +1 -2
  103. package/dist/src/runtime/layer.d.ts +12 -8
  104. package/dist/src/sql/node-sqlite-client.d.ts +1 -1
  105. package/dist/src/t3tools/index.d.ts +1 -1
  106. package/dist/t3tools.js +2 -2
  107. package/package.json +30 -25
  108. package/src/application/actions.test.ts +328 -0
  109. package/src/application/actions.ts +475 -0
  110. package/src/application/index.ts +9 -5
  111. package/src/application/layer.ts +9 -0
  112. package/src/application/model-selection.ts +2 -2
  113. package/src/application/project-commands.ts +23 -5
  114. package/src/application/projects.ts +6 -7
  115. package/src/application/service.ts +79 -1
  116. package/src/application/shell-sequence.ts +1 -1
  117. package/src/application/terminals.ts +2 -1
  118. package/src/application/thread-commands.ts +1 -1
  119. package/src/application/thread-update.ts +1 -1
  120. package/src/application/thread-wait.ts +5 -1
  121. package/src/application/threads.test.ts +9 -3
  122. package/src/application/threads.ts +6 -6
  123. package/src/auth/error.ts +4 -9
  124. package/src/auth/index.ts +7 -0
  125. package/src/auth/layer.test-utils.ts +34 -0
  126. package/src/auth/layer.ts +116 -34
  127. package/src/auth/local-base-dir.ts +13 -6
  128. package/src/auth/local-origin.ts +10 -7
  129. package/src/auth/local-token.ts +28 -23
  130. package/src/auth/remote-error.ts +17 -0
  131. package/src/auth/service.test.ts +200 -0
  132. package/src/auth/service.ts +27 -3
  133. package/src/auth/transport.ts +45 -114
  134. package/src/auth/type.ts +47 -1
  135. package/src/bin.ts +24 -6
  136. package/src/cli/action-format.ts +110 -0
  137. package/src/cli/action.ts +398 -0
  138. package/src/cli/app.ts +6 -0
  139. package/src/cli/auth.ts +76 -23
  140. package/src/cli/env/flag.ts +8 -0
  141. package/src/cli/env/index.ts +2 -0
  142. package/src/cli/env/selection-layer.test.ts +49 -0
  143. package/src/cli/env/selection-layer.ts +27 -0
  144. package/src/cli/env.ts +107 -0
  145. package/src/cli/extra-args.test.ts +32 -0
  146. package/src/cli/extra-args.ts +22 -0
  147. package/src/cli/flags.ts +11 -1
  148. package/src/cli/format/auth.ts +92 -0
  149. package/src/cli/format/human.ts +131 -0
  150. package/src/cli/format/index.ts +12 -0
  151. package/src/cli/format/model.ts +29 -0
  152. package/src/cli/format/output.ts +51 -0
  153. package/src/cli/format/project.ts +32 -0
  154. package/src/cli/format/terminal.ts +87 -0
  155. package/src/cli/{thread-format.test.ts → format/thread.test.ts} +6 -4
  156. package/src/cli/format/thread.ts +185 -0
  157. package/src/cli/index.ts +20 -0
  158. package/src/cli/interaction/confirm.ts +64 -0
  159. package/src/cli/{self-action.ts → interaction/self-action.ts} +8 -6
  160. package/src/cli/message-input.ts +1 -1
  161. package/src/cli/model.ts +9 -5
  162. package/src/cli/project.ts +13 -7
  163. package/src/cli/projects/delete.ts +13 -12
  164. package/src/cli/require.ts +9 -7
  165. package/src/cli/runtime/index.ts +1 -0
  166. package/src/cli/runtime/service.ts +28 -0
  167. package/src/{scope → cli/scope}/resolve.ts +11 -9
  168. package/src/cli/terminal/attach.ts +3 -6
  169. package/src/cli/terminal/create.ts +10 -9
  170. package/src/cli/terminal/destroy.ts +13 -11
  171. package/src/cli/terminal/list.ts +10 -9
  172. package/src/cli/terminal/read.test-utils.ts +48 -0
  173. package/src/cli/terminal/read.test.ts +35 -0
  174. package/src/cli/terminal/read.ts +3 -6
  175. package/src/cli/terminal/scope.ts +10 -5
  176. package/src/cli/terminal/shared.ts +1 -1
  177. package/src/cli/terminal/stream.ts +3 -6
  178. package/src/cli/terminal/wait.test.ts +5 -3
  179. package/src/cli/terminal/wait.ts +10 -9
  180. package/src/cli/terminal/write.test.ts +34 -0
  181. package/src/cli/terminal/write.ts +10 -9
  182. package/src/cli/threads/approve.ts +11 -7
  183. package/src/cli/threads/archive.ts +14 -9
  184. package/src/cli/threads/callback.ts +6 -4
  185. package/src/cli/threads/delete.ts +17 -11
  186. package/src/cli/threads/interrupt.ts +14 -9
  187. package/src/cli/threads/list.test.ts +5 -3
  188. package/src/cli/threads/list.ts +10 -10
  189. package/src/cli/threads/messages.ts +11 -7
  190. package/src/cli/threads/respond.ts +11 -7
  191. package/src/cli/threads/send.ts +19 -9
  192. package/src/cli/threads/show.ts +11 -7
  193. package/src/cli/threads/start.ts +18 -13
  194. package/src/cli/threads/unarchive.ts +11 -7
  195. package/src/cli/threads/update.ts +14 -9
  196. package/src/cli/threads/wait.ts +11 -7
  197. package/src/cli/wait-events.ts +1 -1
  198. package/src/config/config.ts +260 -0
  199. package/src/config/credential/cipher-node.ts +40 -0
  200. package/src/config/credential/cipher-web.ts +82 -0
  201. package/src/config/credential/cipher.ts +39 -0
  202. package/src/config/credential/env.ts +13 -0
  203. package/src/config/credential/error.ts +9 -0
  204. package/src/config/credential/index.ts +6 -0
  205. package/src/config/credential/service.test-utils.ts +25 -0
  206. package/src/config/credential/service.test.ts +116 -0
  207. package/src/config/credential/service.ts +205 -0
  208. package/src/config/env/env.test-utils.ts +14 -0
  209. package/src/config/env/env.ts +81 -0
  210. package/src/config/env/index.ts +2 -0
  211. package/src/{layout/base-dir.ts → config/env/layout.ts} +0 -9
  212. package/src/config/environment-name/index.ts +7 -0
  213. package/src/config/environment-name/name.test.ts +66 -0
  214. package/src/config/environment-name/name.ts +49 -0
  215. package/src/config/error.ts +6 -8
  216. package/src/config/index.ts +16 -5
  217. package/src/config/keystore/error.ts +9 -0
  218. package/src/config/keystore/file.ts +96 -0
  219. package/src/config/keystore/index.ts +4 -0
  220. package/src/config/keystore/keyring-node.test.ts +12 -0
  221. package/src/config/keystore/keyring-node.ts +100 -0
  222. package/src/config/keystore/service.test-utils.ts +15 -0
  223. package/src/config/keystore/service.test.ts +18 -0
  224. package/src/config/keystore/service.ts +29 -0
  225. package/src/config/layer.test-utils.ts +45 -0
  226. package/src/config/layer.test.ts +209 -0
  227. package/src/config/paths/index.ts +1 -0
  228. package/src/config/paths/paths.ts +36 -0
  229. package/src/config/persist/file-mode.ts +21 -0
  230. package/src/config/persist/migration.test.ts +71 -0
  231. package/src/config/persist/migration.ts +82 -0
  232. package/src/config/persist/persist.test.ts +113 -0
  233. package/src/config/persist/persist.ts +65 -0
  234. package/src/config/persist/schema.ts +37 -0
  235. package/src/config/platform.test-utils.ts +20 -0
  236. package/src/config/resolve/resolve.test-utils.ts +20 -0
  237. package/src/config/resolve/resolve.test.ts +137 -0
  238. package/src/config/resolve/resolve.ts +104 -0
  239. package/src/config/selection/index.ts +1 -0
  240. package/src/config/selection/resolve.ts +14 -0
  241. package/src/config/selection/service.ts +29 -0
  242. package/src/config/temp-home.test-utils.ts +7 -0
  243. package/src/config/types.ts +49 -0
  244. package/src/config/url/error.ts +8 -0
  245. package/src/config/url/index.ts +7 -0
  246. package/src/config/url/url.test.ts +31 -0
  247. package/src/connection/error.ts +4 -1
  248. package/src/connection/index.ts +0 -1
  249. package/src/connection/layer.ts +5 -2
  250. package/src/connection/prepared.ts +83 -0
  251. package/src/contracts/index.ts +1 -1
  252. package/src/domain/error.ts +20 -1
  253. package/src/domain/helpers.test.ts +1 -1
  254. package/src/domain/helpers.ts +1 -1
  255. package/src/domain/model-config.ts +1 -1
  256. package/src/domain/thread-activities.test.ts +1 -1
  257. package/src/domain/thread-activities.ts +1 -1
  258. package/src/domain/thread-lifecycle.test.ts +1 -1
  259. package/src/domain/thread-lifecycle.ts +1 -1
  260. package/src/effect.test-utils.ts +25 -0
  261. package/src/index.ts +1 -1
  262. package/src/{connection/node.ts → node/connection.ts} +1 -1
  263. package/src/node/index.ts +1 -2
  264. package/src/orchestration/index.ts +0 -1
  265. package/src/orchestration/layer.ts +3 -2
  266. package/src/orchestration/service.ts +1 -1
  267. package/src/rpc/error.ts +10 -17
  268. package/src/rpc/layer.ts +17 -51
  269. package/src/rpc/operation.ts +16 -2
  270. package/src/rpc/service.ts +2 -1
  271. package/src/rpc/session.ts +168 -0
  272. package/src/rpc/ws-group.ts +13 -27
  273. package/src/runtime/index.ts +2 -1
  274. package/src/runtime/layer.test-utils.ts +32 -0
  275. package/src/runtime/layer.test.ts +66 -0
  276. package/src/runtime/layer.ts +21 -12
  277. package/src/t3tools/index.ts +1 -1
  278. package/src/types/marked-terminal.d.ts +11 -0
  279. package/dist/layout.js +0 -2
  280. package/dist/scope.js +0 -2
  281. package/dist/src/cli/output-format.d.ts +0 -11
  282. package/dist/src/config/layer.d.ts +0 -22
  283. package/dist/src/config/service.d.ts +0 -22
  284. package/dist/src/environment/layer.d.ts +0 -3
  285. package/dist/src/environment/service.d.ts +0 -12
  286. package/dist/src/layout/index.d.ts +0 -1
  287. package/dist/upstream-t3code/packages/contracts/src/auth.d.ts +0 -441
  288. package/dist/upstream-t3code/packages/contracts/src/baseSchemas.d.ts +0 -38
  289. package/dist/upstream-t3code/packages/contracts/src/desktopBootstrap.d.ts +0 -14
  290. package/dist/upstream-t3code/packages/contracts/src/editor.d.ts +0 -124
  291. package/dist/upstream-t3code/packages/contracts/src/environment.d.ts +0 -64
  292. package/dist/upstream-t3code/packages/contracts/src/environmentHttp.d.ts +0 -762
  293. package/dist/upstream-t3code/packages/contracts/src/filesystem.d.ts +0 -26
  294. package/dist/upstream-t3code/packages/contracts/src/git.d.ts +0 -500
  295. package/dist/upstream-t3code/packages/contracts/src/index.d.ts +0 -25
  296. package/dist/upstream-t3code/packages/contracts/src/ipc.d.ts +0 -490
  297. package/dist/upstream-t3code/packages/contracts/src/keybindings.d.ts +0 -133
  298. package/dist/upstream-t3code/packages/contracts/src/model.d.ts +0 -112
  299. package/dist/upstream-t3code/packages/contracts/src/orchestration.d.ts +0 -6682
  300. package/dist/upstream-t3code/packages/contracts/src/project.d.ts +0 -45
  301. package/dist/upstream-t3code/packages/contracts/src/provider.d.ts +0 -116
  302. package/dist/upstream-t3code/packages/contracts/src/providerInstance.d.ts +0 -99
  303. package/dist/upstream-t3code/packages/contracts/src/providerRuntime.d.ts +0 -4276
  304. package/dist/upstream-t3code/packages/contracts/src/relay.d.ts +0 -1262
  305. package/dist/upstream-t3code/packages/contracts/src/relayClient.d.ts +0 -48
  306. package/dist/upstream-t3code/packages/contracts/src/remoteAccess.d.ts +0 -45
  307. package/dist/upstream-t3code/packages/contracts/src/review.d.ts +0 -37
  308. package/dist/upstream-t3code/packages/contracts/src/rpc.d.ts +0 -8218
  309. package/dist/upstream-t3code/packages/contracts/src/server.d.ts +0 -2291
  310. package/dist/upstream-t3code/packages/contracts/src/settings.d.ts +0 -271
  311. package/dist/upstream-t3code/packages/contracts/src/sourceControl.d.ts +0 -177
  312. package/dist/upstream-t3code/packages/contracts/src/terminal.d.ts +0 -330
  313. package/dist/upstream-t3code/packages/contracts/src/vcs.d.ts +0 -148
  314. package/src/cli/auth-format.ts +0 -45
  315. package/src/cli/confirm.ts +0 -31
  316. package/src/cli/model-format.ts +0 -18
  317. package/src/cli/output-format.ts +0 -45
  318. package/src/cli/project-format.ts +0 -18
  319. package/src/cli/terminal/commands.test.ts +0 -101
  320. package/src/cli/terminal-format.ts +0 -167
  321. package/src/cli/thread-format.ts +0 -128
  322. package/src/config/layer.ts +0 -105
  323. package/src/config/service.ts +0 -26
  324. package/src/environment/layer.ts +0 -12
  325. package/src/environment/service.ts +0 -13
  326. package/src/layout/index.ts +0 -1
  327. /package/dist/src/{scope → cli/scope}/index.d.ts +0 -0
  328. /package/dist/src/config/{url.d.ts → url/url.d.ts} +0 -0
  329. /package/src/{scope → cli/scope}/index.ts +0 -0
  330. /package/src/config/{url.ts → url/url.ts} +0 -0
@@ -3,16 +3,18 @@ import * as Option from "effect/Option";
3
3
  import * as Stream from "effect/Stream";
4
4
  import { Argument, Command, Flag } from "effect/unstable/cli";
5
5
 
6
+ import { extraArgsConfig } from "../extra-args.ts";
6
7
  import { modelFlags, projectFlag, threadFormatFlag, worktreeFlag } from "../flags.ts";
7
8
  import { readInitialMessage } from "../message-input.ts";
8
9
  import { buildModelOptions } from "../model-options.ts";
9
10
  import { requireCommandProjectRef } from "../require.ts";
10
- import { resolveWorktreePath } from "../../scope/index.ts";
11
- import { formatThreadStartedHuman } from "../thread-format.ts";
11
+ import { resolveWorktreePath } from "../scope/index.ts";
12
+ import { formatThreadStartedHuman } from "../format/thread.ts";
12
13
  import { T3Application } from "../../application/service.ts";
13
- import { Environment } from "../../environment/service.ts";
14
+ import { CliRuntime } from "../../cli/runtime/service.ts";
15
+ import { loadT3CliEnv } from "../../config/env/env.ts";
14
16
  import { T3Input } from "../input/service.ts";
15
- import { canRenderLiveTerminal, resolveOutputFormat } from "../output-format.ts";
17
+ import { canRenderLiveTerminal, resolveOutputFormat } from "../format/output.ts";
16
18
  import { T3Output } from "../output/service.ts";
17
19
  import { printWaitEventsHuman, printWaitEventsNdjson } from "../wait-events.ts";
18
20
 
@@ -29,6 +31,7 @@ export const startThreadCommand = Command.make(
29
31
  ...modelFlags,
30
32
  wait: Flag.boolean("wait"),
31
33
  format: threadFormatFlag,
34
+ ...extraArgsConfig,
32
35
  },
33
36
  ({
34
37
  project,
@@ -64,16 +67,13 @@ export const startThreadCommand = Command.make(
64
67
  thinking,
65
68
  });
66
69
  const application = yield* T3Application;
67
- const environment = yield* Environment;
70
+ const cliRuntime = yield* CliRuntime;
71
+ const t3CliEnv = yield* loadT3CliEnv;
68
72
  const output = yield* T3Output;
69
- const projectRef = yield* requireCommandProjectRef({
70
- project,
71
- env: environment.env,
72
- cwd: environment.cwd,
73
- });
73
+ const projectRef = yield* requireCommandProjectRef({ project });
74
74
  const worktreePath = resolveWorktreePath({
75
75
  value: Option.getOrUndefined(worktree),
76
- env: environment.env,
76
+ scope: t3CliEnv.scope,
77
77
  });
78
78
  const input = {
79
79
  message: text,
@@ -86,7 +86,12 @@ export const startThreadCommand = Command.make(
86
86
  ...(modelValue !== undefined && modelValue.length > 0 ? { model: modelValue } : {}),
87
87
  ...(options.length > 0 ? { options } : {}),
88
88
  };
89
- const resolvedFormat = resolveOutputFormat(format, environment, wait ? "ndjson" : "json");
89
+ const resolvedFormat = resolveOutputFormat(
90
+ format,
91
+ cliRuntime,
92
+ t3CliEnv,
93
+ wait ? "ndjson" : "json",
94
+ );
90
95
 
91
96
  if (resolvedFormat === "ndjson") {
92
97
  const started = yield* application.startThread(input, {
@@ -117,7 +122,7 @@ export const startThreadCommand = Command.make(
117
122
  }
118
123
  yield* printWaitEventsHuman(output, application.watchThread(started.threadId), {
119
124
  threadId: started.threadId,
120
- live: canRenderLiveTerminal(environment),
125
+ live: canRenderLiveTerminal(cliRuntime, t3CliEnv),
121
126
  });
122
127
  return;
123
128
  }
@@ -2,12 +2,14 @@ import * as Effect from "effect/Effect";
2
2
  import * as Option from "effect/Option";
3
3
  import { Command } from "effect/unstable/cli";
4
4
 
5
+ import { extraArgsConfig } from "../extra-args.ts";
5
6
  import { formatFlag, threadFlag } from "../flags.ts";
6
7
  import { MissingThreadError } from "../error.ts";
7
- import { resolveThreadId } from "../../scope/index.ts";
8
+ import { resolveThreadId } from "../scope/index.ts";
8
9
  import { T3Application } from "../../application/service.ts";
9
- import { Environment } from "../../environment/service.ts";
10
- import { resolveOutputFormat } from "../output-format.ts";
10
+ import { CliRuntime } from "../../cli/runtime/service.ts";
11
+ import { loadT3CliEnv } from "../../config/env/env.ts";
12
+ import { resolveOutputFormat } from "../format/output.ts";
11
13
  import { T3Output } from "../output/service.ts";
12
14
 
13
15
  export const unarchiveThreadCommand = Command.make(
@@ -15,15 +17,17 @@ export const unarchiveThreadCommand = Command.make(
15
17
  {
16
18
  thread: threadFlag,
17
19
  format: formatFlag,
20
+ ...extraArgsConfig,
18
21
  },
19
22
  ({ thread, format }) =>
20
23
  Effect.gen(function* () {
21
24
  const application = yield* T3Application;
22
- const environment = yield* Environment;
25
+ const cliRuntime = yield* CliRuntime;
26
+ const t3CliEnv = yield* loadT3CliEnv;
23
27
  const output = yield* T3Output;
24
28
  const threadId = resolveThreadId({
25
29
  value: Option.getOrUndefined(thread),
26
- env: environment.env,
30
+ scope: t3CliEnv.scope,
27
31
  });
28
32
  if (threadId === undefined) {
29
33
  return yield* Effect.fail(
@@ -32,13 +36,13 @@ export const unarchiveThreadCommand = Command.make(
32
36
  }),
33
37
  );
34
38
  }
35
- const resolvedFormat = resolveOutputFormat(format, environment, "json");
39
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
36
40
  const dispatch = yield* application.unarchiveThread(threadId);
37
41
  if (resolvedFormat === "json") {
38
42
  return yield* output.printJson(dispatch);
39
43
  }
40
44
  return yield* output.printInfo(
41
- `thread unarchived: ${threadId}\nsequence: ${dispatch.sequence}`,
45
+ `thread unarchived: ${threadId} (sequence ${dispatch.sequence})`,
42
46
  );
43
47
  }),
44
48
  ).pipe(Command.withDescription("unarchive thread"));
@@ -2,18 +2,20 @@ import * as Effect from "effect/Effect";
2
2
  import * as Option from "effect/Option";
3
3
  import { Command, Flag } from "effect/unstable/cli";
4
4
 
5
+ import { extraArgsConfig } from "../extra-args.ts";
5
6
  import { formatFlag, modelFlags, selfActionForceFlag, threadFlag } from "../flags.ts";
6
7
  import {
7
8
  ConflictingUpdateFlagsError,
8
9
  MissingThreadError,
9
10
  MissingUpdateFieldsError,
10
11
  } from "../error.ts";
11
- import { requireSelfActionConfirmation } from "../self-action.ts";
12
+ import { requireSelfActionConfirmation } from "../interaction/self-action.ts";
12
13
  import { buildModelOptions } from "../model-options.ts";
13
- import { resolveThreadId } from "../../scope/index.ts";
14
+ import { resolveThreadId } from "../scope/index.ts";
14
15
  import { T3Application } from "../../application/service.ts";
15
- import { Environment } from "../../environment/service.ts";
16
- import { resolveOutputFormat } from "../output-format.ts";
16
+ import { CliRuntime } from "../../cli/runtime/service.ts";
17
+ import { loadT3CliEnv } from "../../config/env/env.ts";
18
+ import { resolveOutputFormat } from "../format/output.ts";
17
19
  import { T3Output } from "../output/service.ts";
18
20
 
19
21
  export const updateThreadCommand = Command.make(
@@ -30,6 +32,7 @@ export const updateThreadCommand = Command.make(
30
32
  worktree: Flag.string("worktree").pipe(Flag.optional),
31
33
  clearWorktree: Flag.boolean("clear-worktree").pipe(Flag.optional),
32
34
  format: formatFlag,
35
+ ...extraArgsConfig,
33
36
  },
34
37
  ({
35
38
  thread,
@@ -50,11 +53,12 @@ export const updateThreadCommand = Command.make(
50
53
  }) =>
51
54
  Effect.gen(function* () {
52
55
  const application = yield* T3Application;
53
- const environment = yield* Environment;
56
+ const cliRuntime = yield* CliRuntime;
57
+ const t3CliEnv = yield* loadT3CliEnv;
54
58
  const output = yield* T3Output;
55
59
  const threadId = resolveThreadId({
56
60
  value: Option.getOrUndefined(thread),
57
- env: environment.env,
61
+ scope: t3CliEnv.scope,
58
62
  });
59
63
  if (threadId === undefined) {
60
64
  return yield* Effect.fail(
@@ -67,7 +71,8 @@ export const updateThreadCommand = Command.make(
67
71
  yield* requireSelfActionConfirmation({
68
72
  threadId,
69
73
  force,
70
- environment,
74
+ cliRuntime,
75
+ t3CliEnv,
71
76
  action: "update",
72
77
  });
73
78
 
@@ -141,10 +146,10 @@ export const updateThreadCommand = Command.make(
141
146
  ? { worktreePath: worktreeValue }
142
147
  : {}),
143
148
  });
144
- const resolvedFormat = resolveOutputFormat(format, environment, "json");
149
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
145
150
  if (resolvedFormat === "json") {
146
151
  return yield* output.printJson(dispatch);
147
152
  }
148
- return yield* output.printInfo(`thread updated: ${threadId}\nsequence: ${dispatch.sequence}`);
153
+ return yield* output.printInfo(`thread updated: ${threadId} (sequence ${dispatch.sequence})`);
149
154
  }),
150
155
  ).pipe(Command.withDescription("update thread metadata"));
@@ -2,12 +2,14 @@ import * as Effect from "effect/Effect";
2
2
  import * as Option from "effect/Option";
3
3
  import { Command } from "effect/unstable/cli";
4
4
 
5
+ import { extraArgsConfig } from "../extra-args.ts";
5
6
  import { threadFlag, waitFormatFlag } from "../flags.ts";
6
7
  import { MissingThreadError } from "../error.ts";
7
- import { resolveThreadId } from "../../scope/index.ts";
8
- import { Environment } from "../../environment/service.ts";
8
+ import { resolveThreadId } from "../scope/index.ts";
9
+ import { CliRuntime } from "../../cli/runtime/service.ts";
10
+ import { loadT3CliEnv } from "../../config/env/env.ts";
9
11
  import { T3Application } from "../../application/service.ts";
10
- import { canRenderLiveTerminal, resolveOutputFormat } from "../output-format.ts";
12
+ import { canRenderLiveTerminal, resolveOutputFormat } from "../format/output.ts";
11
13
  import { T3Output } from "../output/service.ts";
12
14
  import { printWaitEventsHuman, printWaitEventsNdjson } from "../wait-events.ts";
13
15
 
@@ -16,15 +18,17 @@ export const waitForThreadCommand = Command.make(
16
18
  {
17
19
  thread: threadFlag,
18
20
  format: waitFormatFlag,
21
+ ...extraArgsConfig,
19
22
  },
20
23
  ({ thread, format }) =>
21
24
  Effect.gen(function* () {
22
25
  const application = yield* T3Application;
23
- const environment = yield* Environment;
26
+ const cliRuntime = yield* CliRuntime;
27
+ const t3CliEnv = yield* loadT3CliEnv;
24
28
  const output = yield* T3Output;
25
29
  const threadId = resolveThreadId({
26
30
  value: Option.getOrUndefined(thread),
27
- env: environment.env,
31
+ scope: t3CliEnv.scope,
28
32
  });
29
33
  if (threadId === undefined) {
30
34
  return yield* Effect.fail(
@@ -33,13 +37,13 @@ export const waitForThreadCommand = Command.make(
33
37
  }),
34
38
  );
35
39
  }
36
- const resolvedFormat = resolveOutputFormat(format, environment, "ndjson");
40
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "ndjson");
37
41
  if (resolvedFormat === "ndjson") {
38
42
  return yield* printWaitEventsNdjson(output, application.watchThread(threadId));
39
43
  }
40
44
  return yield* printWaitEventsHuman(output, application.watchThread(threadId), {
41
45
  threadId,
42
- live: canRenderLiveTerminal(environment),
46
+ live: canRenderLiveTerminal(cliRuntime, t3CliEnv),
43
47
  });
44
48
  }),
45
49
  ).pipe(Command.withDescription("wait for thread to pause"));
@@ -8,7 +8,7 @@ import type { WaitEvent } from "../application/service.ts";
8
8
  import { ThreadSessionError } from "../domain/error.ts";
9
9
  import { latestAssistantMessage } from "../domain/thread-lifecycle.ts";
10
10
  import type { T3Output } from "./output/service.ts";
11
- import { formatWaitDoneHuman, formatWaitEventNdjson } from "./thread-format.ts";
11
+ import { formatWaitDoneHuman, formatWaitEventNdjson } from "./format/thread.ts";
12
12
 
13
13
  export function printWaitEventsNdjson(
14
14
  output: T3Output["Service"],
@@ -0,0 +1,260 @@
1
+ import * as Context from "effect/Context";
2
+ import * as ConfigProvider from "effect/ConfigProvider";
3
+ import * as Effect from "effect/Effect";
4
+ import * as FileSystem from "effect/FileSystem";
5
+ import * as Layer from "effect/Layer";
6
+ import * as Option from "effect/Option";
7
+ import * as Path from "effect/Path";
8
+
9
+ import { credentialTokenFromEnv } from "./credential/env.ts";
10
+ import { T3CredentialCrypto } from "./credential/service.ts";
11
+ import { loadT3CliEnv } from "./env/env.ts";
12
+ import { validateEnvironmentName } from "./environment-name/name.ts";
13
+ import { ConfigError } from "./error.ts";
14
+ import { readEncryptedConfigFile, writeEncryptedConfigFile } from "./persist/persist.ts";
15
+ import {
16
+ buildResolvedConfigFromEnv,
17
+ buildResolvedConfigFromStored,
18
+ resolveDefaultForUpsert,
19
+ selectEnvironmentName,
20
+ summarizeEnvironments,
21
+ validateCredentialEnvVars,
22
+ } from "./resolve/resolve.ts";
23
+ import { T3ConfigSelection } from "./selection/service.ts";
24
+ import type { EnvironmentSummary, ResolvedConfig, UpsertEnvironmentInput } from "./types.ts";
25
+ import { normalizeHttpBaseUrl } from "./url/url.ts";
26
+
27
+ export class T3Config extends Context.Service<
28
+ T3Config,
29
+ {
30
+ readonly resolve: () => Effect.Effect<ResolvedConfig, ConfigError>;
31
+ readonly resolveActiveEnvironmentName: () => Effect.Effect<string | undefined, ConfigError>;
32
+ readonly listEnvironments: () => Effect.Effect<readonly EnvironmentSummary[], ConfigError>;
33
+ readonly upsertEnvironment: (input: UpsertEnvironmentInput) => Effect.Effect<void, ConfigError>;
34
+ readonly setDefaultEnvironment: (name: string) => Effect.Effect<void, ConfigError>;
35
+ readonly removeEnvironment: (name: string) => Effect.Effect<void, ConfigError>;
36
+ readonly hasEnvironment: (name: string) => Effect.Effect<boolean, ConfigError>;
37
+ readonly getDefaultEnvironmentName: () => Effect.Effect<string | undefined, ConfigError>;
38
+ }
39
+ >()("t3cli/T3Config") {}
40
+
41
+ export const make = Effect.fn("makeT3Config")(function* () {
42
+ const configSelection = yield* T3ConfigSelection;
43
+ const credentialCrypto = yield* T3CredentialCrypto;
44
+ const t3CliEnv = yield* loadT3CliEnv;
45
+ const fs = yield* FileSystem.FileSystem;
46
+ const path = yield* Path.Path;
47
+ const configProvider = yield* ConfigProvider.ConfigProvider;
48
+ const services = Layer.mergeAll(
49
+ Layer.succeed(FileSystem.FileSystem, fs),
50
+ Layer.succeed(Path.Path, path),
51
+ Layer.succeed(T3CredentialCrypto, credentialCrypto),
52
+ Layer.succeed(ConfigProvider.ConfigProvider, configProvider),
53
+ );
54
+
55
+ const hasEnvironment = Effect.fn("T3Config.hasEnvironment")(function* (name: string) {
56
+ const encrypted = yield* Effect.provide(readEncryptedConfigFile(), services);
57
+ return encrypted.environments[name] !== undefined;
58
+ });
59
+
60
+ const getDefaultEnvironmentName = Effect.fn("T3Config.getDefaultEnvironmentName")(function* () {
61
+ const encrypted = yield* Effect.provide(readEncryptedConfigFile(), services);
62
+ const defaultName = encrypted.default;
63
+ return defaultName !== undefined && defaultName.length > 0 ? defaultName : undefined;
64
+ });
65
+
66
+ const resolveActiveEnvironmentName = Effect.fn("T3Config.resolveActiveEnvironmentName")(
67
+ function* () {
68
+ const encrypted = yield* Effect.provide(readEncryptedConfigFile(), services);
69
+ const envUrl = Option.getOrUndefined(t3CliEnv.t3codeUrl);
70
+ const envToken = Option.getOrUndefined(credentialTokenFromEnv(t3CliEnv));
71
+ yield* validateCredentialEnvVars({ envUrl, envToken });
72
+ const hasEnvCredentials = envUrl !== undefined && envToken !== undefined;
73
+ if (hasEnvCredentials) {
74
+ return undefined;
75
+ }
76
+ const configuredEnvironment = yield* configSelection.getSelectedEnvironment();
77
+ const selectedName = selectEnvironmentName({
78
+ selectedEnvironment: configuredEnvironment,
79
+ defaultEnvironment: encrypted.default,
80
+ });
81
+ if (selectedName === undefined || selectedName.length === 0) {
82
+ return undefined;
83
+ }
84
+ if (encrypted.environments[selectedName] === undefined) {
85
+ return undefined;
86
+ }
87
+ return selectedName;
88
+ },
89
+ );
90
+
91
+ const listEnvironments = Effect.fn("T3Config.listEnvironments")(function* () {
92
+ const encrypted = yield* Effect.provide(readEncryptedConfigFile(), services);
93
+ return summarizeEnvironments(encrypted);
94
+ });
95
+
96
+ const upsertEnvironment = Effect.fn("T3Config.upsertEnvironment")(function* (
97
+ input: UpsertEnvironmentInput,
98
+ ) {
99
+ yield* validateEnvironmentName(input.name);
100
+ const normalizedUrl = yield* normalizeHttpBaseUrl(input.url).pipe(
101
+ Effect.mapError((error) => new ConfigError({ message: error.message, cause: error.cause })),
102
+ );
103
+ const encrypted = yield* Effect.provide(readEncryptedConfigFile(), services);
104
+ const token = yield* credentialCrypto.encrypt({
105
+ environmentName: input.name,
106
+ url: normalizedUrl,
107
+ local: input.local,
108
+ token: input.token,
109
+ });
110
+ const defaultName = resolveDefaultForUpsert(encrypted, input.name, input.makeDefault);
111
+ yield* Effect.provide(
112
+ writeEncryptedConfigFile({
113
+ version: 2,
114
+ ...(defaultName !== undefined ? { default: defaultName } : {}),
115
+ environments: {
116
+ ...encrypted.environments,
117
+ [input.name]: {
118
+ url: normalizedUrl,
119
+ local: input.local,
120
+ token,
121
+ },
122
+ },
123
+ }),
124
+ services,
125
+ );
126
+ });
127
+
128
+ const setDefaultEnvironment = Effect.fn("T3Config.setDefaultEnvironment")(function* (
129
+ name: string,
130
+ ) {
131
+ yield* validateEnvironmentName(name);
132
+ const encrypted = yield* Effect.provide(readEncryptedConfigFile(), services);
133
+ if (encrypted.environments[name] === undefined) {
134
+ return yield* Effect.fail(new ConfigError({ message: `environment not found: ${name}` }));
135
+ }
136
+ const selectedEnvironment = encrypted.environments[name];
137
+ yield* credentialCrypto
138
+ .decrypt({
139
+ environmentName: name,
140
+ url: selectedEnvironment.url,
141
+ local: selectedEnvironment.local,
142
+ token: selectedEnvironment.token,
143
+ })
144
+ .pipe(
145
+ Effect.mapError(
146
+ (error) =>
147
+ new ConfigError({
148
+ message: `failed to decrypt credentials for environment '${name}'`,
149
+ cause: error.cause,
150
+ }),
151
+ ),
152
+ Effect.asVoid,
153
+ );
154
+ yield* Effect.provide(
155
+ writeEncryptedConfigFile({
156
+ ...encrypted,
157
+ default: name,
158
+ }),
159
+ services,
160
+ );
161
+ return yield* Effect.void;
162
+ });
163
+
164
+ const removeEnvironment = Effect.fn("T3Config.removeEnvironment")(function* (name: string) {
165
+ yield* validateEnvironmentName(name);
166
+ const encrypted = yield* Effect.provide(readEncryptedConfigFile(), services);
167
+ if (encrypted.environments[name] === undefined) {
168
+ return yield* Effect.fail(new ConfigError({ message: `environment not found: ${name}` }));
169
+ }
170
+ const { [name]: _removed, ...environments } = encrypted.environments;
171
+ const defaultName = encrypted.default === name ? undefined : encrypted.default;
172
+ yield* Effect.provide(
173
+ writeEncryptedConfigFile({
174
+ version: 2,
175
+ ...(defaultName !== undefined ? { default: defaultName } : {}),
176
+ environments,
177
+ }),
178
+ services,
179
+ );
180
+ return yield* Effect.void;
181
+ });
182
+
183
+ const resolve = Effect.fn("T3Config.resolve")(function* () {
184
+ const encrypted = yield* Effect.provide(readEncryptedConfigFile(), services);
185
+ const envUrl = Option.getOrUndefined(t3CliEnv.t3codeUrl);
186
+ const envToken = Option.getOrUndefined(credentialTokenFromEnv(t3CliEnv));
187
+ yield* validateCredentialEnvVars({ envUrl, envToken });
188
+ const hasEnvUrl = envUrl !== undefined;
189
+ const configuredEnvironment = yield* configSelection.getSelectedEnvironment();
190
+ const selectedName = selectEnvironmentName({
191
+ selectedEnvironment: configuredEnvironment,
192
+ defaultEnvironment: encrypted.default,
193
+ });
194
+
195
+ if (hasEnvUrl && envToken !== undefined) {
196
+ if (selectedName !== undefined && selectedName.length > 0) {
197
+ yield* validateEnvironmentName(selectedName);
198
+ if (encrypted.environments[selectedName] === undefined) {
199
+ return yield* Effect.fail(
200
+ new ConfigError({ message: `environment not found: ${selectedName}` }),
201
+ );
202
+ }
203
+ }
204
+ return yield* buildResolvedConfigFromEnv({
205
+ envUrl,
206
+ envToken,
207
+ });
208
+ }
209
+
210
+ if (selectedName === undefined || selectedName.length === 0) {
211
+ return yield* Effect.fail(
212
+ new ConfigError({
213
+ message: "not authenticated. run: t3cli auth pair --url <pairing-url>",
214
+ }),
215
+ );
216
+ }
217
+ yield* validateEnvironmentName(selectedName);
218
+ if (encrypted.environments[selectedName] === undefined) {
219
+ return yield* Effect.fail(
220
+ new ConfigError({ message: `environment not found: ${selectedName}` }),
221
+ );
222
+ }
223
+
224
+ const selectedEnvironment = encrypted.environments[selectedName];
225
+ const token = yield* credentialCrypto
226
+ .decrypt({
227
+ environmentName: selectedName,
228
+ url: selectedEnvironment.url,
229
+ local: selectedEnvironment.local,
230
+ token: selectedEnvironment.token,
231
+ })
232
+ .pipe(
233
+ Effect.mapError(
234
+ (error) =>
235
+ new ConfigError({
236
+ message: `failed to decrypt credentials for environment '${selectedName}'`,
237
+ cause: error.cause,
238
+ }),
239
+ ),
240
+ );
241
+ return yield* buildResolvedConfigFromStored({
242
+ selectedName,
243
+ token,
244
+ encrypted,
245
+ });
246
+ });
247
+
248
+ return {
249
+ resolve,
250
+ resolveActiveEnvironmentName,
251
+ listEnvironments,
252
+ upsertEnvironment,
253
+ setDefaultEnvironment,
254
+ removeEnvironment,
255
+ hasEnvironment,
256
+ getDefaultEnvironmentName,
257
+ } as const;
258
+ });
259
+
260
+ export const layer = Layer.effect(T3Config, make());
@@ -0,0 +1,40 @@
1
+ import { createCipheriv, createDecipheriv } from "node:crypto";
2
+
3
+ import * as Effect from "effect/Effect";
4
+ import * as Layer from "effect/Layer";
5
+
6
+ import { credentialCipherTagByteLength, T3CredentialCipher } from "./cipher.ts";
7
+ import { CredentialCipherError } from "./error.ts";
8
+
9
+ const make = (): T3CredentialCipher["Service"] => ({
10
+ encrypt: (input) =>
11
+ Effect.try({
12
+ try: () => {
13
+ const cipher = createCipheriv("aes-256-gcm", input.key, input.nonce, {
14
+ authTagLength: credentialCipherTagByteLength,
15
+ });
16
+ cipher.setAAD(input.additionalData);
17
+ const ciphertext = Buffer.concat([cipher.update(input.plaintext), cipher.final()]);
18
+ return {
19
+ ciphertext: new Uint8Array(ciphertext),
20
+ tag: new Uint8Array(cipher.getAuthTag()),
21
+ };
22
+ },
23
+ catch: (cause) => new CredentialCipherError({ operation: "encrypt", cause }),
24
+ }),
25
+ decrypt: (input) =>
26
+ Effect.try({
27
+ try: () => {
28
+ const decipher = createDecipheriv("aes-256-gcm", input.key, input.nonce, {
29
+ authTagLength: credentialCipherTagByteLength,
30
+ });
31
+ decipher.setAAD(input.additionalData);
32
+ decipher.setAuthTag(input.tag);
33
+ const plaintext = Buffer.concat([decipher.update(input.ciphertext), decipher.final()]);
34
+ return new Uint8Array(plaintext);
35
+ },
36
+ catch: (cause) => new CredentialCipherError({ operation: "decrypt", cause }),
37
+ }),
38
+ });
39
+
40
+ export const layerNode = Layer.succeed(T3CredentialCipher, make());
@@ -0,0 +1,82 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+
4
+ import { credentialCipherTagByteLength, T3CredentialCipher } from "./cipher.ts";
5
+ import { CredentialCipherError } from "./error.ts";
6
+
7
+ const aesGcmAlgorithm = "AES-GCM";
8
+
9
+ function copyBytes(bytes: Uint8Array) {
10
+ const copy = new Uint8Array(bytes.byteLength);
11
+ copy.set(bytes);
12
+ return copy;
13
+ }
14
+
15
+ function importAesGcmKey(key: Uint8Array, operation: "encrypt" | "decrypt") {
16
+ return Effect.tryPromise({
17
+ try: () =>
18
+ globalThis.crypto.subtle.importKey("raw", copyBytes(key), { name: aesGcmAlgorithm }, false, [
19
+ "encrypt",
20
+ "decrypt",
21
+ ]),
22
+ catch: (cause) => new CredentialCipherError({ operation, cause }),
23
+ });
24
+ }
25
+
26
+ const make = (): T3CredentialCipher["Service"] => ({
27
+ encrypt: (input) =>
28
+ Effect.gen(function* () {
29
+ const cryptoKey = yield* importAesGcmKey(input.key, "encrypt");
30
+ const encrypted = yield* Effect.tryPromise({
31
+ try: () =>
32
+ globalThis.crypto.subtle.encrypt(
33
+ {
34
+ name: aesGcmAlgorithm,
35
+ iv: copyBytes(input.nonce),
36
+ additionalData: copyBytes(input.additionalData),
37
+ tagLength: credentialCipherTagByteLength * 8,
38
+ },
39
+ cryptoKey,
40
+ copyBytes(input.plaintext),
41
+ ),
42
+ catch: (cause) => new CredentialCipherError({ operation: "encrypt", cause }),
43
+ });
44
+ const bytes = new Uint8Array(encrypted);
45
+ if (bytes.byteLength < credentialCipherTagByteLength) {
46
+ return yield* Effect.fail(
47
+ new CredentialCipherError({
48
+ operation: "encrypt",
49
+ cause: new Error("encrypted output shorter than auth tag"),
50
+ }),
51
+ );
52
+ }
53
+ return {
54
+ ciphertext: bytes.slice(0, -credentialCipherTagByteLength),
55
+ tag: bytes.slice(-credentialCipherTagByteLength),
56
+ };
57
+ }),
58
+ decrypt: (input) =>
59
+ Effect.gen(function* () {
60
+ const cryptoKey = yield* importAesGcmKey(input.key, "decrypt");
61
+ const combined = new Uint8Array(input.ciphertext.byteLength + input.tag.byteLength);
62
+ combined.set(input.ciphertext);
63
+ combined.set(input.tag, input.ciphertext.byteLength);
64
+ const plaintext = yield* Effect.tryPromise({
65
+ try: () =>
66
+ globalThis.crypto.subtle.decrypt(
67
+ {
68
+ name: aesGcmAlgorithm,
69
+ iv: copyBytes(input.nonce),
70
+ additionalData: copyBytes(input.additionalData),
71
+ tagLength: credentialCipherTagByteLength * 8,
72
+ },
73
+ cryptoKey,
74
+ combined,
75
+ ),
76
+ catch: (cause) => new CredentialCipherError({ operation: "decrypt", cause }),
77
+ });
78
+ return new Uint8Array(plaintext);
79
+ }),
80
+ });
81
+
82
+ export const layerWeb = Layer.succeed(T3CredentialCipher, make());