t3code-cli 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -10
- package/dist/application.js +2 -2
- package/dist/auth.js +1 -1
- package/dist/bin.js +85526 -2662
- package/dist/cli.js +15 -2
- package/dist/config.js +120 -2
- package/dist/connection.js +2 -2
- package/dist/index.js +1 -1
- package/dist/node.js +6 -2
- package/dist/orchestration.js +2 -2
- package/dist/rpc.js +1 -1
- package/dist/runtime.js +2 -2
- package/dist/shared.js +42956 -38119
- package/dist/src/application/actions.d.ts +25 -0
- package/dist/src/application/index.d.ts +3 -3
- package/dist/src/application/layer.d.ts +51 -35
- package/dist/src/application/model-selection.d.ts +2 -2
- package/dist/src/application/models.d.ts +1 -1
- package/dist/src/application/project-commands.d.ts +22 -2
- package/dist/src/application/projects.d.ts +7 -7
- package/dist/src/application/service.d.ts +65 -2
- package/dist/src/application/shell-sequence.d.ts +11 -0
- package/dist/src/application/terminals.d.ts +2 -2
- package/dist/src/application/thread-commands.d.ts +1 -1
- package/dist/src/application/thread-wait.d.ts +9 -0
- package/dist/src/application/threads.d.ts +56 -5
- package/dist/src/auth/error.d.ts +2 -1
- package/dist/src/auth/index.d.ts +1 -1
- package/dist/src/auth/layer.d.ts +34 -6
- package/dist/src/auth/local-base-dir.d.ts +5 -2
- package/dist/src/auth/local-origin.d.ts +3 -3
- package/dist/src/auth/local-token.d.ts +3 -3
- package/dist/src/auth/service.d.ts +15 -3
- package/dist/src/auth/type.d.ts +40 -1
- package/dist/src/cli/env/flag.d.ts +2 -0
- package/dist/src/cli/env/index.d.ts +2 -0
- package/dist/src/cli/env/selection-layer.d.ts +3 -0
- package/dist/src/cli/flags.d.ts +2 -0
- package/dist/src/cli/format/index.d.ts +1 -0
- package/dist/src/cli/format/output.d.ts +12 -0
- package/dist/src/cli/index.d.ts +4 -0
- package/dist/src/cli/runtime/index.d.ts +1 -0
- package/dist/src/cli/runtime/service.d.ts +16 -0
- package/dist/src/{scope → cli/scope}/resolve.d.ts +5 -4
- package/dist/src/config/config.d.ts +49 -0
- package/dist/src/config/credential/cipher-node.d.ts +3 -0
- package/dist/src/config/credential/cipher-web.d.ts +3 -0
- package/dist/src/config/credential/cipher.d.ts +29 -0
- package/dist/src/config/credential/env.d.ts +3 -0
- package/dist/src/config/credential/error.d.ts +8 -0
- package/dist/src/config/credential/index.d.ts +6 -0
- package/dist/src/config/credential/service.d.ts +34 -0
- package/dist/src/config/env/env.d.ts +55 -0
- package/dist/src/config/env/index.d.ts +2 -0
- package/dist/src/{layout/base-dir.d.ts → config/env/layout.d.ts} +0 -1
- package/dist/src/config/environment-name/index.d.ts +1 -0
- package/dist/src/config/environment-name/name.d.ts +10 -0
- package/dist/src/config/error.d.ts +4 -11
- package/dist/src/config/index.d.ts +10 -5
- package/dist/src/config/keystore/error.d.ts +8 -0
- package/dist/src/config/keystore/file.d.ts +9 -0
- package/dist/src/config/keystore/index.d.ts +4 -0
- package/dist/src/config/keystore/keyring-node.d.ts +4 -0
- package/dist/src/config/keystore/service.d.ts +33 -0
- package/dist/src/config/paths/index.d.ts +1 -0
- package/dist/src/config/paths/paths.d.ts +6 -0
- package/dist/src/config/persist/file-mode.d.ts +4 -0
- package/dist/src/config/persist/migration.d.ts +68 -0
- package/dist/src/config/persist/persist.d.ts +40 -0
- package/dist/src/config/persist/schema.d.ts +68 -0
- package/dist/src/config/resolve/resolve.d.ts +40 -0
- package/dist/src/config/selection/index.d.ts +1 -0
- package/dist/src/config/selection/resolve.d.ts +4 -0
- package/dist/src/config/selection/service.d.ts +10 -0
- package/dist/src/config/types.d.ts +36 -0
- package/dist/src/config/url/error.d.ts +10 -0
- package/dist/src/config/url/index.d.ts +2 -0
- package/dist/src/connection/index.d.ts +0 -1
- package/dist/src/contracts/index.d.ts +1 -1
- package/dist/src/domain/error.d.ts +14 -1
- package/dist/src/domain/helpers.d.ts +16 -1
- package/dist/src/domain/model-config.d.ts +4 -1
- package/dist/src/domain/thread-activities.d.ts +1 -1
- package/dist/src/domain/thread-lifecycle.d.ts +10 -1
- package/dist/src/index.d.ts +1 -2
- package/dist/src/{connection/node.d.ts → node/connection.d.ts} +1 -1
- package/dist/src/node/index.d.ts +1 -2
- package/dist/src/orchestration/index.d.ts +0 -1
- package/dist/src/orchestration/layer.d.ts +153 -7
- package/dist/src/orchestration/service.d.ts +1 -1
- package/dist/src/rpc/error.d.ts +2 -2
- package/dist/src/rpc/index.d.ts +2 -3
- package/dist/src/rpc/layer.d.ts +574 -46
- package/dist/src/rpc/operation.d.ts +574 -46
- package/dist/src/rpc/ws-group.d.ts +184 -19
- package/dist/src/runtime/index.d.ts +1 -2
- package/dist/src/runtime/layer.d.ts +12 -8
- package/dist/src/t3tools/index.d.ts +1 -1
- package/dist/t3tools.js +2 -2
- package/package.json +25 -21
- package/src/application/actions.test.ts +328 -0
- package/src/application/actions.ts +475 -0
- package/src/application/index.ts +9 -5
- package/src/application/layer.ts +9 -0
- package/src/application/model-selection.ts +2 -2
- package/src/application/project-commands.ts +23 -5
- package/src/application/projects.ts +6 -7
- package/src/application/service.ts +79 -1
- package/src/application/shell-sequence.ts +1 -1
- package/src/application/terminals.ts +2 -1
- package/src/application/thread-commands.ts +1 -1
- package/src/application/thread-update.ts +1 -1
- package/src/application/thread-wait.ts +1 -1
- package/src/application/threads.test.ts +9 -3
- package/src/application/threads.ts +6 -6
- package/src/auth/error.ts +2 -1
- package/src/auth/index.ts +7 -0
- package/src/auth/layer.test-utils.ts +34 -0
- package/src/auth/layer.ts +116 -34
- package/src/auth/local-base-dir.ts +13 -6
- package/src/auth/local-origin.ts +10 -7
- package/src/auth/local-token.ts +28 -23
- package/src/auth/service.test.ts +200 -0
- package/src/auth/service.ts +27 -3
- package/src/auth/transport.ts +2 -2
- package/src/auth/type.ts +47 -1
- package/src/bin.ts +24 -6
- package/src/cli/action-format.ts +110 -0
- package/src/cli/action.ts +398 -0
- package/src/cli/app.ts +6 -0
- package/src/cli/auth.ts +76 -23
- package/src/cli/env/flag.ts +8 -0
- package/src/cli/env/index.ts +2 -0
- package/src/cli/env/selection-layer.test.ts +49 -0
- package/src/cli/env/selection-layer.ts +27 -0
- package/src/cli/env.ts +107 -0
- package/src/cli/extra-args.test.ts +32 -0
- package/src/cli/extra-args.ts +22 -0
- package/src/cli/flags.ts +11 -1
- package/src/cli/format/auth.ts +92 -0
- package/src/cli/format/human.ts +131 -0
- package/src/cli/format/index.ts +12 -0
- package/src/cli/format/model.ts +29 -0
- package/src/cli/format/output.ts +51 -0
- package/src/cli/format/project.ts +32 -0
- package/src/cli/format/terminal.ts +87 -0
- package/src/cli/{thread-format.test.ts → format/thread.test.ts} +6 -4
- package/src/cli/format/thread.ts +185 -0
- package/src/cli/index.ts +20 -0
- package/src/cli/interaction/confirm.ts +64 -0
- package/src/cli/{self-action.ts → interaction/self-action.ts} +8 -6
- package/src/cli/message-input.ts +1 -1
- package/src/cli/model.ts +9 -5
- package/src/cli/project.ts +13 -7
- package/src/cli/projects/delete.ts +13 -12
- package/src/cli/require.ts +9 -7
- package/src/cli/runtime/index.ts +1 -0
- package/src/cli/runtime/service.ts +28 -0
- package/src/{scope → cli/scope}/resolve.ts +11 -9
- package/src/cli/terminal/attach.ts +3 -6
- package/src/cli/terminal/create.ts +10 -9
- package/src/cli/terminal/destroy.ts +13 -11
- package/src/cli/terminal/list.ts +10 -9
- package/src/cli/terminal/read.test-utils.ts +48 -0
- package/src/cli/terminal/read.test.ts +35 -0
- package/src/cli/terminal/read.ts +3 -6
- package/src/cli/terminal/scope.ts +10 -5
- package/src/cli/terminal/shared.ts +1 -1
- package/src/cli/terminal/stream.ts +3 -6
- package/src/cli/terminal/wait.test.ts +5 -3
- package/src/cli/terminal/wait.ts +10 -9
- package/src/cli/terminal/write.test.ts +34 -0
- package/src/cli/terminal/write.ts +10 -9
- package/src/cli/threads/approve.ts +11 -7
- package/src/cli/threads/archive.ts +14 -9
- package/src/cli/threads/callback.ts +6 -4
- package/src/cli/threads/delete.ts +17 -11
- package/src/cli/threads/interrupt.ts +14 -9
- package/src/cli/threads/list.test.ts +5 -3
- package/src/cli/threads/list.ts +10 -10
- package/src/cli/threads/messages.ts +11 -7
- package/src/cli/threads/respond.ts +11 -7
- package/src/cli/threads/send.ts +19 -9
- package/src/cli/threads/show.ts +11 -7
- package/src/cli/threads/start.ts +18 -13
- package/src/cli/threads/unarchive.ts +11 -7
- package/src/cli/threads/update.ts +14 -9
- package/src/cli/threads/wait.ts +11 -7
- package/src/cli/wait-events.ts +1 -1
- package/src/config/config.ts +260 -0
- package/src/config/credential/cipher-node.ts +40 -0
- package/src/config/credential/cipher-web.ts +82 -0
- package/src/config/credential/cipher.ts +39 -0
- package/src/config/credential/env.ts +13 -0
- package/src/config/credential/error.ts +9 -0
- package/src/config/credential/index.ts +6 -0
- package/src/config/credential/service.test-utils.ts +25 -0
- package/src/config/credential/service.test.ts +116 -0
- package/src/config/credential/service.ts +205 -0
- package/src/config/env/env.test-utils.ts +14 -0
- package/src/config/env/env.ts +81 -0
- package/src/config/env/index.ts +2 -0
- package/src/{layout/base-dir.ts → config/env/layout.ts} +0 -9
- package/src/config/environment-name/index.ts +7 -0
- package/src/config/environment-name/name.test.ts +66 -0
- package/src/config/environment-name/name.ts +49 -0
- package/src/config/error.ts +6 -8
- package/src/config/index.ts +16 -5
- package/src/config/keystore/error.ts +9 -0
- package/src/config/keystore/file.ts +96 -0
- package/src/config/keystore/index.ts +4 -0
- package/src/config/keystore/keyring-node.test.ts +12 -0
- package/src/config/keystore/keyring-node.ts +100 -0
- package/src/config/keystore/service.test-utils.ts +15 -0
- package/src/config/keystore/service.test.ts +18 -0
- package/src/config/keystore/service.ts +29 -0
- package/src/config/layer.test-utils.ts +45 -0
- package/src/config/layer.test.ts +209 -0
- package/src/config/paths/index.ts +1 -0
- package/src/config/paths/paths.ts +36 -0
- package/src/config/persist/file-mode.ts +21 -0
- package/src/config/persist/migration.test.ts +71 -0
- package/src/config/persist/migration.ts +82 -0
- package/src/config/persist/persist.test.ts +113 -0
- package/src/config/persist/persist.ts +65 -0
- package/src/config/persist/schema.ts +37 -0
- package/src/config/platform.test-utils.ts +20 -0
- package/src/config/resolve/resolve.test-utils.ts +20 -0
- package/src/config/resolve/resolve.test.ts +137 -0
- package/src/config/resolve/resolve.ts +104 -0
- package/src/config/selection/index.ts +1 -0
- package/src/config/selection/resolve.ts +14 -0
- package/src/config/selection/service.ts +29 -0
- package/src/config/temp-home.test-utils.ts +7 -0
- package/src/config/types.ts +49 -0
- package/src/config/url/error.ts +8 -0
- package/src/config/url/index.ts +7 -0
- package/src/config/url/url.test.ts +31 -0
- package/src/connection/index.ts +0 -1
- package/src/contracts/index.ts +1 -1
- package/src/domain/error.ts +20 -1
- package/src/domain/helpers.test.ts +1 -1
- package/src/domain/helpers.ts +1 -1
- package/src/domain/model-config.ts +1 -1
- package/src/domain/thread-activities.test.ts +1 -1
- package/src/domain/thread-activities.ts +1 -1
- package/src/domain/thread-lifecycle.test.ts +1 -1
- package/src/domain/thread-lifecycle.ts +1 -1
- package/src/effect.test-utils.ts +25 -0
- package/src/index.ts +1 -1
- package/src/{connection/node.ts → node/connection.ts} +1 -1
- package/src/node/index.ts +1 -2
- package/src/orchestration/index.ts +0 -1
- package/src/orchestration/layer.ts +1 -1
- package/src/orchestration/service.ts +1 -1
- package/src/rpc/error.ts +2 -2
- package/src/rpc/index.ts +2 -3
- package/src/rpc/layer.ts +1 -1
- package/src/rpc/ws-group.ts +1 -1
- package/src/runtime/index.ts +2 -1
- package/src/runtime/layer.test-utils.ts +32 -0
- package/src/runtime/layer.test.ts +66 -0
- package/src/runtime/layer.ts +15 -9
- package/src/t3tools/index.ts +1 -1
- package/src/types/marked-terminal.d.ts +11 -0
- package/dist/layout.js +0 -2
- package/dist/scope.js +0 -2
- package/dist/src/cli/output-format.d.ts +0 -11
- package/dist/src/config/layer.d.ts +0 -22
- package/dist/src/config/service.d.ts +0 -22
- package/dist/src/environment/layer.d.ts +0 -3
- package/dist/src/environment/service.d.ts +0 -12
- package/dist/src/layout/index.d.ts +0 -1
- package/dist/upstream-t3code/packages/contracts/src/auth.d.ts +0 -441
- package/dist/upstream-t3code/packages/contracts/src/baseSchemas.d.ts +0 -38
- package/dist/upstream-t3code/packages/contracts/src/desktopBootstrap.d.ts +0 -14
- package/dist/upstream-t3code/packages/contracts/src/editor.d.ts +0 -124
- package/dist/upstream-t3code/packages/contracts/src/environment.d.ts +0 -64
- package/dist/upstream-t3code/packages/contracts/src/environmentHttp.d.ts +0 -762
- package/dist/upstream-t3code/packages/contracts/src/filesystem.d.ts +0 -26
- package/dist/upstream-t3code/packages/contracts/src/git.d.ts +0 -500
- package/dist/upstream-t3code/packages/contracts/src/index.d.ts +0 -25
- package/dist/upstream-t3code/packages/contracts/src/ipc.d.ts +0 -490
- package/dist/upstream-t3code/packages/contracts/src/keybindings.d.ts +0 -133
- package/dist/upstream-t3code/packages/contracts/src/model.d.ts +0 -112
- package/dist/upstream-t3code/packages/contracts/src/orchestration.d.ts +0 -6682
- package/dist/upstream-t3code/packages/contracts/src/project.d.ts +0 -45
- package/dist/upstream-t3code/packages/contracts/src/provider.d.ts +0 -116
- package/dist/upstream-t3code/packages/contracts/src/providerInstance.d.ts +0 -99
- package/dist/upstream-t3code/packages/contracts/src/providerRuntime.d.ts +0 -4276
- package/dist/upstream-t3code/packages/contracts/src/relay.d.ts +0 -1262
- package/dist/upstream-t3code/packages/contracts/src/relayClient.d.ts +0 -48
- package/dist/upstream-t3code/packages/contracts/src/remoteAccess.d.ts +0 -45
- package/dist/upstream-t3code/packages/contracts/src/review.d.ts +0 -37
- package/dist/upstream-t3code/packages/contracts/src/rpc.d.ts +0 -8218
- package/dist/upstream-t3code/packages/contracts/src/server.d.ts +0 -2291
- package/dist/upstream-t3code/packages/contracts/src/settings.d.ts +0 -271
- package/dist/upstream-t3code/packages/contracts/src/sourceControl.d.ts +0 -177
- package/dist/upstream-t3code/packages/contracts/src/terminal.d.ts +0 -330
- package/dist/upstream-t3code/packages/contracts/src/vcs.d.ts +0 -148
- package/src/cli/auth-format.ts +0 -45
- package/src/cli/confirm.ts +0 -31
- package/src/cli/model-format.ts +0 -18
- package/src/cli/output-format.ts +0 -45
- package/src/cli/project-format.ts +0 -18
- package/src/cli/terminal/commands.test.ts +0 -101
- package/src/cli/terminal-format.ts +0 -167
- package/src/cli/thread-format.ts +0 -128
- package/src/config/layer.ts +0 -105
- package/src/config/service.ts +0 -26
- package/src/environment/layer.ts +0 -12
- package/src/environment/service.ts +0 -13
- package/src/layout/index.ts +0 -1
- /package/dist/src/{scope → cli/scope}/index.d.ts +0 -0
- /package/dist/src/config/{url.d.ts → url/url.d.ts} +0 -0
- /package/src/{scope → cli/scope}/index.ts +0 -0
- /package/src/config/{url.ts → url/url.ts} +0 -0
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
|
|
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";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ServerProvider } from "@t3tools/contracts";
|
|
2
|
+
|
|
3
|
+
import { formatTable } from "./human.ts";
|
|
4
|
+
|
|
5
|
+
export function formatModelsHuman(providers: ReadonlyArray<ServerProvider>) {
|
|
6
|
+
if (providers.length === 0) {
|
|
7
|
+
return "no models found\n";
|
|
8
|
+
}
|
|
9
|
+
return providers
|
|
10
|
+
.map((provider) => {
|
|
11
|
+
const models = provider.models;
|
|
12
|
+
const displayName = provider.displayName ?? provider.instanceId;
|
|
13
|
+
const header = `${displayName} (${provider.instanceId}) - ${provider.status}`;
|
|
14
|
+
if (models.length === 0) {
|
|
15
|
+
return `${header}\n no models`;
|
|
16
|
+
}
|
|
17
|
+
return `${header}\n${formatTable(
|
|
18
|
+
[
|
|
19
|
+
{ header: "slug", value: (model) => model.slug, maxWidth: 40 },
|
|
20
|
+
{ header: "name", value: (model) => model.name, maxWidth: 48 },
|
|
21
|
+
{ header: "custom", value: (model) => (model.isCustom ? "yes" : "no"), maxWidth: 6 },
|
|
22
|
+
{ header: "sub-provider", value: (model) => model.subProvider ?? "-", maxWidth: 24 },
|
|
23
|
+
],
|
|
24
|
+
models,
|
|
25
|
+
)}`;
|
|
26
|
+
})
|
|
27
|
+
.join("\n\n")
|
|
28
|
+
.concat("\n");
|
|
29
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as Option from "effect/Option";
|
|
2
|
+
|
|
3
|
+
import type { T3CliEnvShape } from "../../config/env/env.ts";
|
|
4
|
+
import type { CliRuntime } from "../runtime/service.ts";
|
|
5
|
+
|
|
6
|
+
export const humanJsonFormatChoices = ["auto", "human", "json"] as const;
|
|
7
|
+
export const humanNdjsonFormatChoices = ["auto", "human", "ndjson"] as const;
|
|
8
|
+
export const humanJsonNdjsonFormatChoices = ["auto", "human", "json", "ndjson"] as const;
|
|
9
|
+
|
|
10
|
+
export type HumanJsonFormat = (typeof humanJsonFormatChoices)[number];
|
|
11
|
+
export type HumanNdjsonFormat = (typeof humanNdjsonFormatChoices)[number];
|
|
12
|
+
export type HumanJsonNdjsonFormat = (typeof humanJsonNdjsonFormatChoices)[number];
|
|
13
|
+
|
|
14
|
+
export function resolveOutputFormat<T extends "json" | "ndjson">(
|
|
15
|
+
format: "auto" | "human" | T,
|
|
16
|
+
cliRuntime: CliRuntime["Service"],
|
|
17
|
+
t3CliEnv: T3CliEnvShape,
|
|
18
|
+
nonHumanFormat: T,
|
|
19
|
+
): "human" | T {
|
|
20
|
+
if (format !== "auto") {
|
|
21
|
+
return format;
|
|
22
|
+
}
|
|
23
|
+
return isHumanTerminal(cliRuntime, t3CliEnv) ? "human" : nonHumanFormat;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function canRenderLiveTerminal(cliRuntime: CliRuntime["Service"], t3CliEnv: T3CliEnvShape) {
|
|
27
|
+
return (
|
|
28
|
+
cliRuntime.stderrIsTTY &&
|
|
29
|
+
Option.getOrElse(t3CliEnv.term, () => "") !== "dumb" &&
|
|
30
|
+
!isAgentEnvironment(t3CliEnv)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isInteractiveHumanTerminal(
|
|
35
|
+
cliRuntime: CliRuntime["Service"],
|
|
36
|
+
t3CliEnv: T3CliEnvShape,
|
|
37
|
+
) {
|
|
38
|
+
return (
|
|
39
|
+
cliRuntime.stdoutIsTTY &&
|
|
40
|
+
!isAgentEnvironment(t3CliEnv) &&
|
|
41
|
+
Option.getOrElse(t3CliEnv.term, () => "") !== "dumb"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isHumanTerminal(cliRuntime: CliRuntime["Service"], t3CliEnv: T3CliEnvShape) {
|
|
46
|
+
return isInteractiveHumanTerminal(cliRuntime, t3CliEnv);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function isAgentEnvironment(t3CliEnv: T3CliEnvShape) {
|
|
50
|
+
return t3CliEnv.ci || t3CliEnv.codexCi || t3CliEnv.codexThreadId || t3CliEnv.t3cliAgent;
|
|
51
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { OrchestrationProjectShell } from "@t3tools/contracts";
|
|
2
|
+
|
|
3
|
+
import { formatRecord, formatTable } from "./human.ts";
|
|
4
|
+
|
|
5
|
+
export function formatProjectsHuman(projects: ReadonlyArray<OrchestrationProjectShell>) {
|
|
6
|
+
if (projects.length === 0) {
|
|
7
|
+
return "no projects\n";
|
|
8
|
+
}
|
|
9
|
+
return `${formatTable(
|
|
10
|
+
[
|
|
11
|
+
{ header: "title", value: (project) => project.title, maxWidth: 32 },
|
|
12
|
+
{ header: "id", value: (project) => project.id, maxWidth: 40 },
|
|
13
|
+
{ header: "path", value: (project) => project.workspaceRoot, maxWidth: 72 },
|
|
14
|
+
],
|
|
15
|
+
projects,
|
|
16
|
+
)}\n`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function formatProjectAddedHuman(project: OrchestrationProjectShell) {
|
|
20
|
+
return `project added\n${formatRecord([
|
|
21
|
+
{ field: "title", value: project.title },
|
|
22
|
+
{ field: "id", value: project.id },
|
|
23
|
+
{ field: "path", value: project.workspaceRoot },
|
|
24
|
+
])}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function formatProjectDeletedHuman(input: {
|
|
28
|
+
readonly projectId: string;
|
|
29
|
+
readonly dispatch: { readonly sequence: number };
|
|
30
|
+
}) {
|
|
31
|
+
return `project deleted: ${input.projectId} (sequence ${input.dispatch.sequence})`;
|
|
32
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { TerminalSessionSnapshot, TerminalSummary } from "@t3tools/contracts";
|
|
2
|
+
|
|
3
|
+
import { formatTable } from "./human.ts";
|
|
4
|
+
|
|
5
|
+
type TerminalRow = {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly status: string;
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly cwd: string;
|
|
10
|
+
readonly updated: string;
|
|
11
|
+
readonly process: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function formatTerminalListHuman(terminals: ReadonlyArray<TerminalSummary>) {
|
|
15
|
+
if (terminals.length === 0) {
|
|
16
|
+
return "no terminals\n";
|
|
17
|
+
}
|
|
18
|
+
return `${formatTerminalTable(terminals.map(toSummaryRow))}\n`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function formatTerminalCreatedHuman(snapshot: TerminalSessionSnapshot) {
|
|
22
|
+
return `terminal created\n${formatTerminalTable([toSnapshotRow(snapshot)])}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function formatTerminalWrittenHuman(input: {
|
|
26
|
+
readonly terminalId: string;
|
|
27
|
+
readonly threadId: string;
|
|
28
|
+
readonly bytes: number;
|
|
29
|
+
}) {
|
|
30
|
+
return `wrote ${input.bytes} bytes to ${input.terminalId} (${input.threadId})`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function formatTerminalDestroyedHuman(input: {
|
|
34
|
+
readonly terminalId: string;
|
|
35
|
+
readonly threadId: string;
|
|
36
|
+
}) {
|
|
37
|
+
return `destroyed ${input.terminalId} (${input.threadId})`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function formatTerminalTable(rows: ReadonlyArray<TerminalRow>) {
|
|
41
|
+
return formatTable(
|
|
42
|
+
[
|
|
43
|
+
{ header: "id", value: (row) => row.id, maxWidth: 44 },
|
|
44
|
+
{ header: "status", value: (row) => row.status, maxWidth: 8 },
|
|
45
|
+
{ header: "label", value: (row) => row.label, maxWidth: 24 },
|
|
46
|
+
{ header: "cwd", value: (row) => row.cwd, maxWidth: 40 },
|
|
47
|
+
{ header: "updated", value: (row) => row.updated, maxWidth: 24 },
|
|
48
|
+
{ header: "process", value: (row) => row.process, maxWidth: 20 },
|
|
49
|
+
],
|
|
50
|
+
rows,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function toSummaryRow(terminal: TerminalSummary): TerminalRow {
|
|
55
|
+
return {
|
|
56
|
+
id: terminal.terminalId,
|
|
57
|
+
status: terminal.status,
|
|
58
|
+
label: terminal.label,
|
|
59
|
+
cwd: terminal.cwd,
|
|
60
|
+
updated: terminal.updatedAt,
|
|
61
|
+
process: formatProcessInfo(terminal),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function toSnapshotRow(snapshot: TerminalSessionSnapshot): TerminalRow {
|
|
66
|
+
return {
|
|
67
|
+
id: snapshot.terminalId,
|
|
68
|
+
status: snapshot.status,
|
|
69
|
+
label: snapshot.label,
|
|
70
|
+
cwd: snapshot.cwd,
|
|
71
|
+
updated: snapshot.updatedAt,
|
|
72
|
+
process: formatProcessInfo(snapshot),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function formatProcessInfo(input: {
|
|
77
|
+
readonly pid: number | null;
|
|
78
|
+
readonly exitCode: number | null;
|
|
79
|
+
readonly exitSignal: number | null;
|
|
80
|
+
}) {
|
|
81
|
+
const parts = [
|
|
82
|
+
typeof input.pid === "number" ? `pid ${input.pid}` : null,
|
|
83
|
+
typeof input.exitCode === "number" ? `exit ${input.exitCode}` : null,
|
|
84
|
+
typeof input.exitSignal === "number" ? `sig ${input.exitSignal}` : null,
|
|
85
|
+
].filter((part): part is string => part !== null);
|
|
86
|
+
return parts.length > 0 ? parts.join(", ") : "-";
|
|
87
|
+
}
|
|
@@ -2,9 +2,9 @@ import "vite-plus/test/config";
|
|
|
2
2
|
|
|
3
3
|
import { assert, describe, it } from "@effect/vitest";
|
|
4
4
|
import { fromPartial } from "@total-typescript/shoehorn";
|
|
5
|
-
import type { OrchestrationThreadShell } from "
|
|
5
|
+
import type { OrchestrationThreadShell } from "@t3tools/contracts";
|
|
6
6
|
|
|
7
|
-
import { formatThreadsHuman } from "./thread
|
|
7
|
+
import { formatThreadsHuman } from "./thread.ts";
|
|
8
8
|
|
|
9
9
|
describe("formatThreadsHuman", () => {
|
|
10
10
|
it("appends (archived) for archived threads", () => {
|
|
@@ -26,7 +26,9 @@ describe("formatThreadsHuman", () => {
|
|
|
26
26
|
];
|
|
27
27
|
|
|
28
28
|
const output = formatThreadsHuman(threads);
|
|
29
|
-
assert.include(output, "
|
|
30
|
-
assert.include(output, "
|
|
29
|
+
assert.include(output, "title id status updated flags");
|
|
30
|
+
assert.include(output, "Active thread active-1");
|
|
31
|
+
assert.include(output, "Archived thread archived-1");
|
|
32
|
+
assert.include(output, "archived");
|
|
31
33
|
});
|
|
32
34
|
});
|