t3code-cli 0.5.1 → 0.7.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 (112) hide show
  1. package/README.md +41 -16
  2. package/dist/Context-DueQ9iMH.js +4916 -0
  3. package/dist/Path-D8WPdPwR.js +11406 -0
  4. package/dist/Schema-DsQxYh6_.js +13581 -0
  5. package/dist/UrlParams-BA6gBvaY.js +205 -0
  6. package/dist/application.js +2 -0
  7. package/dist/auth.js +3 -0
  8. package/dist/base-dir-R12OMDso.js +20 -0
  9. package/dist/bin.js +236 -13526
  10. package/dist/chunk-B5meny8j.js +36 -0
  11. package/dist/cli.js +2 -0
  12. package/dist/config.js +4 -0
  13. package/dist/connection.js +15 -0
  14. package/dist/contracts.js +1 -0
  15. package/dist/error-B2t1bAP9.js +169 -0
  16. package/dist/error-BHRnjLux.js +15 -0
  17. package/dist/error-jwMt3VoW.js +54 -0
  18. package/dist/flags-CM7_iGdA.js +13371 -0
  19. package/dist/index.js +5 -12
  20. package/dist/layer-CfC5qZol.js +1508 -0
  21. package/dist/layer-DHhKS5jd.js +13 -0
  22. package/dist/layer-DUv99vsS.js +72 -0
  23. package/dist/layer-DvHnKBYj.js +29466 -0
  24. package/dist/layer-MqCKkIGD.js +1206 -0
  25. package/dist/layout.js +2 -0
  26. package/dist/orchestration.js +2 -0
  27. package/dist/rpc.js +2 -0
  28. package/dist/runtime.js +3 -0
  29. package/dist/scope-GycYiJ54.js +29 -0
  30. package/dist/scope.js +2 -0
  31. package/dist/service-CLmRO2Dp.js +8 -0
  32. package/dist/service-ybOWV9pL.js +5 -0
  33. package/dist/src/application/index.d.ts +3 -0
  34. package/dist/src/application/layer.d.ts +4 -4
  35. package/dist/src/application/projects.d.ts +1 -1
  36. package/dist/src/application/service.d.ts +1 -1
  37. package/dist/src/application/threads.d.ts +4 -4
  38. package/dist/src/auth/index.d.ts +10 -0
  39. package/dist/src/auth/local-base-dir.d.ts +5 -7
  40. package/dist/src/auth/local-token.d.ts +2 -2
  41. package/dist/src/auth/type.d.ts +1 -0
  42. package/dist/src/cli/flags.d.ts +15 -0
  43. package/dist/src/cli/index.d.ts +1 -0
  44. package/dist/src/cli/output-format.d.ts +9 -0
  45. package/dist/src/config/index.d.ts +5 -0
  46. package/dist/src/config/layer.d.ts +2 -0
  47. package/dist/src/config/service.d.ts +2 -0
  48. package/dist/src/connection/index.d.ts +5 -0
  49. package/dist/src/contracts/index.d.ts +1 -0
  50. package/dist/src/domain/helpers.d.ts +163 -32
  51. package/dist/src/index.d.ts +4 -28
  52. package/dist/src/layout/base-dir.d.ts +12 -0
  53. package/dist/src/layout/index.d.ts +1 -0
  54. package/dist/src/orchestration/index.d.ts +2 -0
  55. package/dist/src/rpc/index.d.ts +1 -0
  56. package/dist/src/runtime/index.d.ts +2 -0
  57. package/dist/src/runtime/layer.d.ts +11 -0
  58. package/dist/src/scope/index.d.ts +1 -0
  59. package/dist/src/scope/resolve.d.ts +19 -0
  60. package/dist/src/t3tools/index.d.ts +1 -0
  61. package/dist/src-KdbHqrex.js +8874 -0
  62. package/dist/t3tools.js +2 -0
  63. package/dist/transport-D3zBdZ1h.js +539 -0
  64. package/dist/url-SlsaG8nY.js +165 -0
  65. package/package.json +54 -2
  66. package/src/application/index.ts +3 -0
  67. package/src/application/service.ts +1 -1
  68. package/src/application/threads.ts +38 -11
  69. package/src/auth/index.ts +28 -0
  70. package/src/auth/layer.ts +13 -6
  71. package/src/auth/local-base-dir.ts +15 -19
  72. package/src/auth/local-origin.ts +4 -1
  73. package/src/auth/local-token.ts +4 -1
  74. package/src/auth/type.ts +1 -0
  75. package/src/bin.ts +1 -1
  76. package/src/cli/app.ts +6 -6
  77. package/src/cli/auth-format.ts +2 -0
  78. package/src/cli/auth.ts +19 -9
  79. package/src/cli/error.ts +7 -0
  80. package/src/cli/flags.ts +51 -0
  81. package/src/cli/index.ts +10 -0
  82. package/src/cli/{models.ts → model.ts} +5 -4
  83. package/src/cli/{projects.ts → project.ts} +8 -7
  84. package/src/cli/require.ts +31 -0
  85. package/src/cli/{threads.ts → thread.ts} +2 -2
  86. package/src/cli/threads/archive.ts +24 -8
  87. package/src/cli/threads/list.ts +12 -5
  88. package/src/cli/threads/messages.ts +20 -5
  89. package/src/cli/threads/send.ts +27 -23
  90. package/src/cli/threads/start.ts +22 -21
  91. package/src/cli/threads/wait.ts +22 -12
  92. package/src/config/index.ts +5 -0
  93. package/src/config/layer.ts +2 -0
  94. package/src/config/service.ts +2 -0
  95. package/src/connection/index.ts +9 -0
  96. package/src/contracts/index.ts +8 -0
  97. package/src/domain/helpers.test.ts +103 -0
  98. package/src/domain/helpers.ts +112 -16
  99. package/src/index.ts +4 -87
  100. package/src/layout/base-dir.ts +35 -0
  101. package/src/layout/index.ts +1 -0
  102. package/src/orchestration/index.ts +7 -0
  103. package/src/rpc/index.ts +1 -0
  104. package/src/runtime/index.ts +12 -0
  105. package/src/{runtime.ts → runtime/layer.ts} +15 -15
  106. package/src/scope/index.ts +6 -0
  107. package/src/scope/resolve.ts +62 -0
  108. package/src/t3tools/index.ts +1 -0
  109. package/dist/runtime-KQVRmRk-.js +0 -71499
  110. package/dist/src/connection.d.ts +0 -5
  111. package/dist/src/runtime.d.ts +0 -10
  112. package/src/connection.ts +0 -9
@@ -0,0 +1,165 @@
1
+ import { On as dual } from "./Context-DueQ9iMH.js";
2
+ import { H as fromResult, Tt as succeed, b as catchTags, cn as IllegalArgumentError, j as flatMap, k as fail, mi as try_ } from "./Path-D8WPdPwR.js";
3
+ import { n as UrlError } from "./error-BHRnjLux.js";
4
+ //#region node_modules/.pnpm/effect@4.0.0-beta.78/node_modules/effect/dist/unstable/http/Url.js
5
+ /**
6
+ * Immutable helpers for parsing and editing platform `URL` values.
7
+ *
8
+ * This module keeps the WHATWG `URL` object as the URL representation used by
9
+ * HTTP clients and servers, then adds safe parsing and pipeable setters for
10
+ * credentials, host, path, protocol, query, and hash components. Query strings
11
+ * can be read, replaced, or transformed through `UrlParams` so repeated keys and
12
+ * key/value encoding stay explicit.
13
+ *
14
+ * **Mental model**
15
+ *
16
+ * `fromString` returns a `Result` instead of throwing when URL construction
17
+ * fails. Setters never mutate the original `URL`; each one clones the input and
18
+ * assigns the requested component on the clone. Use `mutate` when several
19
+ * component assignments should happen on the same clone.
20
+ *
21
+ * **Common tasks**
22
+ *
23
+ * - Parse absolute URLs, or resolve relative URL strings against a base URL.
24
+ * - Pipe setters to adjust host, protocol, path, credentials, search, or hash.
25
+ * - Extract query parameters with `urlParams`.
26
+ * - Replace or transform query parameters with `setUrlParams` and
27
+ * `modifyUrlParams`.
28
+ *
29
+ * **Gotchas**
30
+ *
31
+ * Relative input needs an explicit base. Component assignment and serialization
32
+ * follow platform `URL` rules, so values may be normalized or percent-encoded.
33
+ * `setPassword` accepts `Redacted` input, but the resulting `URL` still contains
34
+ * the actual credential and should be treated as sensitive when serialized.
35
+ *
36
+ * @since 4.0.0
37
+ */
38
+ /**
39
+ * Parses a URL string safely into a `URL` object, returning a `Result` type for
40
+ * error handling.
41
+ *
42
+ * **Details**
43
+ *
44
+ * This function converts a string into a `URL` object, enabling safe URL
45
+ * parsing with built-in error handling. If the string is invalid or fails to
46
+ * parse, this function does not throw an error; instead, it wraps the error in
47
+ * a `IllegalArgumentError` and returns it as the `Failure` value of an
48
+ * `Result`. The `Success` value contains the successfully parsed `URL`.
49
+ *
50
+ * An optional `base` parameter can be provided to resolve relative URLs. If
51
+ * specified, the function interprets the input `url` as relative to this
52
+ * `base`. This is especially useful when dealing with URLs that might not be
53
+ * fully qualified.
54
+ *
55
+ * **Example** (Parsing absolute and relative URLs)
56
+ *
57
+ * ```ts
58
+ * import { Result } from "effect"
59
+ * import { Url } from "effect/unstable/http"
60
+ *
61
+ * // Parse an absolute URL
62
+ * //
63
+ * // ┌─── Result<URL, IllegalArgumentError>
64
+ * // ▼
65
+ * const parsed = Url.fromString("https://example.com/path")
66
+ *
67
+ * if (Result.isSuccess(parsed)) {
68
+ * console.log("Parsed URL:", parsed.success.toString())
69
+ * } else {
70
+ * console.log("Error:", parsed.failure.message)
71
+ * }
72
+ * // Output: Parsed URL: https://example.com/path
73
+ *
74
+ * // Parse a relative URL with a base
75
+ * const relativeParsed = Url.fromString("/relative-path", "https://example.com")
76
+ *
77
+ * if (Result.isSuccess(relativeParsed)) {
78
+ * console.log("Parsed relative URL:", relativeParsed.success.toString())
79
+ * } else {
80
+ * console.log("Error:", relativeParsed.failure.message)
81
+ * }
82
+ * // Output: Parsed relative URL: https://example.com/relative-path
83
+ * ```
84
+ *
85
+ * @category constructors
86
+ * @since 4.0.0
87
+ */
88
+ const fromString = (url, base) => try_({
89
+ try: () => new URL(url, base),
90
+ catch: () => new IllegalArgumentError(`Invalid URL: "${url}"${base !== void 0 ? ` with base "${base}"` : ""}`)
91
+ });
92
+ /**
93
+ * Updates a cloned `URL` with a callback, allowing multiple changes at once.
94
+ *
95
+ * **Example** (Mutating URL credentials)
96
+ *
97
+ * ```ts
98
+ * import { Url } from "effect/unstable/http"
99
+ *
100
+ * const myUrl = new URL("https://example.com")
101
+ *
102
+ * const mutatedUrl = Url.mutate(myUrl, (url) => {
103
+ * url.username = "user"
104
+ * url.password = "pass"
105
+ * })
106
+ *
107
+ * console.log("Mutated:", mutatedUrl.toString())
108
+ * // Output: Mutated: https://user:pass@example.com/
109
+ * ```
110
+ *
111
+ * @category modifiers
112
+ * @since 4.0.0
113
+ */
114
+ const mutate = /*#__PURE__*/ dual(2, (self, f) => {
115
+ const copy = new URL(self);
116
+ f(copy);
117
+ return copy;
118
+ });
119
+ //#endregion
120
+ //#region src/config/url.ts
121
+ function normalizeHttpBaseUrl(value) {
122
+ return parseUrl(value).pipe(flatMap((url) => succeed(normalizeBaseUrl(url))));
123
+ }
124
+ function toHttpEndpointUrl(httpBaseUrl, pathname) {
125
+ return parseUrl(httpBaseUrl).pipe(flatMap((url) => succeed(withPathname(url, pathname).toString())));
126
+ }
127
+ function toWebSocketEndpointUrl(httpBaseUrl, pathname) {
128
+ return parseUrl(httpBaseUrl).pipe(flatMap((url) => {
129
+ if (url.protocol === "http:") return succeed(withPathname(url, pathname, "ws:").toString());
130
+ if (url.protocol === "https:") return succeed(withPathname(url, pathname, "wss:").toString());
131
+ return fail(new UrlError({
132
+ message: `unsupported server url protocol: ${url.protocol}`,
133
+ protocol: url.protocol
134
+ }));
135
+ }));
136
+ }
137
+ function parseUrl(value) {
138
+ return fromResult(fromString(value)).pipe(catchTags({ IllegalArgumentError: (error) => fail(new UrlError({
139
+ message: "invalid url",
140
+ cause: error
141
+ })) }));
142
+ }
143
+ function normalizeBaseUrl(url) {
144
+ return mutate(url, (current) => {
145
+ current.hash = "";
146
+ current.search = "";
147
+ current.pathname = normalizePathname(current.pathname);
148
+ }).toString().replace(/\/$/, "");
149
+ }
150
+ function withPathname(url, pathname, protocol) {
151
+ return mutate(url, (current) => {
152
+ if (protocol !== void 0) current.protocol = protocol;
153
+ current.pathname = appendPathname(current.pathname, pathname);
154
+ current.search = "";
155
+ current.hash = "";
156
+ });
157
+ }
158
+ function normalizePathname(pathname) {
159
+ return pathname === "/" ? "" : pathname.replace(/\/+$/u, "");
160
+ }
161
+ function appendPathname(basePathname, pathname) {
162
+ return `${normalizePathname(basePathname)}${pathname.startsWith("/") ? pathname : `/${pathname}`}`;
163
+ }
164
+ //#endregion
165
+ export { mutate as a, fromString as i, toHttpEndpointUrl as n, toWebSocketEndpointUrl as r, normalizeHttpBaseUrl as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "t3code-cli",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "description": "CLI for t3code",
5
5
  "keywords": [
6
6
  "claude",
@@ -37,6 +37,54 @@
37
37
  ".": {
38
38
  "types": "./dist/src/index.d.ts",
39
39
  "default": "./dist/index.js"
40
+ },
41
+ "./application": {
42
+ "types": "./dist/src/application/index.d.ts",
43
+ "default": "./dist/application.js"
44
+ },
45
+ "./auth": {
46
+ "types": "./dist/src/auth/index.d.ts",
47
+ "default": "./dist/auth.js"
48
+ },
49
+ "./cli": {
50
+ "types": "./dist/src/cli/index.d.ts",
51
+ "default": "./dist/cli.js"
52
+ },
53
+ "./config": {
54
+ "types": "./dist/src/config/index.d.ts",
55
+ "default": "./dist/config.js"
56
+ },
57
+ "./connection": {
58
+ "types": "./dist/src/connection/index.d.ts",
59
+ "default": "./dist/connection.js"
60
+ },
61
+ "./contracts": {
62
+ "types": "./dist/src/contracts/index.d.ts",
63
+ "default": "./dist/contracts.js"
64
+ },
65
+ "./t3tools": {
66
+ "types": "./dist/upstream-t3code/packages/contracts/src/index.d.ts",
67
+ "default": "./dist/t3tools.js"
68
+ },
69
+ "./layout": {
70
+ "types": "./dist/src/layout/index.d.ts",
71
+ "default": "./dist/layout.js"
72
+ },
73
+ "./orchestration": {
74
+ "types": "./dist/src/orchestration/index.d.ts",
75
+ "default": "./dist/orchestration.js"
76
+ },
77
+ "./rpc": {
78
+ "types": "./dist/src/rpc/index.d.ts",
79
+ "default": "./dist/rpc.js"
80
+ },
81
+ "./runtime": {
82
+ "types": "./dist/src/runtime/index.d.ts",
83
+ "default": "./dist/runtime.js"
84
+ },
85
+ "./scope": {
86
+ "types": "./dist/src/scope/index.d.ts",
87
+ "default": "./dist/scope.js"
40
88
  }
41
89
  },
42
90
  "dependencies": {
@@ -45,13 +93,15 @@
45
93
  },
46
94
  "devDependencies": {
47
95
  "@changesets/cli": "^2.31.0",
96
+ "@effect/vitest": "4.0.0-beta.78",
97
+ "@total-typescript/shoehorn": "^0.1.2",
48
98
  "@types/node": "^25.9.1",
49
99
  "typescript": "^6.0.3",
50
100
  "vite-plus": "^0.1.24",
51
101
  "@t3tools/contracts": "0.0.24"
52
102
  },
53
103
  "engines": {
54
- "node": "^24.0.0"
104
+ "node": ">=24.0.0"
55
105
  },
56
106
  "scripts": {
57
107
  "build": "vp pack && tsc -p tsconfig.dts.json",
@@ -60,7 +110,9 @@
60
110
  "format:check": "vp fmt --check",
61
111
  "lint": "vp lint",
62
112
  "lint:fix": "vp lint --fix",
113
+ "test": "vp test run",
63
114
  "changeset": "changeset",
115
+ "release:version": "changeset version && pnpm format",
64
116
  "release:check": "pnpm check && pnpm typecheck && pnpm pack --dry-run",
65
117
  "release:publish": "changeset publish",
66
118
  "typecheck": "tsc --noEmit"
@@ -0,0 +1,3 @@
1
+ export { T3Application } from "./service.ts";
2
+ export type { SendThreadInput, StartThreadInput, StartThreadPolicy, WaitEvent } from "./service.ts";
3
+ export type { ApplicationError } from "./error.ts";
@@ -15,7 +15,7 @@ import type {
15
15
  import type { ApplicationError } from "./error.ts";
16
16
 
17
17
  export type StartThreadInput = {
18
- readonly projectRef: string;
18
+ readonly projectRef?: string;
19
19
  readonly message: string;
20
20
  readonly title?: string;
21
21
  readonly provider?: string;
@@ -4,8 +4,8 @@ import * as Path from "effect/Path";
4
4
 
5
5
  import { Environment } from "../environment/service.ts";
6
6
  import { T3Orchestration } from "../orchestration/service.ts";
7
- import { ThreadSessionError } from "../domain/error.ts";
8
- import { resolveProject } from "../domain/helpers.ts";
7
+ import { ProjectLookupError, ThreadSessionError } from "../domain/error.ts";
8
+ import { resolveProjectScope } from "../domain/helpers.ts";
9
9
  import { type StartThreadInput } from "./service.ts";
10
10
  import type { SendThreadInput } from "./service.ts";
11
11
  import { mergeModelOptions } from "./model-selection.ts";
@@ -27,10 +27,17 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
27
27
  const environment = yield* Environment;
28
28
  const listThreads = Effect.fn("T3ApplicationLive.listThreads")(function* (projectRef: string) {
29
29
  const snapshot = yield* orchestration.getShellSnapshot();
30
- const project = resolveProject(snapshot, projectRef, path, environment.cwd);
30
+ const scope = yield* resolveProjectScope(snapshot, {
31
+ ref: projectRef,
32
+ }).pipe(Effect.provideService(Path.Path, path));
33
+ if (scope === undefined) {
34
+ return yield* Effect.fail(
35
+ new ProjectLookupError({ message: `project not found: ${projectRef}`, ref: projectRef }),
36
+ );
37
+ }
31
38
  return {
32
- project,
33
- threads: snapshot.threads.filter((thread) => thread.projectId === project.id),
39
+ project: scope.project,
40
+ threads: snapshot.threads.filter((thread) => thread.projectId === scope.project.id),
34
41
  };
35
42
  });
36
43
  const getThreadMessages = Effect.fn("T3ApplicationLive.getThreadMessages")(function* (
@@ -51,11 +58,31 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
51
58
  },
52
59
  ) {
53
60
  const snapshot = yield* orchestration.getShellSnapshot();
54
- const project = resolveProject(snapshot, startInput.projectRef, path, environment.cwd);
61
+ const projectRef = startInput.projectRef;
62
+ if (projectRef === undefined) {
63
+ return yield* Effect.fail(
64
+ new ProjectLookupError({
65
+ message: "project is required",
66
+ ref: environment.cwd,
67
+ }),
68
+ );
69
+ }
70
+ const scope = yield* resolveProjectScope(snapshot, {
71
+ ref: projectRef,
72
+ }).pipe(Effect.provideService(Path.Path, path));
73
+ if (scope === undefined) {
74
+ return yield* Effect.fail(
75
+ new ProjectLookupError({ message: `project not found: ${projectRef}`, ref: projectRef }),
76
+ );
77
+ }
78
+ const worktreePath = startInput.worktreePath ?? scope.inferredWorktreePath;
55
79
  const serverConfig = yield* orchestration.getServerConfig();
56
80
  const commands = yield* makeThreadStartCommands({
57
- start: startInput,
58
- project,
81
+ start: {
82
+ ...startInput,
83
+ ...(worktreePath !== undefined ? { worktreePath } : {}),
84
+ },
85
+ project: scope.project,
59
86
  serverConfig,
60
87
  }).pipe(Effect.provideService(Crypto.Crypto, crypto));
61
88
  const createDispatch = yield* orchestration.dispatch(commands.createCommand);
@@ -67,7 +94,7 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
67
94
  const threadId = commands.threadId;
68
95
  const until = policy?.until ?? "dispatch";
69
96
  if (until === "dispatch") {
70
- return { dispatch, project, threadId };
97
+ return { dispatch, project: scope.project, threadId };
71
98
  }
72
99
  yield* waitForShellSequence({
73
100
  orchestration,
@@ -80,14 +107,14 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
80
107
  return opened.snapshot;
81
108
  }),
82
109
  );
83
- return { dispatch, project, threadId, thread };
110
+ return { dispatch, project: scope.project, threadId, thread };
84
111
  }
85
112
  const thread = yield* waitForThreadUntilComplete({
86
113
  orchestration,
87
114
  threadId,
88
115
  });
89
116
  yield* failIfThreadError(thread);
90
- return { dispatch, project, threadId, thread };
117
+ return { dispatch, project: scope.project, threadId, thread };
91
118
  });
92
119
  const sendThread = Effect.fn("T3ApplicationLive.sendThread")(function* (
93
120
  input: SendThreadInput,
@@ -0,0 +1,28 @@
1
+ export { T3Auth } from "./service.ts";
2
+ export { T3AuthLive, makeT3Auth } from "./layer.ts";
3
+ export { T3LocalAuth, T3LocalAuthLive, makeT3LocalAuth } from "./local.ts";
4
+ export { T3LocalAuthOrigin, T3LocalAuthOriginLive, makeT3LocalAuthOrigin } from "./local-origin.ts";
5
+ export { T3LocalAuthToken, T3LocalAuthTokenLive, makeT3LocalAuthToken } from "./local-token.ts";
6
+ export { T3AuthPairing, T3AuthPairingLive, makeT3AuthPairing, parsePairingUrl } from "./pairing.ts";
7
+ export {
8
+ AuthConfigError,
9
+ AuthLocalDatabaseError,
10
+ AuthLocalError,
11
+ AuthLocalSecretError,
12
+ AuthLocalSigningError,
13
+ AuthPairingUrlError,
14
+ AuthTransportError,
15
+ } from "./error.ts";
16
+ export type { AuthError } from "./error.ts";
17
+ export type { AuthSessionState, AuthWebSocketTicketResult } from "./schema.ts";
18
+ export type {
19
+ AuthConfigInput,
20
+ AuthSessionRole,
21
+ LocalAuthInput,
22
+ LocalAuthOriginInput,
23
+ LocalAuthResult,
24
+ LocalAuthTokenInput,
25
+ LocalAuthTokenResult,
26
+ PairingUrl,
27
+ PairResult,
28
+ } from "./type.ts";
package/src/auth/layer.ts CHANGED
@@ -46,12 +46,19 @@ export const makeT3Auth = Effect.fn("makeT3Auth")(function* () {
46
46
  Effect.fail(new AuthConfigError({ message: "auth config failed", cause: error })),
47
47
  }),
48
48
  );
49
- yield* config.writeStored({ ...existing, url: input.url, token: input.token }).pipe(
50
- Effect.catchTags({
51
- ConfigError: (error) =>
52
- Effect.fail(new AuthConfigError({ message: "auth config failed", cause: error })),
53
- }),
54
- );
49
+ yield* config
50
+ .writeStored({
51
+ ...existing,
52
+ url: input.url,
53
+ token: input.token,
54
+ ...(input.local !== undefined ? { local: input.local } : {}),
55
+ })
56
+ .pipe(
57
+ Effect.catchTags({
58
+ ConfigError: (error) =>
59
+ Effect.fail(new AuthConfigError({ message: "auth config failed", cause: error })),
60
+ }),
61
+ );
55
62
  });
56
63
 
57
64
  return {
@@ -1,22 +1,18 @@
1
- import type * as Path from "effect/Path";
1
+ import * as Effect from "effect/Effect";
2
2
 
3
- import type { EnvironmentShape } from "../environment/service.ts";
3
+ import { Environment } from "../environment/service.ts";
4
+ import { resolveT3BaseDir } from "../layout/base-dir.ts";
4
5
 
5
- export function resolveLocalBaseDir(input: {
6
- readonly baseDir: string | undefined;
7
- readonly environment: EnvironmentShape;
8
- readonly path: Path.Path;
6
+ export const resolveLocalBaseDir = Effect.fn("resolveLocalBaseDir")(function* (input: {
7
+ readonly baseDir?: string | undefined;
9
8
  }) {
10
- const envBaseDir = input.environment.env["T3CODE_HOME"];
11
- const raw = input.baseDir ?? envBaseDir;
12
- if (raw === undefined || raw.length === 0) {
13
- return input.path.join(input.environment.homeDir, ".t3");
14
- }
15
- if (raw === "~") {
16
- return input.environment.homeDir;
17
- }
18
- if (raw.startsWith("~/") || raw.startsWith("~\\")) {
19
- return input.path.join(input.environment.homeDir, raw.slice(2));
20
- }
21
- return input.path.resolve(input.environment.cwd, raw);
22
- }
9
+ const environment = yield* Environment;
10
+ return yield* resolveT3BaseDir({
11
+ layout: {
12
+ cwd: environment.cwd,
13
+ homeDir: environment.homeDir,
14
+ t3codeHome: environment.env["T3CODE_HOME"],
15
+ },
16
+ baseDir: input.baseDir,
17
+ });
18
+ });
@@ -26,7 +26,10 @@ export const makeT3LocalAuthOrigin = Effect.fn("makeT3LocalAuthOrigin")(function
26
26
  const resolve = Effect.fn("T3LocalAuthOriginLive.resolve")(function* (
27
27
  input?: LocalAuthOriginInput,
28
28
  ) {
29
- const baseDir = resolveLocalBaseDir({ baseDir: input?.baseDir, environment, path });
29
+ const baseDir = yield* resolveLocalBaseDir({ baseDir: input?.baseDir }).pipe(
30
+ Effect.provideService(Environment, environment),
31
+ Effect.provideService(Path.Path, path),
32
+ );
30
33
  if (input?.origin !== undefined) {
31
34
  return yield* normalizeLocalOrigin(input.origin);
32
35
  }
@@ -185,7 +185,10 @@ export const makeT3LocalAuthToken = Effect.fn("makeT3LocalAuthToken")(function*
185
185
  );
186
186
  }
187
187
 
188
- const baseDir = resolveLocalBaseDir({ baseDir: input.baseDir, environment, path });
188
+ const baseDir = yield* resolveLocalBaseDir({ baseDir: input.baseDir }).pipe(
189
+ Effect.provideService(Environment, environment),
190
+ Effect.provideService(Path.Path, path),
191
+ );
189
192
  const session = yield* issueLocalDatabaseSession({
190
193
  dbPath: path.join(baseDir, "userdata", "state.sqlite"),
191
194
  secretsDir: path.join(baseDir, "userdata", "secrets"),
package/src/auth/type.ts CHANGED
@@ -10,6 +10,7 @@ export type PairingUrl = {
10
10
  export type AuthConfigInput = {
11
11
  readonly url: string;
12
12
  readonly token: string;
13
+ readonly local?: boolean;
13
14
  };
14
15
 
15
16
  export type PairResult = {
package/src/bin.ts CHANGED
@@ -10,7 +10,7 @@ import { NodeEnvironmentLive } from "./environment/layer.ts";
10
10
  import { T3InputLive } from "./cli/input/layer.ts";
11
11
  import { T3OutputLive } from "./cli/output/layer.ts";
12
12
  import { T3Output } from "./cli/output/service.ts";
13
- import { AppLayer } from "./runtime.ts";
13
+ import { AppLayer } from "./runtime/layer.ts";
14
14
  import { T3VersionBundledLive, T3VersionPackageJsonLive } from "./version/layer.ts";
15
15
  import { T3Version } from "./version/service.ts";
16
16
 
package/src/cli/app.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  import { Command } from "effect/unstable/cli";
2
2
 
3
3
  import { createAuthCommand } from "./auth.ts";
4
- import { createModelsCommand } from "./models.ts";
5
- import { createProjectsCommand } from "./projects.ts";
6
- import { createThreadsCommand } from "./threads.ts";
4
+ import { createModelCommand } from "./model.ts";
5
+ import { createProjectCommand } from "./project.ts";
6
+ import { createThreadCommand } from "./thread.ts";
7
7
 
8
8
  export function createCliCommand() {
9
9
  return Command.make("t3cli").pipe(
10
10
  Command.withDescription("non-interactive cli for running t3code server"),
11
11
  Command.withSubcommands([
12
12
  createAuthCommand(),
13
- createModelsCommand(),
14
- createProjectsCommand(),
15
- createThreadsCommand(),
13
+ createModelCommand(),
14
+ createProjectCommand(),
15
+ createThreadCommand(),
16
16
  ]),
17
17
  );
18
18
  }
@@ -25,6 +25,7 @@ export function formatAuthStatusHuman(input: {
25
25
  }) {
26
26
  return [
27
27
  `url: ${input.config.url}`,
28
+ `local: ${input.config.local ? "yes" : "no"}`,
28
29
  `authenticated: ${input.result.authenticated ? "yes" : "no"}`,
29
30
  ...(input.result.role !== undefined ? [`role: ${input.result.role}`] : []),
30
31
  ...(input.result.expiresAt !== undefined ? [`expires: ${input.result.expiresAt}`] : []),
@@ -39,5 +40,6 @@ export function formatAuthStatusJson(input: {
39
40
  ...input.result,
40
41
  url: input.config.url,
41
42
  source: input.config.source,
43
+ local: input.config.local,
42
44
  };
43
45
  }
package/src/cli/auth.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import * as Option from "effect/Option";
3
- import { Argument, Command, Flag } from "effect/unstable/cli";
3
+ import { Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  import {
6
6
  formatAuthLocalHuman,
@@ -12,7 +12,8 @@ import {
12
12
  import { T3Auth } from "../auth/service.ts";
13
13
  import { T3Config } from "../config/service.ts";
14
14
  import { Environment } from "../environment/service.ts";
15
- import { humanJsonFormatChoices, resolveOutputFormat } from "./output-format.ts";
15
+ import { formatFlag } from "./flags.ts";
16
+ import { resolveOutputFormat } from "./output-format.ts";
16
17
  import { T3Output } from "./output/service.ts";
17
18
 
18
19
  export function createAuthCommand() {
@@ -25,17 +26,22 @@ export function createAuthCommand() {
25
26
  const pairCommand = Command.make(
26
27
  "pair",
27
28
  {
28
- url: Argument.string("url"),
29
- format: Flag.choice("format", humanJsonFormatChoices).pipe(Flag.withDefault("auto")),
29
+ url: Flag.string("url"),
30
+ local: Flag.boolean("local"),
31
+ format: formatFlag,
30
32
  },
31
- ({ url, format }) =>
33
+ ({ url, local, format }) =>
32
34
  Effect.gen(function* () {
33
35
  const auth = yield* T3Auth;
34
36
  const environment = yield* Environment;
35
37
  const output = yield* T3Output;
36
38
  const resolvedFormat = resolveOutputFormat(format, environment, "json");
37
39
  const result = yield* auth.pair(url);
38
- yield* auth.writeConfig(result);
40
+ yield* auth.writeConfig({
41
+ url: result.url,
42
+ token: result.token,
43
+ local,
44
+ });
39
45
  if (resolvedFormat === "json") {
40
46
  yield* output.printJson(result);
41
47
  } else {
@@ -52,7 +58,7 @@ const localCommand = Command.make(
52
58
  role: Flag.choice("role", ["owner", "client"] as const).pipe(Flag.withDefault("owner")),
53
59
  label: Flag.string("label").pipe(Flag.withDefault("t3cli")),
54
60
  subject: Flag.string("subject").pipe(Flag.withDefault("t3cli-local")),
55
- format: Flag.choice("format", humanJsonFormatChoices).pipe(Flag.withDefault("auto")),
61
+ format: formatFlag,
56
62
  },
57
63
  ({ baseDir, origin, role, label, subject, format }) =>
58
64
  Effect.gen(function* () {
@@ -67,7 +73,11 @@ const localCommand = Command.make(
67
73
  ...(Option.isSome(baseDir) ? { baseDir: baseDir.value } : {}),
68
74
  ...(Option.isSome(origin) ? { origin: origin.value } : {}),
69
75
  });
70
- yield* auth.writeConfig(result);
76
+ yield* auth.writeConfig({
77
+ url: result.url,
78
+ token: result.token,
79
+ local: true,
80
+ });
71
81
  if (resolvedFormat === "json") {
72
82
  yield* output.printJson(formatAuthLocalJson(result));
73
83
  } else {
@@ -79,7 +89,7 @@ const localCommand = Command.make(
79
89
  const statusCommand = Command.make(
80
90
  "status",
81
91
  {
82
- format: Flag.choice("format", humanJsonFormatChoices).pipe(Flag.withDefault("auto")),
92
+ format: formatFlag,
83
93
  },
84
94
  ({ format }) =>
85
95
  Effect.gen(function* () {
package/src/cli/error.ts CHANGED
@@ -14,3 +14,10 @@ export class InvalidLimitError extends Schema.TaggedErrorClass<InvalidLimitError
14
14
  value: Schema.String,
15
15
  },
16
16
  ) {}
17
+
18
+ export class MissingThreadError extends Schema.TaggedErrorClass<MissingThreadError>()(
19
+ "MissingThreadError",
20
+ {
21
+ message: Schema.String,
22
+ },
23
+ ) {}