tirtc-devtools-cli 0.0.1
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 +52 -0
- package/USAGE.md +417 -0
- package/bin/tirtc-devtool.js +2 -0
- package/dist/app-server/protocol-client/index.d.ts +25 -0
- package/dist/app-server/protocol-client/index.js +114 -0
- package/dist/devtools/cli/src/config.d.ts +46 -0
- package/dist/devtools/cli/src/config.js +98 -0
- package/dist/devtools/cli/src/dummy.d.ts +0 -0
- package/dist/devtools/cli/src/dummy.js +1 -0
- package/dist/devtools/cli/src/embedded_paths.d.ts +7 -0
- package/dist/devtools/cli/src/embedded_paths.js +85 -0
- package/dist/devtools/cli/src/facade.d.ts +723 -0
- package/dist/devtools/cli/src/facade.js +194 -0
- package/dist/devtools/cli/src/ffmpeg_tool.d.ts +6 -0
- package/dist/devtools/cli/src/ffmpeg_tool.js +146 -0
- package/dist/devtools/cli/src/guide.d.ts +1 -0
- package/dist/devtools/cli/src/guide.js +49 -0
- package/dist/devtools/cli/src/index.d.ts +1 -0
- package/dist/devtools/cli/src/index.js +753 -0
- package/dist/devtools/cli/src/media_assets.d.ts +25 -0
- package/dist/devtools/cli/src/media_assets.js +121 -0
- package/dist/devtools/cli/src/session_manager.d.ts +25 -0
- package/dist/devtools/cli/src/session_manager.js +393 -0
- package/dist/devtools/cli/src/token_tool.d.ts +33 -0
- package/dist/devtools/cli/src/token_tool.js +217 -0
- package/dist/devtools/cli/src/transport.d.ts +30 -0
- package/dist/devtools/cli/src/transport.js +84 -0
- package/dist/dummy.d.ts +0 -0
- package/dist/dummy.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +48 -0
- package/package.json +54 -0
- package/script/ensure_ffmpeg.sh +22 -0
- package/vendor/app-server/bin/native/macos-arm64/credential_napi.node +0 -0
- package/vendor/app-server/bin/native/macos-arm64/libcrypto.dylib +0 -0
- package/vendor/app-server/bin/native/macos-arm64/libssl.dylib +0 -0
- package/vendor/app-server/bin/native/macos-arm64/libtgrtc.dylib +0 -0
- package/vendor/app-server/bin/native/macos-arm64/runtime_host_napi.node +0 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_codec.h +23 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_frame.h +36 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io.h +56 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io_android.h +19 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io_apple.h +19 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io_harmony.h +19 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_io_windows.h +19 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_processing.h +56 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/audio_sample_rate.h +18 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/error.h +20 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/logging.h +53 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/media_codec.h +21 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/media_downlink.h +89 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/media_uplink.h +115 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/runtime.h +236 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_codec.h +57 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_frame.h +55 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io.h +46 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io_android.h +32 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io_apple.h +34 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io_harmony.h +32 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_io_windows.h +26 -0
- package/vendor/app-server/bin/runtime/linux-x64/include/tirtc/video_processing.h +34 -0
- package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_audio.a +0 -0
- package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_facade.a +0 -0
- package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_foundation_logging.a +0 -0
- package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_media.a +0 -0
- package/vendor/app-server/bin/runtime/linux-x64/lib/libmatrix_runtime_video.a +0 -0
- package/vendor/app-server/bin/runtime/linux-x64/lib/libwebrtc_apm.a +0 -0
- package/vendor/app-server/bin/runtime/linux-x64/lib/libxlog.a +0 -0
- package/vendor/app-server/bin/runtime/linux-x64/manifest.txt +34 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio.h +398 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_codec.h +23 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_frame.h +36 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io.h +56 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io_android.h +19 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io_apple.h +19 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io_harmony.h +19 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_io_windows.h +19 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_processing.h +56 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/audio_sample_rate.h +18 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/av.h +452 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/credential.h +34 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/error.h +30 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/foundation/build_info.h +27 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/http.h +57 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/logging.h +55 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_codec.h +21 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_downlink.h +95 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_fixture_av_sync.h +61 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_fixture_source.h +77 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_live_source.h +71 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/media_uplink.h +116 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/transport.h +481 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/trp.h +541 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_codec.h +58 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_frame.h +55 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io.h +46 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io_android.h +32 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io_apple.h +47 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io_harmony.h +32 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_io_windows.h +26 -0
- package/vendor/app-server/bin/runtime/macos-arm64/include/tirtc/video_processing.h +34 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libcrypto.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libcrypto.dylib +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_audio.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_credential.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_facade.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_foundation_http.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_foundation_logging.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_media.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_transport.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libmatrix_runtime_video.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libssl.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libssl.dylib +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libwebrtc_apm.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/lib/libxlog.a +0 -0
- package/vendor/app-server/bin/runtime/macos-arm64/manifest.txt +50 -0
- package/vendor/app-server/bin/tirtc-devtool-host.js +2 -0
- package/vendor/app-server/dist/host/ArtifactManager.d.ts +18 -0
- package/vendor/app-server/dist/host/ArtifactManager.js +83 -0
- package/vendor/app-server/dist/host/HostProtocol.d.ts +107 -0
- package/vendor/app-server/dist/host/HostProtocol.js +256 -0
- package/vendor/app-server/dist/host/HostServer.d.ts +49 -0
- package/vendor/app-server/dist/host/HostServer.js +635 -0
- package/vendor/app-server/dist/host/HostState.d.ts +60 -0
- package/vendor/app-server/dist/host/HostState.js +19 -0
- package/vendor/app-server/dist/host/RuntimeAdapter.d.ts +81 -0
- package/vendor/app-server/dist/host/RuntimeAdapter.js +559 -0
- package/vendor/app-server/dist/host/RuntimeCredentialTokenIssuer.d.ts +30 -0
- package/vendor/app-server/dist/host/RuntimeCredentialTokenIssuer.js +224 -0
- package/vendor/app-server/dist/host/RuntimeReceiveWorker.d.ts +37 -0
- package/vendor/app-server/dist/host/RuntimeReceiveWorker.js +186 -0
- package/vendor/app-server/dist/host/RuntimeSendWorker.d.ts +42 -0
- package/vendor/app-server/dist/host/RuntimeSendWorker.js +274 -0
- package/vendor/app-server/dist/host/TokenTool.d.ts +15 -0
- package/vendor/app-server/dist/host/TokenTool.js +84 -0
- package/vendor/app-server/dist/host/WebPreviewGateway.d.ts +28 -0
- package/vendor/app-server/dist/host/WebPreviewGateway.js +815 -0
- package/vendor/app-server/dist/host/native/RuntimeCredentialTokenIssuer.d.ts +26 -0
- package/vendor/app-server/dist/host/native/RuntimeCredentialTokenIssuer.js +118 -0
- package/vendor/app-server/dist/host/native/RuntimeHostBridge.d.ts +19 -0
- package/vendor/app-server/dist/host/native/RuntimeHostBridge.js +141 -0
- package/vendor/app-server/dist/host/runtime_backed_preflight.d.ts +10 -0
- package/vendor/app-server/dist/host/runtime_backed_preflight.js +78 -0
- package/vendor/app-server/dist/host/tests/helpers/host_socket_client.d.ts +28 -0
- package/vendor/app-server/dist/host/tests/helpers/host_socket_client.js +85 -0
- package/vendor/app-server/dist/host/tests/helpers/runtime_e2e_local_config.d.ts +10 -0
- package/vendor/app-server/dist/host/tests/helpers/runtime_e2e_local_config.js +41 -0
- package/vendor/app-server/dist/host/tests/helpers/runtime_test_env.d.ts +11 -0
- package/vendor/app-server/dist/host/tests/helpers/runtime_test_env.js +32 -0
- package/vendor/app-server/dist/protocol/contract.d.ts +983 -0
- package/vendor/app-server/dist/protocol/contract.js +198 -0
- package/vendor/app-server/dist/protocol-client/index.d.ts +25 -0
- package/vendor/app-server/dist/protocol-client/index.js +114 -0
- package/vendor/app-server/dist/src/index.d.ts +1 -0
- package/vendor/app-server/dist/src/index.js +294 -0
- package/vendor/runtime/script/prepare_runtime_media_dataset.sh +427 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const commander_1 = require("commander");
|
|
4
|
+
const protocol_client_1 = require("../../../app-server/protocol-client");
|
|
5
|
+
const facade_1 = require("./facade");
|
|
6
|
+
const config_1 = require("./config");
|
|
7
|
+
const guide_1 = require("./guide");
|
|
8
|
+
const session_manager_1 = require("./session_manager");
|
|
9
|
+
const media_assets_1 = require("./media_assets");
|
|
10
|
+
const transport_1 = require("./transport");
|
|
11
|
+
const token_tool_1 = require("./token_tool");
|
|
12
|
+
const CLI_VERSION = '0.0.1';
|
|
13
|
+
const HOST_VERSION = '1.0.0';
|
|
14
|
+
const PROTOCOL_VERSION = '1.0.0';
|
|
15
|
+
if (process.argv.includes('--version') || process.argv.includes('-V')) {
|
|
16
|
+
console.log('CLI Version: ' + CLI_VERSION);
|
|
17
|
+
console.log('Host Version: ' + HOST_VERSION);
|
|
18
|
+
console.log('Protocol Version: ' + PROTOCOL_VERSION);
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
const program = new commander_1.Command();
|
|
22
|
+
function getCliOptions() {
|
|
23
|
+
return program.opts();
|
|
24
|
+
}
|
|
25
|
+
function normalizeError(error) {
|
|
26
|
+
if (error instanceof Error) {
|
|
27
|
+
return {
|
|
28
|
+
reasonCode: 'internal_error',
|
|
29
|
+
message: error.message,
|
|
30
|
+
data: undefined,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (typeof error === 'object' && error !== null) {
|
|
34
|
+
const typed = error;
|
|
35
|
+
return {
|
|
36
|
+
reasonCode: typed.reasonCode ?? 'internal_error',
|
|
37
|
+
message: typed.message ?? 'Internal error',
|
|
38
|
+
data: typed.data,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
reasonCode: 'internal_error',
|
|
43
|
+
message: 'Internal error',
|
|
44
|
+
data: undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function createSessionTransport(sessionId) {
|
|
48
|
+
const session = (0, session_manager_1.resolveSession)(sessionId);
|
|
49
|
+
return new transport_1.SocketTransport(session.socketPath);
|
|
50
|
+
}
|
|
51
|
+
function isBootstrapCommand(cmdName) {
|
|
52
|
+
return cmdName === 'service start' || cmdName === 'connection connect';
|
|
53
|
+
}
|
|
54
|
+
function resolveSessionForCommand(cmdName, options, config) {
|
|
55
|
+
const sessionOptions = {
|
|
56
|
+
logRootDir: config.logging?.root_dir,
|
|
57
|
+
consoleMirror: config.logging?.console_mirror,
|
|
58
|
+
};
|
|
59
|
+
if (cmdName === 'connection connect') {
|
|
60
|
+
if (options.session && options.session.trim().length > 0) {
|
|
61
|
+
throw new Error('connection connect always creates a new session; do not pass --session');
|
|
62
|
+
}
|
|
63
|
+
const created = (0, session_manager_1.createSession)({ ...sessionOptions, role: 'client' });
|
|
64
|
+
return { sessionId: created.sessionId, created: true };
|
|
65
|
+
}
|
|
66
|
+
if (cmdName === 'service start') {
|
|
67
|
+
if (options.session && options.session.trim().length > 0) {
|
|
68
|
+
const existing = (0, session_manager_1.resolveSession)(options.session);
|
|
69
|
+
(0, session_manager_1.setSessionRole)(existing.sessionId, 'service');
|
|
70
|
+
return { sessionId: existing.sessionId, created: false };
|
|
71
|
+
}
|
|
72
|
+
const created = (0, session_manager_1.createSession)({ ...sessionOptions, role: 'service' });
|
|
73
|
+
return { sessionId: created.sessionId, created: true };
|
|
74
|
+
}
|
|
75
|
+
if (cmdName === 'debug bootstrap qrcode') {
|
|
76
|
+
const existing = (0, session_manager_1.resolveSession)(options.session);
|
|
77
|
+
return { sessionId: existing.sessionId, created: false };
|
|
78
|
+
}
|
|
79
|
+
if (options.session && options.session.trim().length > 0) {
|
|
80
|
+
const existing = (0, session_manager_1.resolveSession)(options.session);
|
|
81
|
+
return { sessionId: existing.sessionId, created: false };
|
|
82
|
+
}
|
|
83
|
+
throw new Error(cmdName + ' requires --session <sessionId>');
|
|
84
|
+
}
|
|
85
|
+
function printSuccess(options, result, session, cmdName) {
|
|
86
|
+
const attachSession = isBootstrapCommand(cmdName);
|
|
87
|
+
if (options.json) {
|
|
88
|
+
const data = attachSession ? { session: { sessionId: session.sessionId, created: session.created }, result } : result;
|
|
89
|
+
console.log(JSON.stringify({ code: 0, message: 'OK', data }));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (attachSession) {
|
|
93
|
+
const lifecycle = session.created ? 'created' : 'selected';
|
|
94
|
+
console.log('Session ' + lifecycle + ': ' + session.sessionId);
|
|
95
|
+
}
|
|
96
|
+
console.log('Success:', JSON.stringify(result, null, 2));
|
|
97
|
+
}
|
|
98
|
+
function toResultObject(result) {
|
|
99
|
+
if (typeof result === 'object' && result !== null && !Array.isArray(result)) {
|
|
100
|
+
return { ...result };
|
|
101
|
+
}
|
|
102
|
+
return { result };
|
|
103
|
+
}
|
|
104
|
+
async function applyConfiguredStreamBootstrap(client, config) {
|
|
105
|
+
const sendStreams = [];
|
|
106
|
+
const mode = config.stream?.request_policy?.mode;
|
|
107
|
+
if (mode === 'manual' || mode === 'auto-if-bound') {
|
|
108
|
+
await client.sendRequest('stream/requestPolicy/set', { mode });
|
|
109
|
+
}
|
|
110
|
+
const configuredSend = config.streams?.send;
|
|
111
|
+
const assetsDir = configuredSend?.assets_dir;
|
|
112
|
+
const audioStreamId = configuredSend?.audio_stream_id;
|
|
113
|
+
const videoStreamId = configuredSend?.video_stream_id;
|
|
114
|
+
if (assetsDir && assetsDir.length > 0) {
|
|
115
|
+
if (Number.isFinite(audioStreamId)) {
|
|
116
|
+
const stream = await client.sendRequest('stream/sendStart', {
|
|
117
|
+
streamId: audioStreamId,
|
|
118
|
+
media: 'audio',
|
|
119
|
+
source: {
|
|
120
|
+
mode: 'local_assets',
|
|
121
|
+
local_assets: {
|
|
122
|
+
assets_dir: assetsDir,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
}, 180_000);
|
|
126
|
+
sendStreams.push({
|
|
127
|
+
streamId: audioStreamId,
|
|
128
|
+
assetsDir,
|
|
129
|
+
media: 'audio',
|
|
130
|
+
stream,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (Number.isFinite(videoStreamId)) {
|
|
134
|
+
const stream = await client.sendRequest('stream/sendStart', {
|
|
135
|
+
streamId: videoStreamId,
|
|
136
|
+
media: 'video',
|
|
137
|
+
source: {
|
|
138
|
+
mode: 'local_assets',
|
|
139
|
+
local_assets: {
|
|
140
|
+
assets_dir: assetsDir,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
}, 180_000);
|
|
144
|
+
sendStreams.push({
|
|
145
|
+
streamId: videoStreamId,
|
|
146
|
+
assetsDir,
|
|
147
|
+
media: 'video',
|
|
148
|
+
stream,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
requestPolicyMode: mode,
|
|
154
|
+
sendStreams,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
async function runCommand(cmdName, params, options) {
|
|
158
|
+
let transport;
|
|
159
|
+
try {
|
|
160
|
+
const config = (0, config_1.loadConfig)(options.config);
|
|
161
|
+
const session = resolveSessionForCommand(cmdName, options, config);
|
|
162
|
+
transport = createSessionTransport(session.sessionId);
|
|
163
|
+
const client = new protocol_client_1.AppServerClient(transport);
|
|
164
|
+
await client.initialize({ name: 'tirtc-devtool-cli', version: CLI_VERSION });
|
|
165
|
+
const method = facade_1.CommandToHostMethodMapping[cmdName];
|
|
166
|
+
if (!method) {
|
|
167
|
+
throw new Error('Unknown command mapping for: ' + cmdName);
|
|
168
|
+
}
|
|
169
|
+
const commandParams = cmdName === 'stream request-policy set' ? (() => {
|
|
170
|
+
const mode = String(params.mode ?? '');
|
|
171
|
+
if (mode !== 'manual' && mode !== 'auto-if-bound') {
|
|
172
|
+
throw new Error('Invalid mode, expected manual|auto-if-bound');
|
|
173
|
+
}
|
|
174
|
+
return { mode };
|
|
175
|
+
})() : params;
|
|
176
|
+
const result = await client.sendRequest(method, commandParams);
|
|
177
|
+
let resultForPrint = result;
|
|
178
|
+
if (cmdName === 'service start') {
|
|
179
|
+
const autoApplied = await applyConfiguredStreamBootstrap(client, config);
|
|
180
|
+
const hasAutoApplied = autoApplied.requestPolicyMode !== undefined || autoApplied.sendStreams.length > 0;
|
|
181
|
+
if (hasAutoApplied) {
|
|
182
|
+
const resultObject = toResultObject(result);
|
|
183
|
+
resultForPrint = {
|
|
184
|
+
...resultObject,
|
|
185
|
+
autoApplied,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
printSuccess(options, resultForPrint, session, cmdName);
|
|
190
|
+
transport.stop();
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
const normalized = normalizeError(error);
|
|
195
|
+
const reasonCode = normalized.reasonCode;
|
|
196
|
+
const exitCode = facade_1.ErrorReasonCodeMapping[reasonCode] ?? 1;
|
|
197
|
+
if (options.json) {
|
|
198
|
+
console.log(JSON.stringify({
|
|
199
|
+
code: exitCode,
|
|
200
|
+
message: normalized.message,
|
|
201
|
+
data: normalized.data,
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.error('Error (' + reasonCode + '): ' + normalized.message);
|
|
206
|
+
}
|
|
207
|
+
transport?.stop();
|
|
208
|
+
return exitCode;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async function tailEvents(_cmdName, families, options) {
|
|
212
|
+
let transport;
|
|
213
|
+
try {
|
|
214
|
+
if (!options.session || options.session.trim().length === 0) {
|
|
215
|
+
throw new Error('events tail requires --session <sessionId>');
|
|
216
|
+
}
|
|
217
|
+
const session = (0, session_manager_1.resolveSession)(options.session);
|
|
218
|
+
transport = new transport_1.SocketTransport(session.socketPath);
|
|
219
|
+
const client = new protocol_client_1.AppServerClient(transport);
|
|
220
|
+
client.on('event', (event) => {
|
|
221
|
+
console.log(JSON.stringify(event));
|
|
222
|
+
});
|
|
223
|
+
client.on('error', (error) => {
|
|
224
|
+
if (!options.json) {
|
|
225
|
+
console.error('Client error:', error);
|
|
226
|
+
}
|
|
227
|
+
transport?.stop();
|
|
228
|
+
});
|
|
229
|
+
await client.initialize({ name: 'tirtc-devtool-cli', version: CLI_VERSION });
|
|
230
|
+
await client.subscribeEvents(families);
|
|
231
|
+
await new Promise((resolve) => {
|
|
232
|
+
process.on('SIGINT', () => {
|
|
233
|
+
transport?.stop();
|
|
234
|
+
resolve();
|
|
235
|
+
});
|
|
236
|
+
transport?.onClose(() => resolve());
|
|
237
|
+
});
|
|
238
|
+
return 0;
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
const normalized = normalizeError(error);
|
|
242
|
+
const reasonCode = normalized.reasonCode;
|
|
243
|
+
const exitCode = facade_1.ErrorReasonCodeMapping[reasonCode] ?? 1;
|
|
244
|
+
if (options.json) {
|
|
245
|
+
console.log(JSON.stringify({
|
|
246
|
+
code: exitCode,
|
|
247
|
+
message: normalized.message,
|
|
248
|
+
data: normalized.data,
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
console.error('Error (' + reasonCode + '): ' + normalized.message);
|
|
253
|
+
}
|
|
254
|
+
transport?.stop();
|
|
255
|
+
return exitCode;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async function runMediaAssetsPrepare(source, outputRoot, outputDir, overwrite, options) {
|
|
259
|
+
try {
|
|
260
|
+
const result = await (0, media_assets_1.prepareMediaAssets)({
|
|
261
|
+
source,
|
|
262
|
+
outputRoot,
|
|
263
|
+
outputDir,
|
|
264
|
+
overwrite,
|
|
265
|
+
});
|
|
266
|
+
if (options.json) {
|
|
267
|
+
console.log(JSON.stringify({ code: 0, message: 'OK', data: result }));
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
console.log('Prepared media assets:', JSON.stringify(result, null, 2));
|
|
271
|
+
}
|
|
272
|
+
return 0;
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
const normalized = normalizeError(error);
|
|
276
|
+
const reasonCode = normalized.reasonCode;
|
|
277
|
+
const exitCode = facade_1.ErrorReasonCodeMapping[reasonCode] ?? 1;
|
|
278
|
+
if (options.json) {
|
|
279
|
+
console.log(JSON.stringify({
|
|
280
|
+
code: exitCode,
|
|
281
|
+
message: normalized.message,
|
|
282
|
+
data: normalized.data,
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
console.error('Error (' + reasonCode + '): ' + normalized.message);
|
|
287
|
+
}
|
|
288
|
+
return exitCode;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function runTokenIssue(params, options) {
|
|
292
|
+
try {
|
|
293
|
+
const result = await (0, token_tool_1.issueTokenWithQrcode)(params);
|
|
294
|
+
if (options.json) {
|
|
295
|
+
console.log(JSON.stringify({
|
|
296
|
+
code: 0,
|
|
297
|
+
message: 'OK',
|
|
298
|
+
data: {
|
|
299
|
+
payload: result.payload,
|
|
300
|
+
payloadJson: result.payloadJson,
|
|
301
|
+
token: result.token,
|
|
302
|
+
qrCodePngPath: result.qrCodePngPath,
|
|
303
|
+
},
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
console.log((0, token_tool_1.formatTokenIssueConsoleOutput)(result));
|
|
308
|
+
}
|
|
309
|
+
return 0;
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
const normalized = normalizeError(error);
|
|
313
|
+
const reasonCode = normalized.reasonCode;
|
|
314
|
+
const exitCode = facade_1.ErrorReasonCodeMapping[reasonCode] ?? 1;
|
|
315
|
+
if (options.json) {
|
|
316
|
+
console.log(JSON.stringify({
|
|
317
|
+
code: exitCode,
|
|
318
|
+
message: normalized.message,
|
|
319
|
+
data: normalized.data,
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
console.error('Error (' + reasonCode + '): ' + normalized.message);
|
|
324
|
+
}
|
|
325
|
+
return exitCode;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function runAndExit(promise) {
|
|
329
|
+
promise.then((code) => {
|
|
330
|
+
process.exit(code);
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
function mustParseJsonPayload(input) {
|
|
334
|
+
try {
|
|
335
|
+
const parsed = JSON.parse(input);
|
|
336
|
+
return JSON.stringify(parsed);
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
throw new Error('payloadJson must be valid JSON text');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
program.name('tirtc-devtool')
|
|
343
|
+
.description('TiRTC DevTool CLI')
|
|
344
|
+
.option('--config <path>', '配置文件路径(TOML)')
|
|
345
|
+
.option('--json', '以机器可读 JSON 输出(便于脚本集成)')
|
|
346
|
+
.option('--session <sessionId>', '指定会话 ID;除 service start/connect 外必须传');
|
|
347
|
+
const host = program.command('host').description('Host 基础能力(状态、停止)');
|
|
348
|
+
host.command('status').description('查看 Host/service/connection/artifact 当前状态').action(() => {
|
|
349
|
+
runAndExit(runCommand('host status', {}, getCliOptions()));
|
|
350
|
+
});
|
|
351
|
+
host.command('stop')
|
|
352
|
+
.description('停止 Host 进程(可用 --all 停止所有会话)')
|
|
353
|
+
.option('--all', '停止所有会话对应的 Host 进程')
|
|
354
|
+
.action(() => {
|
|
355
|
+
const options = getCliOptions();
|
|
356
|
+
const stopAll = process.argv.includes('--all');
|
|
357
|
+
if (!stopAll) {
|
|
358
|
+
runAndExit(runCommand('host stop', {}, options));
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const stopped = (0, session_manager_1.stopAllSessions)();
|
|
363
|
+
if (options.json) {
|
|
364
|
+
console.log(JSON.stringify({ code: 0, message: 'OK', data: { count: stopped.length, sessions: stopped } }));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
console.log('Stopped sessions:', stopped.length);
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
const normalized = normalizeError(error);
|
|
371
|
+
const code = facade_1.ErrorReasonCodeMapping[normalized.reasonCode] ?? 1;
|
|
372
|
+
if (options.json) {
|
|
373
|
+
console.log(JSON.stringify({ code, message: normalized.message, data: normalized.data }));
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
console.error('Error:', normalized.message);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
const hostSession = host.command('session').description('会话管理:start/list/stop(推荐先 start)');
|
|
380
|
+
hostSession.command('start').description('创建并启动一个常驻会话,返回 sessionId').action(() => {
|
|
381
|
+
try {
|
|
382
|
+
const options = getCliOptions();
|
|
383
|
+
const config = (0, config_1.loadConfig)(options.config);
|
|
384
|
+
const created = (0, session_manager_1.createSession)({
|
|
385
|
+
logRootDir: config.logging?.root_dir,
|
|
386
|
+
consoleMirror: config.logging?.console_mirror,
|
|
387
|
+
role: 'idle',
|
|
388
|
+
});
|
|
389
|
+
if (options.json) {
|
|
390
|
+
console.log(JSON.stringify({ code: 0, message: 'OK', data: created }));
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
console.log('Session started:', created.sessionId);
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
const normalized = normalizeError(error);
|
|
397
|
+
const options = getCliOptions();
|
|
398
|
+
const code = facade_1.ErrorReasonCodeMapping[normalized.reasonCode] ?? 1;
|
|
399
|
+
if (options.json) {
|
|
400
|
+
console.log(JSON.stringify({ code, message: normalized.message }));
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
console.error('Error:', normalized.message);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
hostSession.command('list').description('列出当前所有会话及其状态').action(() => {
|
|
407
|
+
try {
|
|
408
|
+
const sessions = (0, session_manager_1.listSessions)();
|
|
409
|
+
const options = getCliOptions();
|
|
410
|
+
if (options.json) {
|
|
411
|
+
console.log(JSON.stringify({ code: 0, message: 'OK', data: sessions }));
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
console.log(JSON.stringify(sessions, null, 2));
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
const normalized = normalizeError(error);
|
|
418
|
+
const options = getCliOptions();
|
|
419
|
+
const code = facade_1.ErrorReasonCodeMapping[normalized.reasonCode] ?? 1;
|
|
420
|
+
if (options.json) {
|
|
421
|
+
console.log(JSON.stringify({ code, message: normalized.message }));
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
console.error('Error:', normalized.message);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
hostSession.command('stop <sessionId>')
|
|
428
|
+
.description('停止并回收指定会话')
|
|
429
|
+
.action((sessionId) => {
|
|
430
|
+
try {
|
|
431
|
+
const stopped = (0, session_manager_1.stopSession)(sessionId);
|
|
432
|
+
const options = getCliOptions();
|
|
433
|
+
if (options.json) {
|
|
434
|
+
console.log(JSON.stringify({ code: 0, message: 'OK', data: stopped }));
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
console.log('Session stopped:', stopped.sessionId);
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
const normalized = normalizeError(error);
|
|
441
|
+
const options = getCliOptions();
|
|
442
|
+
const code = facade_1.ErrorReasonCodeMapping[normalized.reasonCode] ?? 1;
|
|
443
|
+
if (options.json) {
|
|
444
|
+
console.log(JSON.stringify({ code, message: normalized.message }));
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
console.error('Error:', normalized.message);
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
function handleConfigInit(outputPath) {
|
|
451
|
+
try {
|
|
452
|
+
const written = (0, config_1.writeConfigTemplate)(outputPath);
|
|
453
|
+
const options = getCliOptions();
|
|
454
|
+
if (options.json) {
|
|
455
|
+
console.log(JSON.stringify({ code: 0, message: 'OK', data: { path: written } }));
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
console.log('Config template created:', written);
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
const normalized = normalizeError(error);
|
|
462
|
+
const options = getCliOptions();
|
|
463
|
+
const code = facade_1.ErrorReasonCodeMapping[normalized.reasonCode] ?? 1;
|
|
464
|
+
if (options.json) {
|
|
465
|
+
console.log(JSON.stringify({ code, message: normalized.message }));
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
console.error('Error:', normalized.message);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const init = program.command('init [outputPath]').description('新手第一步:生成 CLI 配置模板(默认输出到 ./.tmp/tirtc-devtool/config.toml)');
|
|
472
|
+
init.action((outputPath) => {
|
|
473
|
+
handleConfigInit(outputPath);
|
|
474
|
+
});
|
|
475
|
+
const media = program.command('media').description('媒体素材辅助:prepare local assets');
|
|
476
|
+
const mediaAssets = media.command('assets').description('prepared local assets contract');
|
|
477
|
+
mediaAssets.command('prepare')
|
|
478
|
+
.description('把 MP4 prepare 为 local_assets 目录')
|
|
479
|
+
.requiredOption('--source <path>', '输入 MP4 路径')
|
|
480
|
+
.requiredOption('--output-root <dir>', 'prepared assets 输出根目录')
|
|
481
|
+
.option('--output-dir <dir>', 'prepared assets 精确输出目录(可选)')
|
|
482
|
+
.option('--overwrite', '忽略现有 cache 并重建')
|
|
483
|
+
.action((commandOptions) => {
|
|
484
|
+
runAndExit(runMediaAssetsPrepare(commandOptions.source, commandOptions.outputRoot, commandOptions.outputDir, commandOptions.overwrite === true, getCliOptions()));
|
|
485
|
+
});
|
|
486
|
+
// Backward-compatible alias.
|
|
487
|
+
const hostConfig = host.command('config').description('配置辅助(兼容别名,推荐用顶层 config)');
|
|
488
|
+
hostConfig.command('init [outputPath]').description('生成配置模板(等价于 config init)').action((outputPath) => {
|
|
489
|
+
handleConfigInit(outputPath);
|
|
490
|
+
});
|
|
491
|
+
const service = program.command('service').description('服务端能力:启动/停止服务,等待远端接入');
|
|
492
|
+
service.command('start [service_entry] [license]')
|
|
493
|
+
.description('启动服务端会话(license 必填,service_entry 可省略走默认)')
|
|
494
|
+
.action((service_entry, license) => {
|
|
495
|
+
const config = (0, config_1.loadConfig)(getCliOptions().config);
|
|
496
|
+
const finalServiceEntry = service_entry ?? config.service?.service_entry;
|
|
497
|
+
const finalLicense = license ?? config.service?.license;
|
|
498
|
+
if (!finalLicense) {
|
|
499
|
+
throw new Error('service start requires license (service_entry can be omitted)');
|
|
500
|
+
}
|
|
501
|
+
runAndExit(runCommand('service start', { serviceEntry: finalServiceEntry, license: finalLicense, timeoutMs: 5000 }, getCliOptions()));
|
|
502
|
+
});
|
|
503
|
+
service.command('stop').description('停止服务端监听').action(() => {
|
|
504
|
+
runAndExit(runCommand('service stop', {}, getCliOptions()));
|
|
505
|
+
});
|
|
506
|
+
const connection = program.command('connection').description('客户端能力:主动连接远端 peer');
|
|
507
|
+
connection.command('show').description('查看连接状态').action(() => {
|
|
508
|
+
runAndExit(runCommand('connection show', {}, getCliOptions()));
|
|
509
|
+
});
|
|
510
|
+
connection.command('connect [service_entry] <peer_id> [token]')
|
|
511
|
+
.description('作为客户端连接远端(总是新建 session;不传 token 时自动签发)')
|
|
512
|
+
.action((service_entry, peer_id, token) => {
|
|
513
|
+
const config = (0, config_1.loadConfig)(getCliOptions().config);
|
|
514
|
+
const finalServiceEntry = [service_entry, config.connection?.service_entry, config.service?.service_entry].find((value) => typeof value === "string" && value.trim().length > 0);
|
|
515
|
+
const configuredToken = config.connection?.token?.trim();
|
|
516
|
+
const manualToken = (token ?? configuredToken)?.trim();
|
|
517
|
+
const autoTokenCfg = config.connection?.auto_token;
|
|
518
|
+
const hasAutoToken = Boolean(autoTokenCfg?.access_id && autoTokenCfg?.secret_key);
|
|
519
|
+
if (!manualToken && !hasAutoToken) {
|
|
520
|
+
throw new Error('connection connect requires token or [connection.auto_token] access_id/secret_key');
|
|
521
|
+
}
|
|
522
|
+
const params = {
|
|
523
|
+
serviceEntry: finalServiceEntry,
|
|
524
|
+
peerId: peer_id,
|
|
525
|
+
timeoutMs: 5000,
|
|
526
|
+
};
|
|
527
|
+
if (manualToken && manualToken.length > 0) {
|
|
528
|
+
params.token = manualToken;
|
|
529
|
+
params.tokenMode = 'manual';
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
params.tokenMode = 'auto';
|
|
533
|
+
params.autoToken = {
|
|
534
|
+
openapiEntry: autoTokenCfg?.openapi_entry,
|
|
535
|
+
accessId: autoTokenCfg?.access_id,
|
|
536
|
+
secretKey: autoTokenCfg?.secret_key,
|
|
537
|
+
localId: autoTokenCfg?.local_id ?? peer_id,
|
|
538
|
+
userTtlSeconds: autoTokenCfg?.user_ttl_seconds,
|
|
539
|
+
channelTtlSeconds: autoTokenCfg?.channel_ttl_seconds,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
runAndExit(runCommand('connection connect', params, getCliOptions()));
|
|
543
|
+
});
|
|
544
|
+
connection.command('disconnect').description('断开当前连接').action(() => {
|
|
545
|
+
runAndExit(runCommand('connection disconnect', {}, getCliOptions()));
|
|
546
|
+
});
|
|
547
|
+
const token = program.command('token').description('Token 工具:签发 token,并输出可直接使用的 JSON 与本地二维码 PNG');
|
|
548
|
+
token.command('issue <access_id> <secret_key> <local_id> <peer_id>')
|
|
549
|
+
.description('使用 access/secret/local/peer 签发 token;输出摘要、payload JSON、token 和二维码 PNG 路径')
|
|
550
|
+
.option('--openapi-entry <url>', '可选 openapi entry;留空时走 runtime 默认值')
|
|
551
|
+
.option('--service-entry <entry>', '可选 service entry;用于组合连接 payload 与二维码 PNG')
|
|
552
|
+
.option('--user-ttl-seconds <seconds>', '可选 user token ttl(秒)')
|
|
553
|
+
.option('--channel-ttl-seconds <seconds>', '可选 channel token ttl(秒)')
|
|
554
|
+
.action((access_id, secret_key, local_id, peer_id, commandOptions) => {
|
|
555
|
+
const parsePositiveInt = (name, raw) => {
|
|
556
|
+
if (raw === undefined) {
|
|
557
|
+
return undefined;
|
|
558
|
+
}
|
|
559
|
+
const parsed = Number.parseInt(raw, 10);
|
|
560
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
561
|
+
throw new Error(name + ' must be a positive integer');
|
|
562
|
+
}
|
|
563
|
+
return parsed;
|
|
564
|
+
};
|
|
565
|
+
runAndExit(runTokenIssue({
|
|
566
|
+
accessId: access_id,
|
|
567
|
+
secretKey: secret_key,
|
|
568
|
+
localId: local_id,
|
|
569
|
+
peerId: peer_id,
|
|
570
|
+
openapiEntry: commandOptions.openapiEntry,
|
|
571
|
+
serviceEntry: commandOptions.serviceEntry,
|
|
572
|
+
userTtlSeconds: parsePositiveInt('user-ttl-seconds', commandOptions.userTtlSeconds),
|
|
573
|
+
channelTtlSeconds: parsePositiveInt('channel-ttl-seconds', commandOptions.channelTtlSeconds),
|
|
574
|
+
}, getCliOptions()));
|
|
575
|
+
});
|
|
576
|
+
const stream = program.command('stream').description('流控制:发送/接收/请求策略');
|
|
577
|
+
stream.command('list').description('查看流快照列表').action(() => {
|
|
578
|
+
runAndExit(runCommand('stream list', {}, getCliOptions()));
|
|
579
|
+
});
|
|
580
|
+
const streamSend = stream.command('send').description('发送流:绑定 prepared local assets 并发起上行');
|
|
581
|
+
streamSend.command('start <streamId> <media> <assets_dir>')
|
|
582
|
+
.description('开始发送流(绑定 source.local_assets 并触发上行)')
|
|
583
|
+
.action((streamId, media, assets_dir) => {
|
|
584
|
+
runAndExit(runCommand('stream send start', {
|
|
585
|
+
streamId: Number.parseInt(streamId, 10),
|
|
586
|
+
media,
|
|
587
|
+
source: {
|
|
588
|
+
mode: 'local_assets',
|
|
589
|
+
local_assets: {
|
|
590
|
+
assets_dir,
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
}, getCliOptions()));
|
|
594
|
+
});
|
|
595
|
+
streamSend.command('stop <streamId>').description('停止发送指定流').action((streamId) => {
|
|
596
|
+
runAndExit(runCommand('stream send stop', { streamId: Number.parseInt(streamId, 10) }, getCliOptions()));
|
|
597
|
+
});
|
|
598
|
+
const streamReceive = stream.command('receive').description('接收流:声明并接收下行');
|
|
599
|
+
streamReceive.command('start <streamId> <media>')
|
|
600
|
+
.description('开始接收指定流')
|
|
601
|
+
.action((streamId, media) => {
|
|
602
|
+
runAndExit(runCommand('stream receive start', {
|
|
603
|
+
streamId: Number.parseInt(streamId, 10),
|
|
604
|
+
media,
|
|
605
|
+
}, getCliOptions()));
|
|
606
|
+
});
|
|
607
|
+
streamReceive.command('stop <streamId>')
|
|
608
|
+
.description('停止接收指定流')
|
|
609
|
+
.action((streamId) => {
|
|
610
|
+
runAndExit(runCommand('stream receive stop', { streamId: Number.parseInt(streamId, 10) }, getCliOptions()));
|
|
611
|
+
});
|
|
612
|
+
const streamRequestPolicy = stream.command('request-policy').description('远端请求策略:manual/auto-if-bound');
|
|
613
|
+
streamRequestPolicy.command('get').description('查看远端请求策略').action(() => {
|
|
614
|
+
runAndExit(runCommand('stream request-policy get', {}, getCliOptions()));
|
|
615
|
+
});
|
|
616
|
+
streamRequestPolicy.command('set <mode>')
|
|
617
|
+
.description('设置远端请求策略(manual|auto-if-bound)')
|
|
618
|
+
.action((mode) => {
|
|
619
|
+
runAndExit(runCommand('stream request-policy set', { mode }, getCliOptions()));
|
|
620
|
+
});
|
|
621
|
+
const output = program.command('output').description('输出挂载:文件等消费者 attach/detach');
|
|
622
|
+
output
|
|
623
|
+
.command('attach <streamId> <consumer> <mediaView> <format> <delivery> [targetPath] [maxFiles]')
|
|
624
|
+
.description('挂载输出消费者(如 file_sink)')
|
|
625
|
+
.action((streamId, consumer, mediaView, format, delivery, targetPath, maxFiles) => {
|
|
626
|
+
const parsed = facade_1.OutputAttachCliParamsSchema.safeParse({
|
|
627
|
+
streamId: Number.parseInt(streamId, 10),
|
|
628
|
+
consumer,
|
|
629
|
+
mediaView,
|
|
630
|
+
format,
|
|
631
|
+
delivery,
|
|
632
|
+
targetPath,
|
|
633
|
+
maxFiles: maxFiles ? Number.parseInt(maxFiles, 10) : undefined,
|
|
634
|
+
});
|
|
635
|
+
if (!parsed.success) {
|
|
636
|
+
const msg = parsed.error.issues.map((issue) => issue.message).join('; ');
|
|
637
|
+
throw new Error('invalid output attach params: ' + msg);
|
|
638
|
+
}
|
|
639
|
+
runAndExit(runCommand('output attach', {
|
|
640
|
+
streamId: Number.parseInt(streamId, 10),
|
|
641
|
+
consumer,
|
|
642
|
+
mediaView,
|
|
643
|
+
format,
|
|
644
|
+
delivery,
|
|
645
|
+
targetPath,
|
|
646
|
+
maxFiles: maxFiles ? Number.parseInt(maxFiles, 10) : undefined,
|
|
647
|
+
}, getCliOptions()));
|
|
648
|
+
});
|
|
649
|
+
output.command('detach <outputId>').description('卸载输出消费者').action((outputId) => {
|
|
650
|
+
runAndExit(runCommand('output detach', { outputId }, getCliOptions()));
|
|
651
|
+
});
|
|
652
|
+
const command = program.command('command').description('命令通道:发送命令与事件跟踪');
|
|
653
|
+
command.command('send <commandId> <kind> <payloadEncoding> <payload>')
|
|
654
|
+
.description('发送命令并等待响应')
|
|
655
|
+
.action((commandId, kind, payloadEncoding, payload) => {
|
|
656
|
+
runAndExit(runCommand('command send', {
|
|
657
|
+
commandId: Number.parseInt(commandId, 10),
|
|
658
|
+
kind,
|
|
659
|
+
payloadEncoding,
|
|
660
|
+
payload,
|
|
661
|
+
awaitResponse: true,
|
|
662
|
+
timeoutMs: 5000,
|
|
663
|
+
}, getCliOptions()));
|
|
664
|
+
});
|
|
665
|
+
command.command('send-json <commandId> <kind> <payloadJson>')
|
|
666
|
+
.description('发送 JSON 命令并等待响应(JSON-first)')
|
|
667
|
+
.action((commandId, kind, payloadJson) => {
|
|
668
|
+
const parsed = facade_1.CommandSendJsonParamsSchema.safeParse({
|
|
669
|
+
commandId: Number.parseInt(commandId, 10),
|
|
670
|
+
kind,
|
|
671
|
+
payloadJson,
|
|
672
|
+
});
|
|
673
|
+
if (!parsed.success) {
|
|
674
|
+
const msg = parsed.error.issues.map((issue) => issue.message).join('; ');
|
|
675
|
+
throw new Error('invalid command send-json params: ' + msg);
|
|
676
|
+
}
|
|
677
|
+
const payload = mustParseJsonPayload(payloadJson);
|
|
678
|
+
runAndExit(runCommand('command send', {
|
|
679
|
+
commandId: parsed.data.commandId,
|
|
680
|
+
kind: parsed.data.kind,
|
|
681
|
+
payloadEncoding: 'json',
|
|
682
|
+
payload,
|
|
683
|
+
awaitResponse: true,
|
|
684
|
+
timeoutMs: 5000,
|
|
685
|
+
}, getCliOptions()));
|
|
686
|
+
});
|
|
687
|
+
command.command('tail').description('持续监听命令相关事件').action(() => {
|
|
688
|
+
runAndExit(tailEvents('command tail', ['command'], getCliOptions()));
|
|
689
|
+
});
|
|
690
|
+
const debug = program.command('debug').description('调试工具:连接 bootstrap 二维码');
|
|
691
|
+
const debugBootstrap = debug.command('bootstrap').description('连接 bootstrap(不在 App Server 侧签发 token)');
|
|
692
|
+
debugBootstrap.command('qrcode <access_id> <secret_key> <peer_id> [service_entry]')
|
|
693
|
+
.description('生成客户端连接 bootstrap 二维码(payload 包含 access_id/secret_key/peer_id)')
|
|
694
|
+
.action((access_id, secret_key, peer_id, service_entry) => {
|
|
695
|
+
const payload = JSON.stringify({
|
|
696
|
+
version: 1,
|
|
697
|
+
type: 'tirtc-connect-bootstrap',
|
|
698
|
+
access_id,
|
|
699
|
+
secret_key,
|
|
700
|
+
peer_id,
|
|
701
|
+
service_entry: service_entry ?? '',
|
|
702
|
+
generated_at: new Date().toISOString(),
|
|
703
|
+
});
|
|
704
|
+
runAndExit(runCommand('debug bootstrap qrcode', { payload, outputStem: 'connect-bootstrap' }, getCliOptions()));
|
|
705
|
+
});
|
|
706
|
+
debugBootstrap.command('qrcode-from-config')
|
|
707
|
+
.description('从 --config 读取 debug.connect_bootstrap 并生成二维码')
|
|
708
|
+
.action(() => {
|
|
709
|
+
const config = (0, config_1.loadConfig)(getCliOptions().config);
|
|
710
|
+
const bootstrap = config.debug?.connect_bootstrap;
|
|
711
|
+
if (!bootstrap) {
|
|
712
|
+
throw new Error('missing debug.connect_bootstrap in config');
|
|
713
|
+
}
|
|
714
|
+
if (!bootstrap.access_id || !bootstrap.secret_key || !bootstrap.peer_id) {
|
|
715
|
+
throw new Error('debug.connect_bootstrap requires access_id + secret_key + peer_id');
|
|
716
|
+
}
|
|
717
|
+
const payload = JSON.stringify({
|
|
718
|
+
version: 1,
|
|
719
|
+
type: 'tirtc-connect-bootstrap',
|
|
720
|
+
access_id: bootstrap.access_id,
|
|
721
|
+
secret_key: bootstrap.secret_key,
|
|
722
|
+
peer_id: bootstrap.peer_id,
|
|
723
|
+
service_entry: bootstrap.service_entry ?? '',
|
|
724
|
+
generated_at: new Date().toISOString(),
|
|
725
|
+
});
|
|
726
|
+
runAndExit(runCommand('debug bootstrap qrcode', { payload, outputStem: 'connect-bootstrap' }, getCliOptions()));
|
|
727
|
+
});
|
|
728
|
+
const events = program.command('events').description('事件订阅:实时观察 Host 事件');
|
|
729
|
+
events.command('tail').description('持续监听 Host 事件').action(() => {
|
|
730
|
+
runAndExit(tailEvents('events tail', ['service', 'connection', 'stream', 'command', 'operation', 'logs'], getCliOptions()));
|
|
731
|
+
});
|
|
732
|
+
const logs = program.command('logs').description('日志导出:导出运行日志');
|
|
733
|
+
logs.command('export <outputPath>')
|
|
734
|
+
.description('导出运行日志到指定路径')
|
|
735
|
+
.action((outputPath) => {
|
|
736
|
+
runAndExit(runCommand('logs export', { outputPath }, getCliOptions()));
|
|
737
|
+
});
|
|
738
|
+
const report = program.command('report').description('报告查看/导出:验收证据');
|
|
739
|
+
const guide = program.command('guide').description('新手入口:按场景给出可执行步骤');
|
|
740
|
+
guide.command('quickstart')
|
|
741
|
+
.description('打印服务端启动 -> 安卓扫码连接 的最小闭环步骤')
|
|
742
|
+
.action(() => {
|
|
743
|
+
(0, guide_1.printQuickstartGuide)();
|
|
744
|
+
});
|
|
745
|
+
report.command('show').description('查看当前报告内容').action(() => {
|
|
746
|
+
runAndExit(runCommand('report show', {}, getCliOptions()));
|
|
747
|
+
});
|
|
748
|
+
report.command('export <outputPath>')
|
|
749
|
+
.description('导出报告到指定路径')
|
|
750
|
+
.action((outputPath) => {
|
|
751
|
+
runAndExit(runCommand('report export', { outputPath }, getCliOptions()));
|
|
752
|
+
});
|
|
753
|
+
program.parse(process.argv);
|