@jingyi0605/codingns 0.1.0 → 0.1.2
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 +14 -0
- package/dist/public/assets/TerminalPage-Dr7knYq2.js +55 -0
- package/dist/public/assets/index-BpUi6zoT.js +108 -0
- package/dist/public/assets/index-NMtdQNda.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/server/config/env.js +72 -7
- package/dist/server/config/env.js.map +1 -1
- package/dist/server/config/opencode-base-url-resolver.d.ts +13 -8
- package/dist/server/config/opencode-base-url-resolver.js +117 -147
- package/dist/server/config/opencode-base-url-resolver.js.map +1 -1
- package/dist/server/config/opencode-system-probe-helper-client.d.ts +18 -0
- package/dist/server/config/opencode-system-probe-helper-client.js +127 -0
- package/dist/server/config/opencode-system-probe-helper-client.js.map +1 -0
- package/dist/server/config/opencode-system-probe-helper-process.d.ts +1 -0
- package/dist/server/config/opencode-system-probe-helper-process.js +208 -0
- package/dist/server/config/opencode-system-probe-helper-process.js.map +1 -0
- package/dist/server/modules/git/git-command-helper-client.d.ts +25 -0
- package/dist/server/modules/git/git-command-helper-client.js +143 -0
- package/dist/server/modules/git/git-command-helper-client.js.map +1 -0
- package/dist/server/modules/git/git-command-helper-process.d.ts +1 -0
- package/dist/server/modules/git/git-command-helper-process.js +237 -0
- package/dist/server/modules/git/git-command-helper-process.js.map +1 -0
- package/dist/server/modules/git/git-command-runner.d.ts +8 -0
- package/dist/server/modules/git/git-command-runner.js +77 -6
- package/dist/server/modules/git/git-command-runner.js.map +1 -1
- package/dist/server/modules/git/git-controller.d.ts +4 -0
- package/dist/server/modules/git/git-controller.js +4 -1
- package/dist/server/modules/git/git-controller.js.map +1 -1
- package/dist/server/modules/git/git-read-service.d.ts +2 -1
- package/dist/server/modules/git/git-read-service.js +30 -0
- package/dist/server/modules/git/git-read-service.js.map +1 -1
- package/dist/server/modules/git/git-write-service.d.ts +1 -1
- package/dist/server/modules/git/git-write-service.js +8 -7
- package/dist/server/modules/git/git-write-service.js.map +1 -1
- package/dist/server/modules/git/types.d.ts +5 -0
- package/dist/server/modules/preferences/common.d.ts +2 -0
- package/dist/server/modules/preferences/common.js +13 -0
- package/dist/server/modules/preferences/common.js.map +1 -0
- package/dist/server/modules/preferences/profile-controller.d.ts +11 -0
- package/dist/server/modules/preferences/profile-controller.js +14 -0
- package/dist/server/modules/preferences/profile-controller.js.map +1 -0
- package/dist/server/modules/preferences/profile-service.d.ts +17 -0
- package/dist/server/modules/preferences/profile-service.js +213 -0
- package/dist/server/modules/preferences/profile-service.js.map +1 -0
- package/dist/server/modules/preferences/quick-phrase-controller.js +2 -12
- package/dist/server/modules/preferences/quick-phrase-controller.js.map +1 -1
- package/dist/server/modules/provider/codex-model-options.js +26 -165
- package/dist/server/modules/provider/codex-model-options.js.map +1 -1
- package/dist/server/modules/provider/opencode-model-options.js +6 -71
- package/dist/server/modules/provider/opencode-model-options.js.map +1 -1
- package/dist/server/modules/provider/provider-controller.d.ts +2 -0
- package/dist/server/modules/provider/provider-controller.js +21 -1
- package/dist/server/modules/provider/provider-controller.js.map +1 -1
- package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +25 -0
- package/dist/server/modules/provider/provider-discovery-helper-client.js +114 -0
- package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -0
- package/dist/server/modules/provider/provider-discovery-helper-process.d.ts +1 -0
- package/dist/server/modules/provider/provider-discovery-helper-process.js +296 -0
- package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -0
- package/dist/server/modules/sessions/claude-runtime-helper-client.d.ts +28 -0
- package/dist/server/modules/sessions/claude-runtime-helper-client.js +221 -0
- package/dist/server/modules/sessions/claude-runtime-helper-client.js.map +1 -0
- package/dist/server/modules/sessions/claude-runtime-helper-process.d.ts +1 -0
- package/dist/server/modules/sessions/claude-runtime-helper-process.js +146 -0
- package/dist/server/modules/sessions/claude-runtime-helper-process.js.map +1 -0
- package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +16 -0
- package/dist/server/modules/sessions/codex-app-server-helper-client.js +237 -0
- package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -0
- package/dist/server/modules/sessions/codex-app-server-helper-process.d.ts +1 -0
- package/dist/server/modules/sessions/codex-app-server-helper-process.js +484 -0
- package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -0
- package/dist/server/modules/sessions/session-activity-authority-service.d.ts +52 -0
- package/dist/server/modules/sessions/session-activity-authority-service.js +377 -0
- package/dist/server/modules/sessions/session-activity-authority-service.js.map +1 -0
- package/dist/server/modules/sessions/session-activity-inspector.js +80 -40
- package/dist/server/modules/sessions/session-activity-inspector.js.map +1 -1
- package/dist/server/modules/sessions/session-controller.d.ts +15 -1
- package/dist/server/modules/sessions/session-controller.js +14 -1
- package/dist/server/modules/sessions/session-controller.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.d.ts +8 -3
- package/dist/server/modules/sessions/session-history-service.js +303 -64
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +40 -4
- package/dist/server/modules/sessions/session-live-runtime-service.js +334 -44
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/sessions/session-permission-request-service.d.ts +175 -0
- package/dist/server/modules/sessions/session-permission-request-service.js +1615 -0
- package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -0
- package/dist/server/modules/sessions/session-provider-error-mapper.js +14 -0
- package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
- package/dist/server/modules/terminal/command-template-service.d.ts +2 -2
- package/dist/server/modules/terminal/command-template-service.js +3 -3
- package/dist/server/modules/terminal/command-template-service.js.map +1 -1
- package/dist/server/modules/terminal/runtime/adapters/conpty-control-helper-client.d.ts +24 -0
- package/dist/server/modules/terminal/runtime/adapters/conpty-control-helper-client.js +104 -0
- package/dist/server/modules/terminal/runtime/adapters/conpty-control-helper-client.js.map +1 -0
- package/dist/server/modules/terminal/runtime/adapters/conpty-control-helper-process.d.ts +1 -0
- package/dist/server/modules/terminal/runtime/adapters/conpty-control-helper-process.js +96 -0
- package/dist/server/modules/terminal/runtime/adapters/conpty-control-helper-process.js.map +1 -0
- package/dist/server/modules/terminal/runtime/adapters/conpty-runtime-adapter.d.ts +37 -0
- package/dist/server/modules/terminal/runtime/adapters/conpty-runtime-adapter.js +123 -0
- package/dist/server/modules/terminal/runtime/adapters/conpty-runtime-adapter.js.map +1 -0
- package/dist/server/modules/terminal/runtime/adapters/embedded-pty-runtime-adapter.d.ts +12 -5
- package/dist/server/modules/terminal/runtime/adapters/embedded-pty-runtime-adapter.js +44 -4
- package/dist/server/modules/terminal/runtime/adapters/embedded-pty-runtime-adapter.js.map +1 -1
- package/dist/server/modules/terminal/runtime/adapters/tmux-helper-client.d.ts +14 -0
- package/dist/server/modules/terminal/runtime/adapters/tmux-helper-client.js +105 -0
- package/dist/server/modules/terminal/runtime/adapters/tmux-helper-client.js.map +1 -0
- package/dist/server/modules/terminal/runtime/adapters/tmux-helper-process.d.ts +1 -0
- package/dist/server/modules/terminal/runtime/adapters/tmux-helper-process.js +67 -0
- package/dist/server/modules/terminal/runtime/adapters/tmux-helper-process.js.map +1 -0
- package/dist/server/modules/terminal/runtime/adapters/tmux-runtime-adapter.d.ts +8 -12
- package/dist/server/modules/terminal/runtime/adapters/tmux-runtime-adapter.js +112 -21
- package/dist/server/modules/terminal/runtime/adapters/tmux-runtime-adapter.js.map +1 -1
- package/dist/server/modules/terminal/runtime/conpty-runtime-shared.d.ts +25 -0
- package/dist/server/modules/terminal/runtime/conpty-runtime-shared.js +124 -0
- package/dist/server/modules/terminal/runtime/conpty-runtime-shared.js.map +1 -0
- package/dist/server/modules/terminal/runtime/conpty-session-agent-process.d.ts +1 -0
- package/dist/server/modules/terminal/runtime/conpty-session-agent-process.js +205 -0
- package/dist/server/modules/terminal/runtime/conpty-session-agent-process.js.map +1 -0
- package/dist/server/modules/terminal/runtime/conpty-session-attach-client.d.ts +1 -0
- package/dist/server/modules/terminal/runtime/conpty-session-attach-client.js +108 -0
- package/dist/server/modules/terminal/runtime/conpty-session-attach-client.js.map +1 -0
- package/dist/server/modules/terminal/runtime/conpty-session-control-client.d.ts +1 -0
- package/dist/server/modules/terminal/runtime/conpty-session-control-client.js +90 -0
- package/dist/server/modules/terminal/runtime/conpty-session-control-client.js.map +1 -0
- package/dist/server/modules/terminal/runtime/pty-broker-agent-process.d.ts +1 -0
- package/dist/server/modules/terminal/runtime/pty-broker-agent-process.js +179 -0
- package/dist/server/modules/terminal/runtime/pty-broker-agent-process.js.map +1 -0
- package/dist/server/modules/terminal/runtime/pty-broker-client.d.ts +29 -0
- package/dist/server/modules/terminal/runtime/pty-broker-client.js +169 -0
- package/dist/server/modules/terminal/runtime/pty-broker-client.js.map +1 -0
- package/dist/server/modules/terminal/runtime/pty-broker-shared.d.ts +22 -0
- package/dist/server/modules/terminal/runtime/pty-broker-shared.js +123 -0
- package/dist/server/modules/terminal/runtime/pty-broker-shared.js.map +1 -0
- package/dist/server/modules/terminal/runtime/pty-host-attachment-manager.d.ts +1 -0
- package/dist/server/modules/terminal/runtime/pty-host-attachment-manager.js +11 -1
- package/dist/server/modules/terminal/runtime/pty-host-attachment-manager.js.map +1 -1
- package/dist/server/modules/terminal/runtime/terminal-log-file-store.d.ts +1 -0
- package/dist/server/modules/terminal/runtime/terminal-log-file-store.js +7 -1
- package/dist/server/modules/terminal/runtime/terminal-log-file-store.js.map +1 -1
- package/dist/server/modules/terminal/runtime/terminal-log-spooler.js +3 -2
- package/dist/server/modules/terminal/runtime/terminal-log-spooler.js.map +1 -1
- package/dist/server/modules/terminal/runtime/terminal-runtime-adapter.d.ts +5 -3
- package/dist/server/modules/terminal/runtime/terminal-runtime-manager.d.ts +11 -5
- package/dist/server/modules/terminal/runtime/terminal-runtime-manager.js +110 -13
- package/dist/server/modules/terminal/runtime/terminal-runtime-manager.js.map +1 -1
- package/dist/server/modules/terminal/terminal-controller.js +7 -7
- package/dist/server/modules/terminal/terminal-controller.js.map +1 -1
- package/dist/server/modules/terminal/terminal-service.d.ts +30 -14
- package/dist/server/modules/terminal/terminal-service.js +260 -51
- package/dist/server/modules/terminal/terminal-service.js.map +1 -1
- package/dist/server/modules/terminal/terminal-shell.d.ts +4 -0
- package/dist/server/modules/terminal/terminal-shell.js +165 -18
- package/dist/server/modules/terminal/terminal-shell.js.map +1 -1
- package/dist/server/modules/workbench/workspace-file-watcher.d.ts +30 -0
- package/dist/server/modules/workbench/workspace-file-watcher.js +137 -0
- package/dist/server/modules/workbench/workspace-file-watcher.js.map +1 -0
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.js +43 -7
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.js.map +1 -1
- package/dist/server/routes/git.js +1 -0
- package/dist/server/routes/git.js.map +1 -1
- package/dist/server/routes/preferences.d.ts +2 -1
- package/dist/server/routes/preferences.js +3 -1
- package/dist/server/routes/preferences.js.map +1 -1
- package/dist/server/routes/sessions.js +2 -0
- package/dist/server/routes/sessions.js.map +1 -1
- package/dist/server/server/create-server.d.ts +4 -0
- package/dist/server/server/create-server.js +22 -5
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/shared/utils/command-launch.d.ts +6 -0
- package/dist/server/shared/utils/command-launch.js +39 -0
- package/dist/server/shared/utils/command-launch.js.map +1 -0
- package/dist/server/shared/utils/perf-log.d.ts +1 -0
- package/dist/server/shared/utils/perf-log.js +8 -2
- package/dist/server/shared/utils/perf-log.js.map +1 -1
- package/dist/server/shared/utils/permission-debug-log.d.ts +2 -0
- package/dist/server/shared/utils/permission-debug-log.js +40 -0
- package/dist/server/shared/utils/permission-debug-log.js.map +1 -0
- package/dist/server/shared/utils/terminal-debug-log.d.ts +4 -0
- package/dist/server/shared/utils/terminal-debug-log.js +71 -0
- package/dist/server/shared/utils/terminal-debug-log.js.map +1 -0
- package/dist/server/storage/repositories/user-preference-profile-repository.d.ts +8 -0
- package/dist/server/storage/repositories/user-preference-profile-repository.js +46 -0
- package/dist/server/storage/repositories/user-preference-profile-repository.js.map +1 -0
- package/dist/server/storage/sqlite/schema.sql +13 -0
- package/dist/server/types/domain.d.ts +28 -1
- package/dist/server/ws/terminal-ws-hub.d.ts +2 -0
- package/dist/server/ws/terminal-ws-hub.js +42 -5
- package/dist/server/ws/terminal-ws-hub.js.map +1 -1
- package/dist/server/ws/workbench-ws-hub.d.ts +9 -1
- package/dist/server/ws/workbench-ws-hub.js +132 -21
- package/dist/server/ws/workbench-ws-hub.js.map +1 -1
- package/dist/server/ws/ws-server.d.ts +22 -1
- package/dist/server/ws/ws-server.js +134 -46
- package/dist/server/ws/ws-server.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.d.ts +4 -1
- package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js +107 -6
- package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +30 -0
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +103 -0
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +38 -6
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-permissions.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-permissions.js +8 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-permissions.js.map +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js +87 -7
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +8 -4
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +5 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.d.ts +5 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +114 -6
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +23 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +721 -18
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.d.ts +3 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js +106 -23
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js +3 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/types.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/services.d.ts +1 -1
- package/node_modules/@codingns/session-sync-core/dist/services.js +2 -2
- package/node_modules/@codingns/session-sync-core/dist/services.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/types.d.ts +1 -1
- package/package.json +6 -2
- package/scripts/postinstall.mjs +218 -0
- package/dist/public/assets/TerminalPage-Dj_VDew3.js +0 -54
- package/dist/public/assets/index-C1GZV2wq.js +0 -106
- package/dist/public/assets/index-DU7f8NaZ.css +0 -1
|
@@ -2,14 +2,18 @@ import { EventEmitter } from "node:events";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { AppError } from "../../shared/errors/app-error.js";
|
|
4
4
|
import { createId } from "../../shared/utils/id.js";
|
|
5
|
+
import { isTerminalDebugEnabled, logTerminalDebug, terminalDebugNowMs } from "../../shared/utils/terminal-debug-log.js";
|
|
5
6
|
import { nowIso } from "../../shared/utils/time.js";
|
|
6
7
|
import { resolveWorkspaceCwd } from "./terminal-paths.js";
|
|
7
|
-
import { getDefaultShell, resolveRequestedShell } from "./terminal-shell.js";
|
|
8
|
+
import { getDefaultShell, resolveRequestedShell, resolveWindowsPersistentRuntimeType } from "./terminal-shell.js";
|
|
8
9
|
import { captureTmuxPaneContent } from "./runtime/adapters/tmux-runtime-adapter.js";
|
|
10
|
+
import { isConptyRuntimeType } from "./runtime/conpty-runtime-shared.js";
|
|
9
11
|
import { TerminalLogFileStore } from "./runtime/terminal-log-file-store.js";
|
|
10
12
|
import { TerminalLogSpooler } from "./runtime/terminal-log-spooler.js";
|
|
11
13
|
import { TerminalOutputBuffer } from "./runtime/terminal-output-buffer.js";
|
|
12
14
|
import { TerminalRuntimeManager } from "./runtime/terminal-runtime-manager.js";
|
|
15
|
+
const TERMINAL_ACTIVITY_FLUSH_INTERVAL_MS = 2_000;
|
|
16
|
+
const TERMINAL_OUTPUT_FLUSH_INTERVAL_MS = 8;
|
|
13
17
|
export class TerminalService extends EventEmitter {
|
|
14
18
|
db;
|
|
15
19
|
terminalInstanceRepository;
|
|
@@ -17,14 +21,17 @@ export class TerminalService extends EventEmitter {
|
|
|
17
21
|
workspaceService;
|
|
18
22
|
outputBuffer = new TerminalOutputBuffer();
|
|
19
23
|
runtimeManager = new TerminalRuntimeManager();
|
|
20
|
-
lastPersistedActivity = new Map();
|
|
21
24
|
terminalSubscriptionCounts = new Map();
|
|
22
25
|
pendingCloseReasons = new Map();
|
|
23
26
|
pendingDeletedTerminalIds = new Set();
|
|
27
|
+
pendingActivityByTerminalId = new Map();
|
|
28
|
+
pendingInputTraceByTerminalId = new Map();
|
|
29
|
+
pendingOutputByTerminalId = new Map();
|
|
24
30
|
terminalLogSpooler;
|
|
25
31
|
terminalLogFileRepository;
|
|
26
32
|
terminalLogSegmentRepository;
|
|
27
33
|
terminalLogFileStore;
|
|
34
|
+
activityFlushTimer = null;
|
|
28
35
|
isDisposing = false;
|
|
29
36
|
constructor(db, terminalInstanceRepository, terminalRuntimeSessionRepository, workspaceService, _idleTimeoutSeconds, options = {}) {
|
|
30
37
|
super();
|
|
@@ -55,12 +62,12 @@ export class TerminalService extends EventEmitter {
|
|
|
55
62
|
});
|
|
56
63
|
this.recoverRuntimeStates();
|
|
57
64
|
}
|
|
58
|
-
createTerminal(input) {
|
|
65
|
+
async createTerminal(input) {
|
|
59
66
|
const workspace = this.workspaceService.getWorkspaceOrThrow(input.workspaceId);
|
|
60
67
|
const now = nowIso();
|
|
61
68
|
const shell = resolveRequestedShell(sanitizeShell(input.shell) ?? getDefaultShell());
|
|
62
69
|
const cwd = resolveWorkspaceCwd(workspace.path, input.cwd);
|
|
63
|
-
const runtimeType = resolveRequestedRuntimeType(input.runtimeType);
|
|
70
|
+
const runtimeType = resolveRequestedRuntimeType(input.runtimeType, shell);
|
|
64
71
|
const runtimeSessionId = createId();
|
|
65
72
|
const attachTarget = buildAttachTarget(runtimeType, runtimeSessionId);
|
|
66
73
|
const terminal = {
|
|
@@ -104,9 +111,12 @@ export class TerminalService extends EventEmitter {
|
|
|
104
111
|
persist();
|
|
105
112
|
try {
|
|
106
113
|
const env = buildTerminalEnv(input.env);
|
|
107
|
-
const createdInspection = this.runtimeManager.createPersistentSession(terminal, runtimeSession, env);
|
|
108
|
-
|
|
114
|
+
const createdInspection = await this.runtimeManager.createPersistentSession(terminal, runtimeSession, env);
|
|
115
|
+
runtimeSession.agentPid = createdInspection.agentPid ?? runtimeSession.agentPid;
|
|
116
|
+
const attachmentProcessId = await this.runtimeManager.ensureAttached(terminal, runtimeSession, env);
|
|
109
117
|
const processId = createdInspection.shellPid ?? attachmentProcessId;
|
|
118
|
+
terminal.processId = processId;
|
|
119
|
+
runtimeSession.shellPid = processId;
|
|
110
120
|
const runningTerminal = {
|
|
111
121
|
...terminal,
|
|
112
122
|
status: "running",
|
|
@@ -123,6 +133,7 @@ export class TerminalService extends EventEmitter {
|
|
|
123
133
|
});
|
|
124
134
|
this.terminalRuntimeSessionRepository.updateState({
|
|
125
135
|
id: runtimeSession.id,
|
|
136
|
+
agentPid: createdInspection.agentPid ?? runtimeSession.agentPid,
|
|
126
137
|
shellPid: processId,
|
|
127
138
|
state: "running",
|
|
128
139
|
lastCheckedAt: nowIso(),
|
|
@@ -145,23 +156,32 @@ export class TerminalService extends EventEmitter {
|
|
|
145
156
|
});
|
|
146
157
|
this.terminalRuntimeSessionRepository.updateState({
|
|
147
158
|
id: runtimeSession.id,
|
|
159
|
+
agentPid: runtimeSession.agentPid,
|
|
148
160
|
shellPid: null,
|
|
149
161
|
state: "error",
|
|
150
162
|
lastCheckedAt: failedAt,
|
|
151
163
|
lastErrorDetail: error instanceof Error ? error.message : "PTY 启动失败",
|
|
152
164
|
updatedAt: failedAt
|
|
153
165
|
});
|
|
166
|
+
if (runtimeSession.agentPid) {
|
|
167
|
+
try {
|
|
168
|
+
process.kill(runtimeSession.agentPid);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// agent 已退出时忽略。
|
|
172
|
+
}
|
|
173
|
+
}
|
|
154
174
|
const failedTerminal = this.getTerminalOrThrow(terminal.id);
|
|
155
175
|
this.emit("status", failedTerminal);
|
|
156
176
|
throw error;
|
|
157
177
|
}
|
|
158
178
|
}
|
|
159
|
-
listTerminals(workspaceId) {
|
|
179
|
+
async listTerminals(workspaceId) {
|
|
160
180
|
this.workspaceService.getWorkspaceOrThrow(workspaceId);
|
|
161
181
|
const terminals = this.terminalInstanceRepository.listByWorkspace(workspaceId);
|
|
162
182
|
let hasLifecycleChange = false;
|
|
163
183
|
for (const terminal of terminals) {
|
|
164
|
-
if (this.reconcileTerminalRuntime(terminal)) {
|
|
184
|
+
if (await this.reconcileTerminalRuntime(terminal)) {
|
|
165
185
|
hasLifecycleChange = true;
|
|
166
186
|
}
|
|
167
187
|
}
|
|
@@ -169,6 +189,10 @@ export class TerminalService extends EventEmitter {
|
|
|
169
189
|
? this.terminalInstanceRepository.listByWorkspace(workspaceId)
|
|
170
190
|
: terminals;
|
|
171
191
|
}
|
|
192
|
+
listTerminalSnapshotItems(workspaceId) {
|
|
193
|
+
this.workspaceService.getWorkspaceOrThrow(workspaceId);
|
|
194
|
+
return this.terminalInstanceRepository.listByWorkspace(workspaceId);
|
|
195
|
+
}
|
|
172
196
|
getTerminalOrThrow(terminalId) {
|
|
173
197
|
const terminal = this.terminalInstanceRepository.findById(terminalId);
|
|
174
198
|
if (!terminal) {
|
|
@@ -191,7 +215,7 @@ export class TerminalService extends EventEmitter {
|
|
|
191
215
|
}
|
|
192
216
|
return session;
|
|
193
217
|
}
|
|
194
|
-
closeTerminal(terminalId, reason = "user_closed") {
|
|
218
|
+
async closeTerminal(terminalId, reason = "user_closed") {
|
|
195
219
|
const terminal = this.getTerminalOrThrow(terminalId);
|
|
196
220
|
const session = this.getRuntimeSessionOrThrow(terminal.runtimeSessionId);
|
|
197
221
|
this.pendingCloseReasons.set(terminalId, reason);
|
|
@@ -200,7 +224,7 @@ export class TerminalService extends EventEmitter {
|
|
|
200
224
|
return { success: true };
|
|
201
225
|
}
|
|
202
226
|
this.flushTerminalLogs(terminalId);
|
|
203
|
-
const willEmitExit = this.runtimeManager.terminateSession(terminal, session);
|
|
227
|
+
const willEmitExit = await this.runtimeManager.terminateSession(terminal, session);
|
|
204
228
|
if (!willEmitExit) {
|
|
205
229
|
this.finalizeTerminalClosure(terminal, session, {
|
|
206
230
|
requestedClose: true,
|
|
@@ -212,11 +236,12 @@ export class TerminalService extends EventEmitter {
|
|
|
212
236
|
}
|
|
213
237
|
return { success: true };
|
|
214
238
|
}
|
|
215
|
-
deleteTerminal(terminalId) {
|
|
239
|
+
async deleteTerminal(terminalId) {
|
|
216
240
|
const terminal = this.getTerminalOrThrow(terminalId);
|
|
217
241
|
const session = this.getRuntimeSessionOrThrow(terminal.runtimeSessionId);
|
|
218
242
|
this.pendingCloseReasons.delete(terminalId);
|
|
219
|
-
this.
|
|
243
|
+
this.pendingActivityByTerminalId.delete(terminalId);
|
|
244
|
+
this.clearActivityFlushTimerIfIdle();
|
|
220
245
|
this.flushTerminalLogs(terminalId);
|
|
221
246
|
this.pendingDeletedTerminalIds.add(terminalId);
|
|
222
247
|
const deleteRecords = this.db.transaction(() => {
|
|
@@ -232,7 +257,7 @@ export class TerminalService extends EventEmitter {
|
|
|
232
257
|
}
|
|
233
258
|
let willEmitExit = false;
|
|
234
259
|
try {
|
|
235
|
-
willEmitExit = this.runtimeManager.terminateSession(terminal, session);
|
|
260
|
+
willEmitExit = await this.runtimeManager.terminateSession(terminal, session);
|
|
236
261
|
}
|
|
237
262
|
catch (error) {
|
|
238
263
|
this.pendingDeletedTerminalIds.delete(terminalId);
|
|
@@ -250,7 +275,7 @@ export class TerminalService extends EventEmitter {
|
|
|
250
275
|
}
|
|
251
276
|
return { success: true };
|
|
252
277
|
}
|
|
253
|
-
writeInput(terminalId, content) {
|
|
278
|
+
async writeInput(terminalId, content, debugContext = {}) {
|
|
254
279
|
if (!content) {
|
|
255
280
|
throw new AppError({
|
|
256
281
|
statusCode: 400,
|
|
@@ -259,12 +284,31 @@ export class TerminalService extends EventEmitter {
|
|
|
259
284
|
field: "content"
|
|
260
285
|
});
|
|
261
286
|
}
|
|
262
|
-
const
|
|
287
|
+
const serviceWriteStartedAtMs = terminalDebugNowMs();
|
|
288
|
+
const terminal = await this.ensureTerminalInteractive(terminalId);
|
|
263
289
|
this.runtimeManager.write(terminal.id, content);
|
|
290
|
+
const serviceWriteFinishedAtMs = terminalDebugNowMs();
|
|
291
|
+
this.recordPendingInputTrace(terminalId, content, {
|
|
292
|
+
...debugContext,
|
|
293
|
+
serviceWriteStartedAtMs,
|
|
294
|
+
serviceWriteFinishedAtMs
|
|
295
|
+
});
|
|
296
|
+
logTerminalDebug("terminal.input.write_completed", {
|
|
297
|
+
terminalId,
|
|
298
|
+
traceId: debugContext.clientTraceId ?? null,
|
|
299
|
+
charCount: content.length,
|
|
300
|
+
ensureAndWriteMs: serviceWriteFinishedAtMs - serviceWriteStartedAtMs,
|
|
301
|
+
wsToWriteMs: debugContext.wsReceivedAtMs === null || debugContext.wsReceivedAtMs === undefined
|
|
302
|
+
? null
|
|
303
|
+
: serviceWriteFinishedAtMs - debugContext.wsReceivedAtMs,
|
|
304
|
+
clientToWriteMs: debugContext.clientSentAtMs === null || debugContext.clientSentAtMs === undefined
|
|
305
|
+
? null
|
|
306
|
+
: serviceWriteFinishedAtMs - debugContext.clientSentAtMs
|
|
307
|
+
});
|
|
264
308
|
this.touchLastActiveAt(terminalId);
|
|
265
309
|
return { accepted: true };
|
|
266
310
|
}
|
|
267
|
-
resizeTerminal(terminalId, cols, rows) {
|
|
311
|
+
async resizeTerminal(terminalId, cols, rows) {
|
|
268
312
|
if (!Number.isInteger(cols) || !Number.isInteger(rows) || cols < 20 || rows < 5) {
|
|
269
313
|
throw new AppError({
|
|
270
314
|
statusCode: 400,
|
|
@@ -273,12 +317,12 @@ export class TerminalService extends EventEmitter {
|
|
|
273
317
|
field: "cols"
|
|
274
318
|
});
|
|
275
319
|
}
|
|
276
|
-
const terminal = this.ensureTerminalInteractive(terminalId);
|
|
320
|
+
const terminal = await this.ensureTerminalInteractive(terminalId);
|
|
277
321
|
this.runtimeManager.resize(terminal.id, cols, rows);
|
|
278
322
|
this.touchLastActiveAt(terminalId);
|
|
279
323
|
return { accepted: true };
|
|
280
324
|
}
|
|
281
|
-
subscribeTerminal(terminalId, lastCursor, callbacks) {
|
|
325
|
+
async subscribeTerminal(terminalId, lastCursor, callbacks) {
|
|
282
326
|
const outputListener = (event) => {
|
|
283
327
|
if (event.terminalId !== terminalId) {
|
|
284
328
|
return;
|
|
@@ -304,7 +348,7 @@ export class TerminalService extends EventEmitter {
|
|
|
304
348
|
this.on("exit", exitListener);
|
|
305
349
|
this.retainTerminalSubscription(terminalId);
|
|
306
350
|
try {
|
|
307
|
-
const current = this.ensureTerminalAttachedForSubscription(terminalId);
|
|
351
|
+
const current = await this.ensureTerminalAttachedForSubscription(terminalId);
|
|
308
352
|
const backfill = this.outputBuffer.readSince(terminalId, lastCursor);
|
|
309
353
|
void callbacks.onStatus(current);
|
|
310
354
|
void callbacks.onBackfill(backfill);
|
|
@@ -332,17 +376,20 @@ export class TerminalService extends EventEmitter {
|
|
|
332
376
|
}
|
|
333
377
|
async dispose() {
|
|
334
378
|
this.isDisposing = true;
|
|
379
|
+
this.flushPendingTerminalOutput();
|
|
380
|
+
this.flushPendingActivity();
|
|
335
381
|
this.terminalLogSpooler?.flushAll();
|
|
336
382
|
this.runtimeManager.closeAllAttachments();
|
|
337
383
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
338
384
|
}
|
|
339
|
-
readTerminalHistory(terminalId, beforeSeq, limit) {
|
|
385
|
+
async readTerminalHistory(terminalId, beforeSeq, limit) {
|
|
340
386
|
const terminal = this.getTerminalOrThrow(terminalId);
|
|
387
|
+
this.flushTerminalLogs(terminalId);
|
|
341
388
|
const runtimeSession = terminal.runtimeSessionId
|
|
342
389
|
? this.terminalRuntimeSessionRepository.findById(terminal.runtimeSessionId)
|
|
343
390
|
: null;
|
|
344
391
|
if (beforeSeq === 1 && runtimeSession?.runtimeType === "tmux") {
|
|
345
|
-
const capturedContent = normalizeTerminalHistoryContent(captureTmuxPaneContent(runtimeSession.sessionKey));
|
|
392
|
+
const capturedContent = normalizeTerminalHistoryContent(await captureTmuxPaneContent(runtimeSession.sessionKey));
|
|
346
393
|
const capturedLineCount = countTerminalHistoryLines(capturedContent);
|
|
347
394
|
return {
|
|
348
395
|
terminalId,
|
|
@@ -427,16 +474,16 @@ export class TerminalService extends EventEmitter {
|
|
|
427
474
|
if (this.isDisposing) {
|
|
428
475
|
return;
|
|
429
476
|
}
|
|
430
|
-
const
|
|
431
|
-
|
|
477
|
+
const batch = this.getOrCreatePendingOutputBatch(terminalId);
|
|
478
|
+
batch.contents.push(content);
|
|
479
|
+
if (batch.timer !== null) {
|
|
432
480
|
return;
|
|
433
481
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
});
|
|
482
|
+
batch.timer = setTimeout(() => {
|
|
483
|
+
batch.timer = null;
|
|
484
|
+
this.flushPendingTerminalOutput(terminalId);
|
|
485
|
+
}, TERMINAL_OUTPUT_FLUSH_INTERVAL_MS);
|
|
486
|
+
batch.timer.unref?.();
|
|
440
487
|
}
|
|
441
488
|
handleRuntimeExit(event) {
|
|
442
489
|
if (this.isDisposing) {
|
|
@@ -444,7 +491,9 @@ export class TerminalService extends EventEmitter {
|
|
|
444
491
|
}
|
|
445
492
|
if (this.pendingDeletedTerminalIds.delete(event.terminalId)) {
|
|
446
493
|
this.pendingCloseReasons.delete(event.terminalId);
|
|
447
|
-
this.
|
|
494
|
+
this.pendingActivityByTerminalId.delete(event.terminalId);
|
|
495
|
+
this.clearActivityFlushTimerIfIdle();
|
|
496
|
+
this.pendingInputTraceByTerminalId.delete(event.terminalId);
|
|
448
497
|
this.clearTerminalLogs(event.terminalId);
|
|
449
498
|
return;
|
|
450
499
|
}
|
|
@@ -461,17 +510,16 @@ export class TerminalService extends EventEmitter {
|
|
|
461
510
|
if (this.isDisposing) {
|
|
462
511
|
return;
|
|
463
512
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (now - previous >= 500) {
|
|
467
|
-
this.lastPersistedActivity.set(terminalId, now);
|
|
468
|
-
this.terminalInstanceRepository.touchLastActiveAt(terminalId, nowIso(new Date(now)));
|
|
469
|
-
}
|
|
513
|
+
this.pendingActivityByTerminalId.set(terminalId, nowIso());
|
|
514
|
+
this.scheduleActivityFlush();
|
|
470
515
|
}
|
|
471
|
-
ensureTerminalInteractive(terminalId) {
|
|
516
|
+
async ensureTerminalInteractive(terminalId) {
|
|
472
517
|
const terminal = this.getTerminalOrThrow(terminalId);
|
|
518
|
+
if (terminal.status === "running" && this.runtimeManager.isAttached(terminal.id)) {
|
|
519
|
+
return terminal;
|
|
520
|
+
}
|
|
473
521
|
const session = this.getRuntimeSessionOrThrow(terminal.runtimeSessionId);
|
|
474
|
-
const nextTerminal = this.ensureTerminalRunning(terminal, session, true);
|
|
522
|
+
const nextTerminal = await this.ensureTerminalRunning(terminal, session, true);
|
|
475
523
|
if (nextTerminal.status !== "running") {
|
|
476
524
|
throw new AppError({
|
|
477
525
|
statusCode: 409,
|
|
@@ -481,16 +529,16 @@ export class TerminalService extends EventEmitter {
|
|
|
481
529
|
}
|
|
482
530
|
return nextTerminal;
|
|
483
531
|
}
|
|
484
|
-
ensureTerminalAttachedForSubscription(terminalId) {
|
|
532
|
+
async ensureTerminalAttachedForSubscription(terminalId) {
|
|
485
533
|
const terminal = this.getTerminalOrThrow(terminalId);
|
|
486
534
|
const session = this.getRuntimeSessionOrThrow(terminal.runtimeSessionId);
|
|
487
535
|
return this.ensureTerminalRunning(terminal, session, true);
|
|
488
536
|
}
|
|
489
|
-
ensureTerminalRunning(terminal, session, ensureAttached) {
|
|
537
|
+
async ensureTerminalRunning(terminal, session, ensureAttached) {
|
|
490
538
|
if (terminal.status === "closed" || terminal.status === "error") {
|
|
491
539
|
return terminal;
|
|
492
540
|
}
|
|
493
|
-
const inspection = this.runtimeManager.inspectPersistentSession(terminal, session);
|
|
541
|
+
const inspection = await this.runtimeManager.inspectPersistentSession(terminal, session);
|
|
494
542
|
if (!inspection.alive) {
|
|
495
543
|
if (shouldMarkRuntimeLost(terminal, inspection.detail)) {
|
|
496
544
|
return this.markTerminalLost(terminal, session, inspection.detail);
|
|
@@ -498,12 +546,18 @@ export class TerminalService extends EventEmitter {
|
|
|
498
546
|
return this.markTerminalError(terminal, session, inspection.detail ?? buildMissingProcessStatusDetail(terminal));
|
|
499
547
|
}
|
|
500
548
|
if (ensureAttached) {
|
|
501
|
-
|
|
549
|
+
if (session.runtimeType === "embedded-pty") {
|
|
550
|
+
if (!this.runtimeManager.isAttached(terminal.id)) {
|
|
551
|
+
return this.markTerminalError(terminal, session, inspection.detail ?? "EMBEDDED_RUNTIME_NOT_ATTACHED");
|
|
552
|
+
}
|
|
553
|
+
return this.markTerminalRunning(terminal, session, inspection.shellPid ?? this.runtimeManager.getProcessId(terminal.id), inspection.detail);
|
|
554
|
+
}
|
|
555
|
+
const attachmentProcessId = this.runtimeManager.ensureLegacyAttached(terminal, session, buildTerminalEnv());
|
|
502
556
|
return this.markTerminalRunning(terminal, session, inspection.shellPid ?? attachmentProcessId, inspection.detail);
|
|
503
557
|
}
|
|
504
558
|
return this.markTerminalRunning(terminal, session, inspection.shellPid, inspection.detail);
|
|
505
559
|
}
|
|
506
|
-
reconcileTerminalRuntime(terminal) {
|
|
560
|
+
async reconcileTerminalRuntime(terminal) {
|
|
507
561
|
if (terminal.status === "closed" || terminal.status === "error") {
|
|
508
562
|
return false;
|
|
509
563
|
}
|
|
@@ -513,7 +567,7 @@ export class TerminalService extends EventEmitter {
|
|
|
513
567
|
statusDetail: terminal.statusDetail
|
|
514
568
|
});
|
|
515
569
|
const session = this.getRuntimeSessionOrThrow(terminal.runtimeSessionId);
|
|
516
|
-
const nextTerminal = this.ensureTerminalRunning(terminal, session, false);
|
|
570
|
+
const nextTerminal = await this.ensureTerminalRunning(terminal, session, false);
|
|
517
571
|
const after = JSON.stringify({
|
|
518
572
|
status: nextTerminal.status,
|
|
519
573
|
processId: nextTerminal.processId,
|
|
@@ -544,6 +598,7 @@ export class TerminalService extends EventEmitter {
|
|
|
544
598
|
});
|
|
545
599
|
this.terminalRuntimeSessionRepository.updateState({
|
|
546
600
|
id: session.id,
|
|
601
|
+
agentPid: session.agentPid,
|
|
547
602
|
shellPid: processId,
|
|
548
603
|
state: "running",
|
|
549
604
|
lastCheckedAt: updatedAt,
|
|
@@ -558,6 +613,7 @@ export class TerminalService extends EventEmitter {
|
|
|
558
613
|
const updatedAt = nowIso();
|
|
559
614
|
this.terminalRuntimeSessionRepository.updateState({
|
|
560
615
|
id: session.id,
|
|
616
|
+
agentPid: session.agentPid,
|
|
561
617
|
shellPid: terminal.processId,
|
|
562
618
|
state: "lost",
|
|
563
619
|
lastCheckedAt: updatedAt,
|
|
@@ -578,6 +634,9 @@ export class TerminalService extends EventEmitter {
|
|
|
578
634
|
return updated;
|
|
579
635
|
}
|
|
580
636
|
markTerminalError(terminal, session, detail) {
|
|
637
|
+
this.pendingActivityByTerminalId.delete(terminal.id);
|
|
638
|
+
this.clearActivityFlushTimerIfIdle();
|
|
639
|
+
this.pendingInputTraceByTerminalId.delete(terminal.id);
|
|
581
640
|
const closedAt = nowIso();
|
|
582
641
|
this.terminalInstanceRepository.updateLifecycle({
|
|
583
642
|
id: terminal.id,
|
|
@@ -590,6 +649,7 @@ export class TerminalService extends EventEmitter {
|
|
|
590
649
|
});
|
|
591
650
|
this.terminalRuntimeSessionRepository.updateState({
|
|
592
651
|
id: session.id,
|
|
652
|
+
agentPid: session.agentPid,
|
|
593
653
|
shellPid: terminal.processId,
|
|
594
654
|
state: "error",
|
|
595
655
|
lastCheckedAt: closedAt,
|
|
@@ -603,10 +663,13 @@ export class TerminalService extends EventEmitter {
|
|
|
603
663
|
finalizeTerminalClosure(terminal, session, event) {
|
|
604
664
|
const closeReason = this.pendingCloseReasons.get(terminal.id) ?? "user_closed";
|
|
605
665
|
this.pendingCloseReasons.delete(terminal.id);
|
|
666
|
+
this.pendingActivityByTerminalId.delete(terminal.id);
|
|
667
|
+
this.clearActivityFlushTimerIfIdle();
|
|
668
|
+
this.pendingInputTraceByTerminalId.delete(terminal.id);
|
|
606
669
|
const finishedAt = nowIso();
|
|
607
670
|
const status = event.requestedClose || event.exitCode === 0 ? "closed" : "error";
|
|
608
671
|
const statusDetail = status === "error"
|
|
609
|
-
? event.sessionDetail
|
|
672
|
+
? normalizeTerminalErrorDetail(event.sessionDetail, event.exitCode)
|
|
610
673
|
: resolveClosedStatusDetail(closeReason, terminal.statusDetail);
|
|
611
674
|
this.terminalInstanceRepository.updateLifecycle({
|
|
612
675
|
id: terminal.id,
|
|
@@ -619,6 +682,7 @@ export class TerminalService extends EventEmitter {
|
|
|
619
682
|
});
|
|
620
683
|
this.terminalRuntimeSessionRepository.updateState({
|
|
621
684
|
id: session.id,
|
|
685
|
+
agentPid: session.agentPid,
|
|
622
686
|
shellPid: event.shellPid,
|
|
623
687
|
state: status === "closed" ? "closed" : "error",
|
|
624
688
|
lastCheckedAt: finishedAt,
|
|
@@ -658,7 +722,7 @@ export class TerminalService extends EventEmitter {
|
|
|
658
722
|
}, "RUNTIME_SESSION_MISSING");
|
|
659
723
|
continue;
|
|
660
724
|
}
|
|
661
|
-
this.ensureTerminalRunning(terminal, session, false);
|
|
725
|
+
void this.ensureTerminalRunning(terminal, session, false);
|
|
662
726
|
}
|
|
663
727
|
}
|
|
664
728
|
retainTerminalSubscription(terminalId) {
|
|
@@ -689,14 +753,134 @@ export class TerminalService extends EventEmitter {
|
|
|
689
753
|
this.runtimeManager.detach(terminalId);
|
|
690
754
|
}
|
|
691
755
|
flushTerminalLogs(terminalId) {
|
|
756
|
+
this.flushPendingTerminalOutput(terminalId);
|
|
692
757
|
this.terminalLogSpooler?.flushTerminal(terminalId);
|
|
693
758
|
}
|
|
694
759
|
clearTerminalLogs(terminalId) {
|
|
760
|
+
const pendingOutput = this.pendingOutputByTerminalId.get(terminalId);
|
|
761
|
+
if (pendingOutput?.timer) {
|
|
762
|
+
clearTimeout(pendingOutput.timer);
|
|
763
|
+
}
|
|
764
|
+
this.pendingOutputByTerminalId.delete(terminalId);
|
|
765
|
+
this.pendingInputTraceByTerminalId.delete(terminalId);
|
|
695
766
|
this.outputBuffer.clear(terminalId);
|
|
696
767
|
this.terminalLogSegmentRepository?.deleteByTerminalId(terminalId);
|
|
697
768
|
this.terminalLogFileRepository?.deleteByTerminalId(terminalId);
|
|
698
769
|
this.terminalLogSpooler?.deleteTerminalLogs(terminalId);
|
|
699
770
|
}
|
|
771
|
+
getOrCreatePendingOutputBatch(terminalId) {
|
|
772
|
+
let batch = this.pendingOutputByTerminalId.get(terminalId);
|
|
773
|
+
if (!batch) {
|
|
774
|
+
batch = {
|
|
775
|
+
contents: [],
|
|
776
|
+
timer: null
|
|
777
|
+
};
|
|
778
|
+
this.pendingOutputByTerminalId.set(terminalId, batch);
|
|
779
|
+
}
|
|
780
|
+
return batch;
|
|
781
|
+
}
|
|
782
|
+
flushPendingTerminalOutput(terminalId) {
|
|
783
|
+
if (terminalId) {
|
|
784
|
+
const batch = this.pendingOutputByTerminalId.get(terminalId);
|
|
785
|
+
if (!batch) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (batch.timer) {
|
|
789
|
+
clearTimeout(batch.timer);
|
|
790
|
+
batch.timer = null;
|
|
791
|
+
}
|
|
792
|
+
const content = batch.contents.join("");
|
|
793
|
+
this.pendingOutputByTerminalId.delete(terminalId);
|
|
794
|
+
this.commitTerminalOutput(terminalId, content);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
for (const nextTerminalId of [...this.pendingOutputByTerminalId.keys()]) {
|
|
798
|
+
this.flushPendingTerminalOutput(nextTerminalId);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
commitTerminalOutput(terminalId, content) {
|
|
802
|
+
const traceQueue = this.pendingInputTraceByTerminalId.get(terminalId) ?? [];
|
|
803
|
+
const outputReceivedAtMs = traceQueue.length > 0 ? terminalDebugNowMs() : null;
|
|
804
|
+
const chunks = this.outputBuffer.append(terminalId, content);
|
|
805
|
+
if (chunks.length === 0) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
this.terminalLogSpooler?.appendChunks(terminalId, chunks);
|
|
809
|
+
this.touchLastActiveAt(terminalId);
|
|
810
|
+
this.emit("output", {
|
|
811
|
+
terminalId,
|
|
812
|
+
chunks
|
|
813
|
+
});
|
|
814
|
+
if (traceQueue.length > 0 && outputReceivedAtMs !== null) {
|
|
815
|
+
for (const trace of traceQueue) {
|
|
816
|
+
logTerminalDebug("terminal.output.after_input", {
|
|
817
|
+
terminalId,
|
|
818
|
+
traceId: trace.traceId,
|
|
819
|
+
charCount: trace.charCount,
|
|
820
|
+
outputBytes: Buffer.byteLength(content, "utf8"),
|
|
821
|
+
writeToOutputMs: outputReceivedAtMs - trace.serviceWriteFinishedAtMs,
|
|
822
|
+
wsToOutputMs: trace.wsReceivedAtMs === null ? null : outputReceivedAtMs - trace.wsReceivedAtMs,
|
|
823
|
+
clientToOutputMs: trace.clientSentAtMs === null ? null : outputReceivedAtMs - trace.clientSentAtMs,
|
|
824
|
+
pendingTraceCount: traceQueue.length
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
this.pendingInputTraceByTerminalId.delete(terminalId);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
recordPendingInputTrace(terminalId, content, input) {
|
|
831
|
+
if (!isTerminalDebugEnabled() || !input.clientTraceId) {
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
const queue = this.pendingInputTraceByTerminalId.get(terminalId) ?? [];
|
|
835
|
+
queue.push({
|
|
836
|
+
traceId: input.clientTraceId,
|
|
837
|
+
clientSentAtMs: input.clientSentAtMs ?? null,
|
|
838
|
+
wsReceivedAtMs: input.wsReceivedAtMs ?? null,
|
|
839
|
+
serviceWriteStartedAtMs: input.serviceWriteStartedAtMs,
|
|
840
|
+
serviceWriteFinishedAtMs: input.serviceWriteFinishedAtMs,
|
|
841
|
+
charCount: content.length
|
|
842
|
+
});
|
|
843
|
+
this.pendingInputTraceByTerminalId.set(terminalId, queue);
|
|
844
|
+
}
|
|
845
|
+
scheduleActivityFlush() {
|
|
846
|
+
if (this.activityFlushTimer !== null) {
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
this.activityFlushTimer = setTimeout(() => {
|
|
850
|
+
this.activityFlushTimer = null;
|
|
851
|
+
this.flushPendingActivity();
|
|
852
|
+
}, TERMINAL_ACTIVITY_FLUSH_INTERVAL_MS);
|
|
853
|
+
this.activityFlushTimer.unref?.();
|
|
854
|
+
}
|
|
855
|
+
flushPendingActivity(terminalId) {
|
|
856
|
+
if (terminalId) {
|
|
857
|
+
const lastActiveAt = this.pendingActivityByTerminalId.get(terminalId);
|
|
858
|
+
if (!lastActiveAt) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
this.pendingActivityByTerminalId.delete(terminalId);
|
|
862
|
+
this.terminalInstanceRepository.touchLastActiveAt(terminalId, lastActiveAt);
|
|
863
|
+
this.clearActivityFlushTimerIfIdle();
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
if (this.pendingActivityByTerminalId.size === 0) {
|
|
867
|
+
this.clearActivityFlushTimerIfIdle();
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
const entries = [...this.pendingActivityByTerminalId.entries()];
|
|
871
|
+
this.pendingActivityByTerminalId.clear();
|
|
872
|
+
for (const [nextTerminalId, lastActiveAt] of entries) {
|
|
873
|
+
this.terminalInstanceRepository.touchLastActiveAt(nextTerminalId, lastActiveAt);
|
|
874
|
+
}
|
|
875
|
+
this.clearActivityFlushTimerIfIdle();
|
|
876
|
+
}
|
|
877
|
+
clearActivityFlushTimerIfIdle() {
|
|
878
|
+
if (this.pendingActivityByTerminalId.size > 0 || this.activityFlushTimer === null) {
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
clearTimeout(this.activityFlushTimer);
|
|
882
|
+
this.activityFlushTimer = null;
|
|
883
|
+
}
|
|
700
884
|
}
|
|
701
885
|
function normalizeTerminalHistoryContent(content) {
|
|
702
886
|
return content
|
|
@@ -730,17 +914,36 @@ function sanitizeShell(shell) {
|
|
|
730
914
|
}
|
|
731
915
|
return value;
|
|
732
916
|
}
|
|
733
|
-
function resolveRequestedRuntimeType(input) {
|
|
917
|
+
function resolveRequestedRuntimeType(input, shell) {
|
|
734
918
|
const runtimeType = input ?? (process.platform === "win32" ? "embedded-pty" : "tmux");
|
|
735
|
-
if (runtimeType
|
|
736
|
-
runtimeType
|
|
919
|
+
if (runtimeType === "embedded-pty") {
|
|
920
|
+
return runtimeType;
|
|
921
|
+
}
|
|
922
|
+
if (process.platform === "win32") {
|
|
923
|
+
if (runtimeType === "tmux" || isConptyRuntimeType(runtimeType)) {
|
|
924
|
+
return resolveWindowsPersistentRuntimeType(shell);
|
|
925
|
+
}
|
|
737
926
|
throw new AppError({
|
|
738
927
|
statusCode: 400,
|
|
739
928
|
errorCode: "UNSUPPORTED_TERMINAL_RUNTIME",
|
|
740
|
-
detail:
|
|
929
|
+
detail: `褰撳墠 Host 杩樻湭瀹炵幇 runtime=${runtimeType}`
|
|
741
930
|
});
|
|
742
931
|
}
|
|
743
|
-
|
|
932
|
+
if (runtimeType === "tmux") {
|
|
933
|
+
return runtimeType;
|
|
934
|
+
}
|
|
935
|
+
if (isConptyRuntimeType(runtimeType)) {
|
|
936
|
+
throw new AppError({
|
|
937
|
+
statusCode: 400,
|
|
938
|
+
errorCode: "RUNTIME_UNSUPPORTED_PLATFORM",
|
|
939
|
+
detail: "conpty runtime 浠呮敮鎸?Windows"
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
throw new AppError({
|
|
943
|
+
statusCode: 400,
|
|
944
|
+
errorCode: "UNSUPPORTED_TERMINAL_RUNTIME",
|
|
945
|
+
detail: `褰撳墠 Host 杩樻湭瀹炵幇 runtime=${runtimeType}`
|
|
946
|
+
});
|
|
744
947
|
}
|
|
745
948
|
function buildAttachTarget(runtimeType, runtimeSessionId) {
|
|
746
949
|
if (runtimeType === "embedded-pty") {
|
|
@@ -788,6 +991,12 @@ function resolveClosedStatusDetail(reason, currentStatusDetail) {
|
|
|
788
991
|
}
|
|
789
992
|
return currentStatusDetail;
|
|
790
993
|
}
|
|
994
|
+
function normalizeTerminalErrorDetail(sessionDetail, exitCode) {
|
|
995
|
+
if (sessionDetail && sessionDetail !== "EMBEDDED_RUNTIME_NOT_RECOVERABLE") {
|
|
996
|
+
return sessionDetail;
|
|
997
|
+
}
|
|
998
|
+
return `终端异常退出,exitCode=${exitCode ?? "unknown"}`;
|
|
999
|
+
}
|
|
791
1000
|
function shouldMarkRuntimeLost(terminal, detail) {
|
|
792
1001
|
return terminal.runtimeType !== "embedded-pty" && Boolean(detail?.includes("检查失败"));
|
|
793
1002
|
}
|