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
package/src/cli/auth.ts CHANGED
@@ -8,14 +8,49 @@ import {
8
8
  formatAuthPaired,
9
9
  formatAuthStatusHuman,
10
10
  formatAuthStatusJson,
11
- } from "./auth-format.ts";
11
+ } from "./format/auth.ts";
12
12
  import { T3Auth } from "../auth/service.ts";
13
- import { T3Config } from "../config/service.ts";
14
- import { Environment } from "../environment/service.ts";
15
- import { formatFlag } from "./flags.ts";
16
- import { resolveOutputFormat } from "./output-format.ts";
13
+ import { extraArgsConfig } from "./extra-args.ts";
14
+ import { CliRuntime } from "../cli/runtime/service.ts";
15
+ import { loadT3CliEnv } from "../config/env/env.ts";
16
+ import { requireEnvironmentReplaceConfirmation } from "./interaction/confirm.ts";
17
+ import { envNameFlag, formatFlag, replaceFlag } from "./flags.ts";
18
+ import { resolveOutputFormat } from "./format/output.ts";
17
19
  import { T3Output } from "./output/service.ts";
18
20
 
21
+ const persistAuthEnvironment = Effect.fn("persistAuthEnvironment")(function* (input: {
22
+ readonly explicitName: Option.Option<string>;
23
+ readonly fallbackName: string;
24
+ readonly url: string;
25
+ readonly token: string;
26
+ readonly local: boolean;
27
+ readonly replace: boolean;
28
+ }) {
29
+ const auth = yield* T3Auth;
30
+ const cliRuntime = yield* CliRuntime;
31
+ const t3CliEnv = yield* loadT3CliEnv;
32
+ const environmentName = Option.isSome(input.explicitName)
33
+ ? input.explicitName.value
34
+ : input.fallbackName;
35
+ const exists = yield* auth.environmentExists(environmentName);
36
+ if (exists) {
37
+ yield* requireEnvironmentReplaceConfirmation({
38
+ name: environmentName,
39
+ replace: input.replace,
40
+ cliRuntime,
41
+ t3CliEnv,
42
+ });
43
+ }
44
+ return yield* auth.persistEnvironment({
45
+ name: environmentName,
46
+ url: input.url,
47
+ token: input.token,
48
+ local: input.local,
49
+ replace: input.replace,
50
+ allowReplace: true,
51
+ });
52
+ });
53
+
19
54
  export function createAuthCommand() {
20
55
  return Command.make("auth").pipe(
21
56
  Command.withDescription("auth commands"),
@@ -28,24 +63,33 @@ const pairCommand = Command.make(
28
63
  {
29
64
  url: Flag.string("url"),
30
65
  local: Flag.boolean("local"),
66
+ name: envNameFlag,
67
+ replace: replaceFlag,
31
68
  format: formatFlag,
69
+ ...extraArgsConfig,
32
70
  },
33
- ({ url, local, format }) =>
71
+ ({ url, local, name, replace, format }) =>
34
72
  Effect.gen(function* () {
35
73
  const auth = yield* T3Auth;
36
- const environment = yield* Environment;
74
+ const cliRuntime = yield* CliRuntime;
75
+ const t3CliEnv = yield* loadT3CliEnv;
37
76
  const output = yield* T3Output;
38
- const resolvedFormat = resolveOutputFormat(format, environment, "json");
77
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
39
78
  const result = yield* auth.pair(url);
40
- yield* auth.writeConfig({
79
+ const fallbackName = yield* auth.defaultNameFromUrl(result.url);
80
+ const environmentName = yield* persistAuthEnvironment({
81
+ explicitName: name,
82
+ fallbackName,
41
83
  url: result.url,
42
84
  token: result.token,
43
85
  local,
86
+ replace,
44
87
  });
88
+ const payload = { ...result, name: environmentName };
45
89
  if (resolvedFormat === "json") {
46
- yield* output.printJson(result);
90
+ yield* output.printJson(payload);
47
91
  } else {
48
- yield* output.printInfo(formatAuthPaired(result));
92
+ yield* output.printInfo(formatAuthPaired(payload));
49
93
  }
50
94
  }),
51
95
  ).pipe(Command.withDescription("pair with t3code server"));
@@ -58,14 +102,18 @@ const localCommand = Command.make(
58
102
  role: Flag.choice("role", ["owner", "client"] as const).pipe(Flag.withDefault("owner")),
59
103
  label: Flag.string("label").pipe(Flag.withDefault("t3cli")),
60
104
  subject: Flag.string("subject").pipe(Flag.withDefault("t3cli-local")),
105
+ name: envNameFlag,
106
+ replace: replaceFlag,
61
107
  format: formatFlag,
108
+ ...extraArgsConfig,
62
109
  },
63
- ({ baseDir, origin, role, label, subject, format }) =>
110
+ ({ baseDir, origin, role, label, subject, name, replace, format }) =>
64
111
  Effect.gen(function* () {
65
112
  const auth = yield* T3Auth;
66
- const environment = yield* Environment;
113
+ const cliRuntime = yield* CliRuntime;
114
+ const t3CliEnv = yield* loadT3CliEnv;
67
115
  const output = yield* T3Output;
68
- const resolvedFormat = resolveOutputFormat(format, environment, "json");
116
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
69
117
  const result = yield* auth.local({
70
118
  role,
71
119
  label,
@@ -73,15 +121,20 @@ const localCommand = Command.make(
73
121
  ...(Option.isSome(baseDir) ? { baseDir: baseDir.value } : {}),
74
122
  ...(Option.isSome(origin) ? { origin: origin.value } : {}),
75
123
  });
76
- yield* auth.writeConfig({
124
+ const fallbackName = yield* auth.defaultNameForLocal();
125
+ const environmentName = yield* persistAuthEnvironment({
126
+ explicitName: name,
127
+ fallbackName,
77
128
  url: result.url,
78
129
  token: result.token,
79
130
  local: true,
131
+ replace,
80
132
  });
133
+ const payload = { ...result, name: environmentName };
81
134
  if (resolvedFormat === "json") {
82
- yield* output.printJson(formatAuthLocalJson(result));
135
+ yield* output.printJson(formatAuthLocalJson(payload));
83
136
  } else {
84
- yield* output.printInfo(formatAuthLocalHuman(result));
137
+ yield* output.printInfo(formatAuthLocalHuman(payload));
85
138
  }
86
139
  }),
87
140
  ).pipe(Command.withDescription("authenticate with local t3code installation"));
@@ -90,20 +143,20 @@ const statusCommand = Command.make(
90
143
  "status",
91
144
  {
92
145
  format: formatFlag,
146
+ ...extraArgsConfig,
93
147
  },
94
148
  ({ format }) =>
95
149
  Effect.gen(function* () {
96
- const configService = yield* T3Config;
97
150
  const auth = yield* T3Auth;
98
- const environment = yield* Environment;
151
+ const cliRuntime = yield* CliRuntime;
152
+ const t3CliEnv = yield* loadT3CliEnv;
99
153
  const output = yield* T3Output;
100
- const resolvedFormat = resolveOutputFormat(format, environment, "json");
101
- const config = yield* configService.resolve();
154
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
102
155
  const result = yield* auth.status();
103
156
  if (resolvedFormat === "json") {
104
- yield* output.printJson(formatAuthStatusJson({ config, result }));
157
+ yield* output.printJson(formatAuthStatusJson(result));
105
158
  } else {
106
- yield* output.printInfo(formatAuthStatusHuman({ config, result }));
159
+ yield* output.printInfo(formatAuthStatusHuman(result));
107
160
  }
108
161
  }),
109
162
  ).pipe(Command.withDescription("show auth status"));
@@ -0,0 +1,8 @@
1
+ import { Flag, GlobalFlag } from "effect/unstable/cli";
2
+
3
+ export const cliEnvironmentSetting = GlobalFlag.setting("environment")({
4
+ flag: Flag.string("environment").pipe(
5
+ Flag.withDescription("Auth environment name for this command"),
6
+ Flag.optional,
7
+ ),
8
+ });
@@ -0,0 +1,2 @@
1
+ export { cliEnvironmentSetting } from "./flag.ts";
2
+ export * as Selection from "./selection-layer.ts";
@@ -0,0 +1,49 @@
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 { T3Config } from "../../config/config.ts";
7
+ import { ConfigPlatformLayer } from "../../config/platform.test-utils.ts";
8
+ import { makeTempHomeScoped } from "../../config/temp-home.test-utils.ts";
9
+ import { t3ConfigDepsLayer } from "../../config/layer.test-utils.ts";
10
+
11
+ describe("T3ConfigSelectionLive", () => {
12
+ it.effect("resolves T3CLI_ENV through the selection layer", () =>
13
+ Effect.gen(function* () {
14
+ return yield* makeTempHomeScoped("t3cli-selection-");
15
+ }).pipe(
16
+ Effect.flatMap((homeDir) =>
17
+ Effect.gen(function* () {
18
+ const config = yield* T3Config;
19
+ yield* config.upsertEnvironment({
20
+ name: "home",
21
+ url: "https://home.example",
22
+ token: "home-token",
23
+ local: false,
24
+ });
25
+ yield* config.upsertEnvironment({
26
+ name: "work",
27
+ url: "https://work.example",
28
+ token: "work-token",
29
+ local: true,
30
+ });
31
+ const resolved = yield* config.resolve();
32
+ assert.equal(resolved.source, "config");
33
+ if (resolved.source === "config") {
34
+ assert.equal(resolved.environment, "work");
35
+ }
36
+ }).pipe(
37
+ Effect.provide(
38
+ t3ConfigDepsLayer(homeDir, {
39
+ useSelectionLive: true,
40
+ env: { T3CLI_ENV: "work" },
41
+ }),
42
+ ),
43
+ ),
44
+ ),
45
+ Effect.provide(ConfigPlatformLayer),
46
+ Effect.scoped,
47
+ ),
48
+ );
49
+ });
@@ -0,0 +1,27 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+ import * as Option from "effect/Option";
4
+
5
+ import { resolveConfiguredEnvironment } from "../../config/selection/resolve.ts";
6
+ import { T3ConfigSelection } from "../../config/selection/service.ts";
7
+ import { loadT3CliEnv } from "../../config/env/env.ts";
8
+ import { cliEnvironmentSetting } from "./flag.ts";
9
+
10
+ export const layer = Layer.effect(
11
+ T3ConfigSelection,
12
+ Effect.gen(function* () {
13
+ const t3CliEnv = yield* loadT3CliEnv;
14
+ return {
15
+ getSelectedEnvironment: () =>
16
+ Effect.gen(function* () {
17
+ const cliEnvironment = yield* Effect.serviceOption(cliEnvironmentSetting);
18
+ return resolveConfiguredEnvironment({
19
+ cliFlag: Option.isSome(cliEnvironment)
20
+ ? Option.getOrUndefined(cliEnvironment.value)
21
+ : undefined,
22
+ t3cliEnv: Option.getOrUndefined(t3CliEnv.t3cliEnv),
23
+ });
24
+ }),
25
+ };
26
+ }),
27
+ );
package/src/cli/env.ts ADDED
@@ -0,0 +1,107 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Option from "effect/Option";
3
+ import { Argument, Command } from "effect/unstable/cli";
4
+
5
+ import {
6
+ formatAuthListHuman,
7
+ formatAuthListJson,
8
+ formatAuthUnpairHuman,
9
+ formatAuthUseHuman,
10
+ } from "./format/auth.ts";
11
+ import { T3Auth } from "../auth/service.ts";
12
+ import { CliRuntime } from "../cli/runtime/service.ts";
13
+ import { loadT3CliEnv } from "../config/env/env.ts";
14
+ import { requireDestructiveConfirmation } from "./interaction/confirm.ts";
15
+ import { extraArgsConfig } from "./extra-args.ts";
16
+ import { envNameFlag, formatFlag, yesFlag } from "./flags.ts";
17
+ import { resolveOutputFormat } from "./format/output.ts";
18
+ import { T3Output } from "./output/service.ts";
19
+
20
+ export function createEnvCommand() {
21
+ return Command.make("env").pipe(
22
+ Command.withDescription("manage stored environments"),
23
+ Command.withSubcommands([listCommand, useCommand, removeCommand]),
24
+ );
25
+ }
26
+
27
+ const listCommand = Command.make(
28
+ "list",
29
+ {
30
+ format: formatFlag,
31
+ ...extraArgsConfig,
32
+ },
33
+ ({ format }) =>
34
+ Effect.gen(function* () {
35
+ const auth = yield* T3Auth;
36
+ const cliRuntime = yield* CliRuntime;
37
+ const t3CliEnv = yield* loadT3CliEnv;
38
+ const output = yield* T3Output;
39
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
40
+ const environments = yield* auth.listEnvironments();
41
+ if (resolvedFormat === "json") {
42
+ yield* output.printJson(formatAuthListJson(environments));
43
+ } else {
44
+ yield* output.printInfo(formatAuthListHuman(environments));
45
+ }
46
+ }),
47
+ ).pipe(Command.withDescription("list stored environments"));
48
+
49
+ const useCommand = Command.make(
50
+ "use",
51
+ {
52
+ name: Argument.string("name"),
53
+ format: formatFlag,
54
+ ...extraArgsConfig,
55
+ },
56
+ ({ name, format }) =>
57
+ Effect.gen(function* () {
58
+ const auth = yield* T3Auth;
59
+ const cliRuntime = yield* CliRuntime;
60
+ const t3CliEnv = yield* loadT3CliEnv;
61
+ const output = yield* T3Output;
62
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
63
+ const result = yield* auth.useEnvironment(name);
64
+ if (resolvedFormat === "json") {
65
+ yield* output.printJson(result);
66
+ } else {
67
+ yield* output.printInfo(formatAuthUseHuman(result));
68
+ }
69
+ }),
70
+ ).pipe(Command.withDescription("set the default environment"));
71
+
72
+ const removeCommand = Command.make(
73
+ "remove",
74
+ {
75
+ name: envNameFlag,
76
+ yes: yesFlag,
77
+ format: formatFlag,
78
+ ...extraArgsConfig,
79
+ },
80
+ ({ name, yes, format }) =>
81
+ Effect.gen(function* () {
82
+ const auth = yield* T3Auth;
83
+ const cliRuntime = yield* CliRuntime;
84
+ const t3CliEnv = yield* loadT3CliEnv;
85
+ const output = yield* T3Output;
86
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
87
+ const targetName = yield* auth.resolveUnpairTarget(
88
+ Option.isSome(name) ? { name: name.value } : {},
89
+ );
90
+ yield* requireDestructiveConfirmation({
91
+ message: `Remove local credentials for environment '${targetName}'?`,
92
+ yes,
93
+ cliRuntime,
94
+ t3CliEnv,
95
+ });
96
+ const result = yield* auth.unpairEnvironment({ name: targetName });
97
+ if (resolvedFormat === "json") {
98
+ yield* output.printJson(result);
99
+ } else {
100
+ yield* output.printInfo(formatAuthUnpairHuman(result));
101
+ }
102
+ }),
103
+ ).pipe(
104
+ Command.withDescription(
105
+ "remove local credentials for an environment (remote tokens may remain valid until expiry)",
106
+ ),
107
+ );
@@ -0,0 +1,32 @@
1
+ import "vite-plus/test/config";
2
+
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
+ import { assert, describe, it } from "@effect/vitest";
8
+ import { CliError } from "effect/unstable/cli";
9
+
10
+ import { extraArgsConfig } from "./extra-args.ts";
11
+
12
+ describe("extraArgsConfig", () => {
13
+ it("rejects leftover positional arguments", () =>
14
+ Effect.gen(function* () {
15
+ const exit = yield* extraArgsConfig.extraArgs
16
+ .parse({ flags: {}, arguments: ["extra"] })
17
+ .pipe(Effect.exit);
18
+ assert.isTrue(Exit.isFailure(exit));
19
+ if (!Exit.isFailure(exit)) {
20
+ return;
21
+ }
22
+ const error = Cause.findErrorOption(exit.cause);
23
+ assert.isTrue(Option.isSome(error));
24
+ if (Option.isSome(error)) {
25
+ assert.instanceOf(error.value, CliError.InvalidValue);
26
+ assert.equal(
27
+ error.value.message,
28
+ 'Invalid value for argument <extra>: "extra". Expected: no extra arguments',
29
+ );
30
+ }
31
+ }));
32
+ });
@@ -0,0 +1,22 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { Argument, CliError } from "effect/unstable/cli";
3
+
4
+ const rejectExtraArguments = Argument.string("extra").pipe(
5
+ Argument.variadic(),
6
+ Argument.mapEffect((extra) =>
7
+ extra.length === 0
8
+ ? Effect.void
9
+ : Effect.fail(
10
+ new CliError.InvalidValue({
11
+ option: "extra",
12
+ value: extra[0] ?? "",
13
+ expected: "no extra arguments",
14
+ kind: "argument",
15
+ }),
16
+ ),
17
+ ),
18
+ );
19
+
20
+ export const extraArgsConfig = {
21
+ extraArgs: rejectExtraArguments,
22
+ };
package/src/cli/flags.ts CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  humanJsonFormatChoices,
5
5
  humanJsonNdjsonFormatChoices,
6
6
  humanNdjsonFormatChoices,
7
- } from "./output-format.ts";
7
+ } from "./format/output.ts";
8
8
 
9
9
  export const projectFlag = Flag.string("project").pipe(
10
10
  Flag.withDescription(
@@ -35,6 +35,16 @@ export const yesFlag = Flag.boolean("yes").pipe(
35
35
  Flag.withDefault(false),
36
36
  );
37
37
 
38
+ export const replaceFlag = Flag.boolean("replace").pipe(
39
+ Flag.withDescription("Replace an existing environment with the same name"),
40
+ Flag.withDefault(false),
41
+ );
42
+
43
+ export const envNameFlag = Flag.string("name").pipe(
44
+ Flag.withDescription("Environment name"),
45
+ Flag.optional,
46
+ );
47
+
38
48
  export const forceFlag = Flag.boolean("force").pipe(
39
49
  Flag.withDescription("Delete non-empty project (cascade thread deletes)"),
40
50
  Flag.withDefault(false),
@@ -0,0 +1,92 @@
1
+ import type {
2
+ AuthEnvironmentListItem,
3
+ AuthStatusResult,
4
+ LocalAuthResult,
5
+ PairResult,
6
+ } from "../../auth/type.ts";
7
+ import { formatRecord, formatTable } from "./human.ts";
8
+
9
+ export function formatAuthPaired(result: PairResult & { readonly name: string }) {
10
+ return `paired\n${formatRecord([
11
+ { field: "url", value: result.url },
12
+ { field: "name", value: result.name },
13
+ { field: "role", value: result.role },
14
+ { field: "expires", value: result.expiresAt },
15
+ ])}`;
16
+ }
17
+
18
+ export function formatAuthLocalHuman(result: LocalAuthResult & { readonly name: string }) {
19
+ return `paired\n${formatRecord([
20
+ { field: "url", value: result.url },
21
+ { field: "name", value: result.name },
22
+ { field: "role", value: result.role },
23
+ { field: "expires", value: result.expiresAt },
24
+ { field: "base dir", value: result.baseDir },
25
+ ])}`;
26
+ }
27
+
28
+ export function formatAuthLocalJson(result: LocalAuthResult & { readonly name: string }) {
29
+ return result;
30
+ }
31
+
32
+ export function formatAuthStatusHuman(input: AuthStatusResult) {
33
+ return formatRecord([
34
+ ...(input.config.environment !== undefined
35
+ ? [{ field: "environment", value: input.config.environment }]
36
+ : []),
37
+ { field: "url", value: input.config.url },
38
+ { field: "local", value: input.config.local ? "yes" : "no" },
39
+ { field: "source", value: input.config.source },
40
+ { field: "authenticated", value: input.session.authenticated ? "yes" : "no" },
41
+ ...(input.session.role !== undefined ? [{ field: "role", value: input.session.role }] : []),
42
+ ...(input.session.expiresAt !== undefined
43
+ ? [{ field: "expires", value: input.session.expiresAt }]
44
+ : []),
45
+ ]);
46
+ }
47
+
48
+ export function formatAuthStatusJson(input: AuthStatusResult) {
49
+ return {
50
+ ...input.session,
51
+ ...(input.config.environment !== undefined ? { environment: input.config.environment } : {}),
52
+ url: input.config.url,
53
+ source: input.config.source,
54
+ local: input.config.local,
55
+ };
56
+ }
57
+
58
+ export function formatAuthListHuman(environments: readonly AuthEnvironmentListItem[]) {
59
+ if (environments.length === 0) {
60
+ return "no environments";
61
+ }
62
+ return formatTable(
63
+ [
64
+ { header: "name", value: (environment) => environment.name, maxWidth: 24 },
65
+ { header: "url", value: (environment) => environment.url, maxWidth: 72 },
66
+ { header: "local", value: (environment) => (environment.local ? "yes" : "no"), maxWidth: 6 },
67
+ {
68
+ header: "default",
69
+ value: (environment) => (environment.default ? "yes" : "no"),
70
+ maxWidth: 7,
71
+ },
72
+ {
73
+ header: "active",
74
+ value: (environment) => (environment.active ? "yes" : "no"),
75
+ maxWidth: 6,
76
+ },
77
+ ],
78
+ environments,
79
+ );
80
+ }
81
+
82
+ export function formatAuthListJson(environments: readonly AuthEnvironmentListItem[]) {
83
+ return environments;
84
+ }
85
+
86
+ export function formatAuthUseHuman(result: { readonly name: string }) {
87
+ return `default environment: ${result.name}`;
88
+ }
89
+
90
+ export function formatAuthUnpairHuman(result: { readonly name: string }) {
91
+ return `removed environment: ${result.name}`;
92
+ }
@@ -0,0 +1,131 @@
1
+ import { Marked } from "marked";
2
+ import { markedTerminal } from "marked-terminal";
3
+ import stringWidth from "string-width";
4
+ import wrapAnsi from "wrap-ansi";
5
+
6
+ type TableColumn<Row> = {
7
+ readonly header: string;
8
+ readonly value: (row: Row) => string;
9
+ readonly maxWidth?: number;
10
+ };
11
+
12
+ type RecordEntry = {
13
+ readonly field: string;
14
+ readonly value: string;
15
+ };
16
+
17
+ const terminalMarkdown = new Marked(
18
+ markedTerminal({
19
+ reflowText: false,
20
+ showSectionPrefix: true,
21
+ width: 100,
22
+ }),
23
+ );
24
+
25
+ export function formatTable<Row>(
26
+ columns: ReadonlyArray<TableColumn<Row>>,
27
+ rows: ReadonlyArray<Row>,
28
+ ) {
29
+ if (rows.length === 0) {
30
+ return "";
31
+ }
32
+
33
+ const widths = columns.map((column) =>
34
+ Math.min(
35
+ column.maxWidth ?? Number.POSITIVE_INFINITY,
36
+ Math.max(stringWidth(column.header), ...rows.map((row) => widestLine(column.value(row)))),
37
+ ),
38
+ );
39
+
40
+ const header = formatTableLine(
41
+ columns.map((column) => column.header),
42
+ widths,
43
+ );
44
+ const divider = widths.map((width) => "-".repeat(width)).join(" ");
45
+ const body = rows.map((row) =>
46
+ formatTableRow(
47
+ columns.map((column) => column.value(row)),
48
+ widths,
49
+ ),
50
+ );
51
+
52
+ return [header, divider, ...body].join("\n");
53
+ }
54
+
55
+ export function formatRecord(entries: ReadonlyArray<RecordEntry>) {
56
+ return formatTable(
57
+ [
58
+ { header: "field", value: (entry) => entry.field, maxWidth: 24 },
59
+ { header: "value", value: (entry) => entry.value, maxWidth: 96 },
60
+ ],
61
+ entries,
62
+ );
63
+ }
64
+
65
+ export function formatMarkdown(text: string) {
66
+ return terminalMarkdown
67
+ .parse(text, {
68
+ async: false,
69
+ })
70
+ .trimEnd();
71
+ }
72
+
73
+ export function formatChatTranscript(
74
+ messages: ReadonlyArray<{
75
+ readonly role: string;
76
+ readonly text: string;
77
+ }>,
78
+ ) {
79
+ return `${messages.map(formatChatMessage).join("\n\n")}\n`;
80
+ }
81
+
82
+ function formatChatMessage(message: { readonly role: string; readonly text: string }) {
83
+ const rendered = formatMarkdown(message.text);
84
+ return `${message.role}\n${indentBlock(rendered.length > 0 ? rendered : "(empty)")}`;
85
+ }
86
+
87
+ function formatTableRow(cells: ReadonlyArray<string>, widths: ReadonlyArray<number>) {
88
+ const wrappedCells = cells.map((cell, index) => wrapCell(cell, widths[index]!));
89
+ const height = Math.max(...wrappedCells.map((cell) => cell.length));
90
+ const lines: string[] = [];
91
+
92
+ for (let rowIndex = 0; rowIndex < height; rowIndex += 1) {
93
+ lines.push(
94
+ formatTableLine(
95
+ wrappedCells.map((cell) => cell[rowIndex] ?? ""),
96
+ widths,
97
+ ),
98
+ );
99
+ }
100
+
101
+ return lines.join("\n");
102
+ }
103
+
104
+ function formatTableLine(cells: ReadonlyArray<string>, widths: ReadonlyArray<number>) {
105
+ return cells.map((cell, index) => padRight(cell, widths[index]!)).join(" ");
106
+ }
107
+
108
+ function wrapCell(value: string, width: number) {
109
+ const lines = value
110
+ .split("\n")
111
+ .flatMap((line) =>
112
+ wrapAnsi(line.length > 0 ? line : " ", width, { hard: true, trim: false }).split("\n"),
113
+ );
114
+ return lines.length > 0 ? lines : [""];
115
+ }
116
+
117
+ function widestLine(value: string) {
118
+ return Math.max(...value.split("\n").map((line) => stringWidth(line)));
119
+ }
120
+
121
+ function padRight(value: string, width: number) {
122
+ const padding = width - stringWidth(value);
123
+ return padding > 0 ? `${value}${" ".repeat(padding)}` : value;
124
+ }
125
+
126
+ function indentBlock(value: string) {
127
+ return value
128
+ .split("\n")
129
+ .map((line) => ` ${line}`)
130
+ .join("\n");
131
+ }
@@ -0,0 +1,12 @@
1
+ export {
2
+ resolveOutputFormat,
3
+ canRenderLiveTerminal,
4
+ isInteractiveHumanTerminal,
5
+ isAgentEnvironment,
6
+ humanJsonFormatChoices,
7
+ humanNdjsonFormatChoices,
8
+ humanJsonNdjsonFormatChoices,
9
+ type HumanJsonFormat,
10
+ type HumanNdjsonFormat,
11
+ type HumanJsonNdjsonFormat,
12
+ } from "./output.ts";