@deadragdoll/tellymcp 0.0.9 → 0.0.11
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/.env.example.client +42 -50
- package/.env.example.gateway +49 -61
- package/CHANGELOG.md +3 -3
- package/README-ru.md +205 -384
- package/README.md +195 -1194
- package/TOOLS.md +294 -377
- package/VERSION.md +36 -10
- package/config/codex/plugins/telly-workflows/.codex-plugin/plugin.json +18 -0
- package/config/codex/plugins/telly-workflows/references/invariants.md +10 -0
- package/config/codex/plugins/telly-workflows/skills/telly-browser-screenshot/SKILL.md +27 -0
- package/config/codex/plugins/telly-workflows/skills/telly-collab-artifact/SKILL.md +22 -0
- package/config/codex/plugins/telly-workflows/skills/telly-human-telegram/SKILL.md +31 -0
- package/config/codex/plugins/telly-workflows/skills/telly-partner-note/SKILL.md +36 -0
- package/config/templates/env.both.template +40 -20
- package/config/templates/env.client.template +34 -18
- package/config/templates/env.gateway.template +38 -22
- package/dist/cli.js +322 -75
- package/dist/codexPluginInstaller.js +215 -0
- package/dist/lib/mixins/session.errors.js +34 -1
- package/dist/lib/pinoTargets.js +2 -2
- package/dist/moleculer.config.js +7 -5
- package/dist/services/features/telegram-mcp/approval.service.js +1 -1
- package/dist/services/features/telegram-mcp/browser.service.js +95 -2
- package/dist/services/features/telegram-mcp/collaboration.service.js +41 -4
- package/dist/services/features/telegram-mcp/ensuredb.service.js +145 -26
- package/dist/services/features/telegram-mcp/gateway-delivery.service.js +285 -103
- package/dist/services/features/telegram-mcp/gateway-rmq.service.js +3 -2
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +905 -87
- package/dist/services/features/telegram-mcp/gateway.service.js +876 -81
- package/dist/services/features/telegram-mcp/mcp-http.service.js +9 -1
- package/dist/services/features/telegram-mcp/mcp-server.service.js +17 -26
- package/dist/services/features/telegram-mcp/notify.service.js +128 -2
- package/dist/services/features/telegram-mcp/runtime.service.js +27 -7
- package/dist/services/features/telegram-mcp/session-context.service.js +29 -2
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +133 -10
- package/dist/services/features/telegram-mcp/src/app/config/env.js +161 -51
- package/dist/services/features/telegram-mcp/src/app/http.js +375 -42
- package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +386 -58
- package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +3 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/terminal.js +12 -0
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +116 -104
- package/dist/services/features/telegram-mcp/src/entities/xchange/model/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/features/ask-user/model/askUserTelegram.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +249 -19
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +71 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/listGatewaySessionsTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/localCollaborationBackend.js +93 -82
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +47 -5
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerNoteTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +82 -0
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +22 -14
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +649 -75
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +76 -0
- package/dist/services/features/telegram-mcp/src/features/embedded-runtime/model/embeddedRuntimeBroker.js +92 -0
- package/dist/services/features/telegram-mcp/src/features/foreground-terminal/model/foregroundTerminalRuntime.js +192 -0
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +382 -3
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyTelegramTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/notify/model/sendFileToTelegramTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/clearSessionContextTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getSessionContextTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/session-context/model/renameSessionTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +42 -200
- package/dist/services/features/telegram-mcp/src/features/session-context/model/setSessionContextTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/terminal-buffer/model/terminalBufferService.js +96 -0
- package/dist/services/features/telegram-mcp/src/features/terminal-input/model/terminalInputService.js +97 -0
- package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownService.js +107 -58
- package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/xchange/model/getXchangeRecordTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/xchange/model/listXchangeRecordsTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/xchange/model/markXchangeRecordReadTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/xchange/model/xchangeService.js +169 -0
- package/dist/services/features/telegram-mcp/src/processes/human-approval/model/orchestrator.js +5 -4
- package/dist/services/features/telegram-mcp/src/shared/i18n/index.js +46 -0
- package/dist/services/features/telegram-mcp/src/shared/i18n/resources/en.js +652 -0
- package/dist/services/features/telegram-mcp/src/shared/i18n/resources/ru.js +652 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalSessionStore.js +27 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +11 -8
- package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +51 -71
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/proxyFetch.js +21 -22
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +411 -6524
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportAttachmentStore.js +93 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportBroadcastActions.js +385 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +143 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConstructorWiring.js +633 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContent.js +84 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContext.js +78 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportDocumentActions.js +36 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportEventActions.js +292 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +352 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFormatting.js +75 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportGatewayActions.js +44 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLifecycleActions.js +161 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLive.js +56 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLiveActions.js +77 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuCallbacks.js +254 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFactories.js +538 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFingerprints.js +93 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFlow.js +344 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuShell.js +62 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuState.js +408 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuText.js +216 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js +452 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportOutputActions.js +189 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPartnerActions.js +286 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPayloadState.js +101 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectActions.js +463 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEntryActions.js +202 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEvents.js +99 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectMenus.js +138 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectState.js +308 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectView.js +426 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportRequestFlow.js +278 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportSessionActions.js +143 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalActions.js +468 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalRuntime.js +171 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTypes.js +2 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportUtils.js +330 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportXchangeState.js +107 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +255 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +543 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/xchange/sqliteRecordStore.js +223 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/gatewayScope.js +24 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/logger/logger.js +6 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +147 -76
- package/dist/services/features/telegram-mcp/src/shared/lib/telegramXchangeRecords.js +72 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/terminalPromptDetection.js +237 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/version/versionHandshake.js +129 -1
- package/dist/services/features/telegram-mcp/src/shared/lib/xchangeRecordHints.js +98 -0
- package/dist/services/features/telegram-mcp/standalone-http.service.js +13 -1
- package/dist/services/features/telegram-mcp/terminal-buffer.service.js +42 -0
- package/dist/services/features/telegram-mcp/terminal-input.service.js +41 -0
- package/dist/services/features/telegram-mcp/tools-sync.service.js +16 -2
- package/dist/services/features/telegram-mcp/xchange.service.js +123 -0
- package/docs/STANDALONE-ru.md +172 -0
- package/docs/STANDALONE.md +172 -0
- package/package.json +11 -5
- package/scripts/build-package-artifact.sh +27 -0
- package/scripts/deploy-gateway.sh +64 -0
- package/scripts/deploy-onebot-branch.sh +27 -0
- package/scripts/postinstall.js +11 -36
- package/STANDALONE-ru.md +0 -274
- package/STANDALONE.md +0 -274
- package/dist/services/features/telegram-mcp/inbox.service.js +0 -33
- package/dist/services/features/telegram-mcp/pair.service.js +0 -33
- package/dist/services/features/telegram-mcp/src/app/webapp/tmux.js +0 -10
- package/dist/services/features/telegram-mcp/src/features/inbox/model/deleteTelegramInboxMessageTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxCountTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/inbox/model/inboxService.js +0 -77
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/clearSessionPairingTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/createSessionPairCodeTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/generatePairCode.js +0 -202
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getTmuxTargetTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/session-context/model/setTmuxTargetTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +0 -363
package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportUtils.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.trimTrailingSlashes = trimTrailingSlashes;
|
|
7
|
+
exports.normalizeBasePath = normalizeBasePath;
|
|
8
|
+
exports.joinHttpPath = joinHttpPath;
|
|
9
|
+
exports.resolveWebAppPublicBaseUrl = resolveWebAppPublicBaseUrl;
|
|
10
|
+
exports.resolveGatewayControlBaseUrl = resolveGatewayControlBaseUrl;
|
|
11
|
+
exports.isMenuEntryCommand = isMenuEntryCommand;
|
|
12
|
+
exports.isHelpCommand = isHelpCommand;
|
|
13
|
+
exports.parseAdminAuthCommand = parseAdminAuthCommand;
|
|
14
|
+
exports.readMenuPayloadKey = readMenuPayloadKey;
|
|
15
|
+
exports.buildDatedRelativePath = buildDatedRelativePath;
|
|
16
|
+
exports.buildPrincipalKey = buildPrincipalKey;
|
|
17
|
+
exports.formatTerminalBridgeError = formatTerminalBridgeError;
|
|
18
|
+
exports.splitLongTelegramText = splitLongTelegramText;
|
|
19
|
+
exports.escapeMarkdownV2 = escapeMarkdownV2;
|
|
20
|
+
exports.escapeMarkdownV2CodeBlock = escapeMarkdownV2CodeBlock;
|
|
21
|
+
exports.escapeHtml = escapeHtml;
|
|
22
|
+
exports.splitTitleAndBody = splitTitleAndBody;
|
|
23
|
+
exports.renderMarkdownChunk = renderMarkdownChunk;
|
|
24
|
+
exports.shouldNudge = shouldNudge;
|
|
25
|
+
exports.slugifyFilenamePart = slugifyFilenamePart;
|
|
26
|
+
exports.formatMenuTimestamp = formatMenuTimestamp;
|
|
27
|
+
exports.normalizeGatewayBaseUrl = normalizeGatewayBaseUrl;
|
|
28
|
+
exports.buildLocalHandoffId = buildLocalHandoffId;
|
|
29
|
+
exports.buildLocalNoteContent = buildLocalNoteContent;
|
|
30
|
+
exports.buildAdminClientTitle = buildAdminClientTitle;
|
|
31
|
+
exports.buildAdminClientButtonLabel = buildAdminClientButtonLabel;
|
|
32
|
+
exports.extractCallbackSuffix = extractCallbackSuffix;
|
|
33
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
34
|
+
const client_1 = require("../terminal/client");
|
|
35
|
+
function trimTrailingSlashes(value) {
|
|
36
|
+
return value.replace(/\/+$/u, "");
|
|
37
|
+
}
|
|
38
|
+
function normalizeBasePath(value) {
|
|
39
|
+
const trimmed = trimTrailingSlashes(value.trim());
|
|
40
|
+
return trimmed.startsWith("/") ? trimmed || "/" : `/${trimmed || ""}`;
|
|
41
|
+
}
|
|
42
|
+
function joinHttpPath(prefix, suffix) {
|
|
43
|
+
const normalizedPrefix = prefix ? normalizeBasePath(prefix) : "";
|
|
44
|
+
const normalizedSuffix = normalizeBasePath(suffix);
|
|
45
|
+
if (!normalizedPrefix || normalizedPrefix === "/") {
|
|
46
|
+
return normalizedSuffix;
|
|
47
|
+
}
|
|
48
|
+
return `${normalizedPrefix}${normalizedSuffix}`.replace(/\/{2,}/gu, "/");
|
|
49
|
+
}
|
|
50
|
+
function resolveWebAppPublicBaseUrl(config) {
|
|
51
|
+
if (!config.webapp.publicUrl) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const rootPrefix = normalizeBasePath(process.env.ROOT_PREFIX || "/api");
|
|
55
|
+
const webAppBasePath = normalizeBasePath(config.webapp.basePath || "/webapp");
|
|
56
|
+
const expectedPath = trimTrailingSlashes(`${rootPrefix === "/" ? "" : rootPrefix}${webAppBasePath}`) || "/";
|
|
57
|
+
const url = new URL(config.webapp.publicUrl);
|
|
58
|
+
const currentPath = normalizeBasePath(url.pathname || "/");
|
|
59
|
+
if (currentPath === expectedPath) {
|
|
60
|
+
return trimTrailingSlashes(url.toString());
|
|
61
|
+
}
|
|
62
|
+
if (currentPath === webAppBasePath) {
|
|
63
|
+
url.pathname = expectedPath;
|
|
64
|
+
return trimTrailingSlashes(url.toString());
|
|
65
|
+
}
|
|
66
|
+
if (currentPath.endsWith(webAppBasePath)) {
|
|
67
|
+
url.pathname = expectedPath;
|
|
68
|
+
return trimTrailingSlashes(url.toString());
|
|
69
|
+
}
|
|
70
|
+
url.pathname = expectedPath;
|
|
71
|
+
return trimTrailingSlashes(url.toString());
|
|
72
|
+
}
|
|
73
|
+
function resolveGatewayControlBaseUrl(config) {
|
|
74
|
+
if (config.distributed.mode === "gateway" ||
|
|
75
|
+
config.distributed.mode === "both") {
|
|
76
|
+
const runtimePort = Number(process.env.PORT || config.mcp.httpPort);
|
|
77
|
+
const rootPrefix = normalizeBasePath(process.env.ROOT_PREFIX || "/api");
|
|
78
|
+
const gatewayPath = joinHttpPath(rootPrefix, "/gateway");
|
|
79
|
+
return trimTrailingSlashes(`http://127.0.0.1:${runtimePort}${gatewayPath}`);
|
|
80
|
+
}
|
|
81
|
+
if (!config.distributed.gatewayPublicUrl) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const url = new URL(config.distributed.gatewayPublicUrl);
|
|
85
|
+
url.pathname = url.pathname.replace(/\/+$/u, "");
|
|
86
|
+
if (!url.pathname.endsWith("/gateway")) {
|
|
87
|
+
url.pathname = `${url.pathname}/gateway`.replace(/\/{2,}/gu, "/");
|
|
88
|
+
}
|
|
89
|
+
return trimTrailingSlashes(url.toString());
|
|
90
|
+
}
|
|
91
|
+
function isMenuEntryCommand(text) {
|
|
92
|
+
return /^\/(?:menu|start)(?:@\w+)?$/i.test(text.trim());
|
|
93
|
+
}
|
|
94
|
+
function isHelpCommand(text) {
|
|
95
|
+
return /^\/help(?:@\w+)?$/i.test(text.trim());
|
|
96
|
+
}
|
|
97
|
+
function parseAdminAuthCommand(text) {
|
|
98
|
+
const match = text.trim().match(/^\/auth(?:@\w+)?(?:\s+(.+))?$/i);
|
|
99
|
+
return match?.[1]?.trim() || null;
|
|
100
|
+
}
|
|
101
|
+
function readMenuPayloadKey(ctx) {
|
|
102
|
+
const payload = ctx.match;
|
|
103
|
+
return typeof payload === "string" && payload.length > 0 ? payload : null;
|
|
104
|
+
}
|
|
105
|
+
function buildDatedRelativePath(fileName, date = new Date()) {
|
|
106
|
+
const dateSegment = date.toISOString().slice(0, 10);
|
|
107
|
+
const timeSegment = date.toTimeString().slice(0, 8).replace(/:/gu, "-");
|
|
108
|
+
return `${dateSegment}/${timeSegment}/${fileName}`;
|
|
109
|
+
}
|
|
110
|
+
function buildPrincipalKey(principal) {
|
|
111
|
+
return `${principal.telegramChatId}:${principal.telegramUserId}`;
|
|
112
|
+
}
|
|
113
|
+
function formatTerminalBridgeError(_config, error, fallback) {
|
|
114
|
+
if ((0, client_1.isTerminalUnavailableError)(error)) {
|
|
115
|
+
return "Terminal runtime is unavailable right now.";
|
|
116
|
+
}
|
|
117
|
+
return fallback;
|
|
118
|
+
}
|
|
119
|
+
function splitLongTelegramText(text, maxChars) {
|
|
120
|
+
const normalized = text.trim();
|
|
121
|
+
if (normalized.length <= maxChars) {
|
|
122
|
+
return [normalized];
|
|
123
|
+
}
|
|
124
|
+
const paragraphs = normalized.split("\n\n");
|
|
125
|
+
const chunks = [];
|
|
126
|
+
let current = "";
|
|
127
|
+
const flush = () => {
|
|
128
|
+
const trimmed = current.trim();
|
|
129
|
+
if (trimmed) {
|
|
130
|
+
chunks.push(trimmed);
|
|
131
|
+
}
|
|
132
|
+
current = "";
|
|
133
|
+
};
|
|
134
|
+
const appendSegment = (segment) => {
|
|
135
|
+
if (!segment) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (segment.length <= maxChars) {
|
|
139
|
+
const candidate = current ? `${current}\n\n${segment}` : segment;
|
|
140
|
+
if (candidate.length <= maxChars) {
|
|
141
|
+
current = candidate;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
flush();
|
|
145
|
+
current = segment;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
flush();
|
|
149
|
+
const lines = segment.split("\n");
|
|
150
|
+
let lineChunk = "";
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
if (line.length > maxChars) {
|
|
153
|
+
if (lineChunk) {
|
|
154
|
+
chunks.push(lineChunk.trim());
|
|
155
|
+
lineChunk = "";
|
|
156
|
+
}
|
|
157
|
+
for (let index = 0; index < line.length; index += maxChars) {
|
|
158
|
+
chunks.push(line.slice(index, index + maxChars).trim());
|
|
159
|
+
}
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const candidate = lineChunk ? `${lineChunk}\n${line}` : line;
|
|
163
|
+
if (candidate.length <= maxChars) {
|
|
164
|
+
lineChunk = candidate;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
chunks.push(lineChunk.trim());
|
|
168
|
+
lineChunk = line;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (lineChunk) {
|
|
172
|
+
current = lineChunk;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
for (const paragraph of paragraphs) {
|
|
176
|
+
appendSegment(paragraph);
|
|
177
|
+
}
|
|
178
|
+
flush();
|
|
179
|
+
return chunks.filter((chunk) => chunk.length > 0);
|
|
180
|
+
}
|
|
181
|
+
function escapeMarkdownV2(text) {
|
|
182
|
+
const specialChars = new Set([
|
|
183
|
+
"_",
|
|
184
|
+
"*",
|
|
185
|
+
"[",
|
|
186
|
+
"]",
|
|
187
|
+
"(",
|
|
188
|
+
")",
|
|
189
|
+
"~",
|
|
190
|
+
"`",
|
|
191
|
+
">",
|
|
192
|
+
"#",
|
|
193
|
+
"+",
|
|
194
|
+
"-",
|
|
195
|
+
"=",
|
|
196
|
+
"|",
|
|
197
|
+
"{",
|
|
198
|
+
"}",
|
|
199
|
+
".",
|
|
200
|
+
"!",
|
|
201
|
+
"\\",
|
|
202
|
+
]);
|
|
203
|
+
return Array.from(text, (char) => specialChars.has(char) ? `\\${char}` : char).join("");
|
|
204
|
+
}
|
|
205
|
+
function escapeMarkdownV2CodeBlock(text) {
|
|
206
|
+
return text.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
|
|
207
|
+
}
|
|
208
|
+
function escapeHtml(text) {
|
|
209
|
+
return text
|
|
210
|
+
.replace(/&/g, "&")
|
|
211
|
+
.replace(/</g, "<")
|
|
212
|
+
.replace(/>/g, ">");
|
|
213
|
+
}
|
|
214
|
+
function splitTitleAndBody(text) {
|
|
215
|
+
const normalized = text.trim();
|
|
216
|
+
const [firstLine = "", ...rest] = normalized.split("\n");
|
|
217
|
+
const title = firstLine.trim() || "Codex";
|
|
218
|
+
const body = rest.join("\n").trim();
|
|
219
|
+
return {
|
|
220
|
+
title,
|
|
221
|
+
body: body || firstLine.trim(),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function renderMarkdownChunk(title, body) {
|
|
225
|
+
return `*${escapeMarkdownV2(title)}*\n\n\`\`\`\n${escapeMarkdownV2CodeBlock(body)}\n\`\`\``;
|
|
226
|
+
}
|
|
227
|
+
function shouldNudge(lastNudgeAt, cooldownSeconds, nowMs) {
|
|
228
|
+
if (!lastNudgeAt) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
const lastMs = Date.parse(lastNudgeAt);
|
|
232
|
+
if (Number.isNaN(lastMs)) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
return nowMs - lastMs >= cooldownSeconds * 1000;
|
|
236
|
+
}
|
|
237
|
+
function slugifyFilenamePart(input) {
|
|
238
|
+
return input
|
|
239
|
+
.trim()
|
|
240
|
+
.toLowerCase()
|
|
241
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
242
|
+
.replace(/^-+|-+$/g, "")
|
|
243
|
+
.slice(0, 48);
|
|
244
|
+
}
|
|
245
|
+
function formatMenuTimestamp(timestamp) {
|
|
246
|
+
if (!timestamp) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
const date = new Date(timestamp);
|
|
250
|
+
if (Number.isNaN(date.getTime())) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
254
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
255
|
+
const year = date.getFullYear();
|
|
256
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
257
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
258
|
+
return `${day}.${month}.${year} ${hours}:${minutes}`;
|
|
259
|
+
}
|
|
260
|
+
function normalizeGatewayBaseUrl(value) {
|
|
261
|
+
const url = new URL(value);
|
|
262
|
+
url.pathname = url.pathname.replace(/\/+$/u, "");
|
|
263
|
+
if (!url.pathname.endsWith("/gateway")) {
|
|
264
|
+
url.pathname = `${url.pathname}/gateway`.replace(/\/{2,}/gu, "/");
|
|
265
|
+
}
|
|
266
|
+
return url;
|
|
267
|
+
}
|
|
268
|
+
function buildLocalHandoffId(fileName, now) {
|
|
269
|
+
const timestamp = now.toISOString().replace(/[:.]/gu, "-");
|
|
270
|
+
return `${timestamp}-${slugifyFilenamePart(fileName) || "file-handoff"}`;
|
|
271
|
+
}
|
|
272
|
+
function buildLocalNoteContent(input) {
|
|
273
|
+
return [
|
|
274
|
+
"---",
|
|
275
|
+
`handoff_id: ${JSON.stringify(input.handoffId)}`,
|
|
276
|
+
`kind: "local-file"`,
|
|
277
|
+
`created_at: ${JSON.stringify(input.createdAt)}`,
|
|
278
|
+
`session_id: ${JSON.stringify(input.sessionId)}`,
|
|
279
|
+
`session_label: ${JSON.stringify(input.sessionLabel ?? input.sessionId)}`,
|
|
280
|
+
`artifacts:\n - ${JSON.stringify(input.filePath)}`,
|
|
281
|
+
"---",
|
|
282
|
+
"",
|
|
283
|
+
"# Summary",
|
|
284
|
+
`Local file handoff: ${node_path_1.default.basename(input.filePath)}`,
|
|
285
|
+
"",
|
|
286
|
+
"# Message",
|
|
287
|
+
input.description.trim(),
|
|
288
|
+
"",
|
|
289
|
+
"# Artifacts",
|
|
290
|
+
`- ${input.filePath}`,
|
|
291
|
+
"",
|
|
292
|
+
].join("\n");
|
|
293
|
+
}
|
|
294
|
+
function buildAdminClientTitle(client) {
|
|
295
|
+
const displayName = client.telegram_display_name?.trim() || "";
|
|
296
|
+
const telegramUsername = client.telegram_username?.trim().replace(/^@/u, "") || "";
|
|
297
|
+
const botUsername = client.bot_username?.trim().replace(/^@/u, "") || "";
|
|
298
|
+
const clientLabel = client.client_label?.trim() || "";
|
|
299
|
+
const namespace = client.namespace?.trim() || "";
|
|
300
|
+
const nodeId = client.node_id?.trim() || "";
|
|
301
|
+
const runtimeLabel = [namespace, nodeId].filter(Boolean).join("/") || nodeId || "";
|
|
302
|
+
const fallback = (clientLabel || runtimeLabel || client.client_uuid).trim();
|
|
303
|
+
const identityParts = [
|
|
304
|
+
displayName || null,
|
|
305
|
+
!displayName && telegramUsername ? `@${telegramUsername}` : null,
|
|
306
|
+
!displayName && !telegramUsername && clientLabel ? clientLabel : null,
|
|
307
|
+
!displayName && !telegramUsername && !clientLabel && runtimeLabel
|
|
308
|
+
? runtimeLabel
|
|
309
|
+
: null,
|
|
310
|
+
botUsername ? `🤖@${botUsername}` : null,
|
|
311
|
+
].filter(Boolean);
|
|
312
|
+
return identityParts.length > 0 ? identityParts.join(" · ") : fallback;
|
|
313
|
+
}
|
|
314
|
+
function buildAdminClientButtonLabel(client) {
|
|
315
|
+
const markers = [
|
|
316
|
+
client.is_connected ? "🟢" : null,
|
|
317
|
+
client.is_registered ? "🗂" : null,
|
|
318
|
+
]
|
|
319
|
+
.filter(Boolean)
|
|
320
|
+
.join("");
|
|
321
|
+
const prefix = markers ? `${markers} ` : "";
|
|
322
|
+
return `${prefix}${buildAdminClientTitle(client)}`.slice(0, 56);
|
|
323
|
+
}
|
|
324
|
+
function extractCallbackSuffix(ctx, prefix) {
|
|
325
|
+
const data = ctx.callbackQuery?.data;
|
|
326
|
+
if (!data || !data.startsWith(prefix)) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
return data.slice(prefix.length) || null;
|
|
330
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransportXchangeState = void 0;
|
|
4
|
+
const relay_1 = require("../../../app/webapp/relay");
|
|
5
|
+
const client_1 = require("../terminal/client");
|
|
6
|
+
class TransportXchangeState {
|
|
7
|
+
host;
|
|
8
|
+
constructor(host) {
|
|
9
|
+
this.host = host;
|
|
10
|
+
}
|
|
11
|
+
async resolveSessionStorageAccess(sessionId) {
|
|
12
|
+
const trimmed = sessionId.trim();
|
|
13
|
+
if (!trimmed) {
|
|
14
|
+
return { sessionId: trimmed, mode: "meta" };
|
|
15
|
+
}
|
|
16
|
+
const relay = (0, relay_1.parseLiveRelaySessionId)(trimmed);
|
|
17
|
+
if (relay?.localSessionId) {
|
|
18
|
+
const localSession = await this.host.sessionStore.getSession(relay.localSessionId);
|
|
19
|
+
if (localSession?.cwd?.trim()) {
|
|
20
|
+
return { sessionId: relay.localSessionId, mode: "filesystem" };
|
|
21
|
+
}
|
|
22
|
+
return { sessionId: trimmed, mode: "relay" };
|
|
23
|
+
}
|
|
24
|
+
const direct = await this.host.sessionStore.getSession(trimmed);
|
|
25
|
+
if (direct?.cwd?.trim()) {
|
|
26
|
+
return { sessionId: trimmed, mode: "filesystem" };
|
|
27
|
+
}
|
|
28
|
+
return { sessionId: trimmed, mode: "meta" };
|
|
29
|
+
}
|
|
30
|
+
async listActiveSessionFiles(sessionId) {
|
|
31
|
+
const access = await this.resolveSessionStorageAccess(sessionId);
|
|
32
|
+
const metas = access.mode === "filesystem"
|
|
33
|
+
? await this.listReconciledSessionXchangeMetas(access.sessionId, await this.listSessionFilesystemXchangeFiles(access.sessionId))
|
|
34
|
+
: access.mode === "relay"
|
|
35
|
+
? await this.listRelaySessionXchangeFileMetas(access.sessionId, "telegram-upload")
|
|
36
|
+
: await this.host.xchangeFileMetaStore.listXchangeFileMetas(access.sessionId);
|
|
37
|
+
const uploadFiles = metas
|
|
38
|
+
.filter((meta) => meta.source === "telegram-upload")
|
|
39
|
+
.map((meta) => meta.filePath);
|
|
40
|
+
return uploadFiles.sort((left, right) => right.localeCompare(left));
|
|
41
|
+
}
|
|
42
|
+
async listActiveSessionStorageEntries(sessionId) {
|
|
43
|
+
const access = await this.resolveSessionStorageAccess(sessionId);
|
|
44
|
+
if (access.mode === "relay") {
|
|
45
|
+
const metas = await this.listRelaySessionXchangeFileMetas(access.sessionId);
|
|
46
|
+
return metas.map((meta) => ({
|
|
47
|
+
filePath: meta.filePath,
|
|
48
|
+
meta,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
if (access.mode === "meta") {
|
|
52
|
+
const metas = await this.host.xchangeFileMetaStore.listXchangeFileMetas(access.sessionId);
|
|
53
|
+
return metas.map((meta) => ({
|
|
54
|
+
filePath: meta.filePath,
|
|
55
|
+
meta,
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
const filePaths = await this.listSessionFilesystemXchangeFiles(access.sessionId);
|
|
59
|
+
const metas = await this.listReconciledSessionXchangeMetas(access.sessionId, filePaths);
|
|
60
|
+
const metaByPath = new Map(metas.map((meta) => [meta.filePath, meta]));
|
|
61
|
+
return filePaths.map((filePath) => ({
|
|
62
|
+
filePath,
|
|
63
|
+
meta: metaByPath.get(filePath) ?? null,
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
async listActiveSessionScreenshots(sessionId) {
|
|
67
|
+
const access = await this.resolveSessionStorageAccess(sessionId);
|
|
68
|
+
const metas = access.mode === "filesystem"
|
|
69
|
+
? await this.listReconciledSessionXchangeMetas(access.sessionId, await this.listSessionFilesystemXchangeFiles(access.sessionId))
|
|
70
|
+
: access.mode === "relay"
|
|
71
|
+
? await this.listRelaySessionXchangeFileMetas(access.sessionId, "browser-screenshot")
|
|
72
|
+
: await this.host.xchangeFileMetaStore.listXchangeFileMetas(access.sessionId);
|
|
73
|
+
const screenshots = metas
|
|
74
|
+
.filter((meta) => meta.source === "browser-screenshot")
|
|
75
|
+
.map((meta) => meta.filePath);
|
|
76
|
+
return screenshots.sort((left, right) => right.localeCompare(left));
|
|
77
|
+
}
|
|
78
|
+
async listSessionFilesystemXchangeFiles(sessionId) {
|
|
79
|
+
const session = await this.host.sessionStore.getSession(sessionId);
|
|
80
|
+
const resolvedWorkspaceDir = session?.cwd?.trim() || "";
|
|
81
|
+
if (!resolvedWorkspaceDir) {
|
|
82
|
+
throw new Error(`Workspace cwd is not registered for console '${sessionId}'.`);
|
|
83
|
+
}
|
|
84
|
+
const files = await (0, client_1.listXchangeFiles)(this.host.config.terminal, resolvedWorkspaceDir, this.host.config.exchange.dir);
|
|
85
|
+
return files.sort((left, right) => right.localeCompare(left));
|
|
86
|
+
}
|
|
87
|
+
async listReconciledSessionXchangeMetas(sessionId, existingFiles) {
|
|
88
|
+
const metas = await this.host.xchangeFileMetaStore.listXchangeFileMetas(sessionId);
|
|
89
|
+
if (metas.length === 0) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
const existingSet = new Set(existingFiles);
|
|
93
|
+
const staleMetas = metas.filter((meta) => !existingSet.has(meta.filePath));
|
|
94
|
+
for (const meta of staleMetas) {
|
|
95
|
+
await this.host.xchangeFileMetaStore.deleteXchangeFileMeta(sessionId, meta.filePath);
|
|
96
|
+
}
|
|
97
|
+
return metas.filter((meta) => existingSet.has(meta.filePath));
|
|
98
|
+
}
|
|
99
|
+
async listRelaySessionXchangeFileMetas(sessionId, source) {
|
|
100
|
+
const output = await this.host.callGatewayJson("/storage/list", {
|
|
101
|
+
session_id: sessionId,
|
|
102
|
+
...(source ? { source } : {}),
|
|
103
|
+
});
|
|
104
|
+
return Array.isArray(output.metas) ? output.metas : [];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.TransportXchangeState = TransportXchangeState;
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ensureXchangeDir = ensureXchangeDir;
|
|
7
|
+
exports.writeXchangeFile = writeXchangeFile;
|
|
8
|
+
exports.writeXchangeRelativeFile = writeXchangeRelativeFile;
|
|
9
|
+
exports.listXchangeFiles = listXchangeFiles;
|
|
10
|
+
exports.deleteXchangeFile = deleteXchangeFile;
|
|
11
|
+
exports.readWorkspaceFile = readWorkspaceFile;
|
|
12
|
+
exports.isTerminalUnavailableError = isTerminalUnavailableError;
|
|
13
|
+
exports.isTerminalTargetInvalidError = isTerminalTargetInvalidError;
|
|
14
|
+
exports.getConfiguredTerminalShell = getConfiguredTerminalShell;
|
|
15
|
+
exports.getConfiguredTerminalShellDisplayName = getConfiguredTerminalShellDisplayName;
|
|
16
|
+
exports.isStreamableTerminalTarget = isStreamableTerminalTarget;
|
|
17
|
+
exports.ensureTerminalTargetForSession = ensureTerminalTargetForSession;
|
|
18
|
+
exports.resolveTerminalTargetFromHint = resolveTerminalTargetFromHint;
|
|
19
|
+
exports.getTerminalWindowHeight = getTerminalWindowHeight;
|
|
20
|
+
exports.getTerminalWindowSize = getTerminalWindowSize;
|
|
21
|
+
exports.captureTerminalPaneRange = captureTerminalPaneRange;
|
|
22
|
+
exports.captureVisibleTerminal = captureVisibleTerminal;
|
|
23
|
+
exports.captureVisibleTerminalHtml = captureVisibleTerminalHtml;
|
|
24
|
+
exports.captureVisibleTerminalAnsi = captureVisibleTerminalAnsi;
|
|
25
|
+
exports.sendAllowedTerminalAction = sendAllowedTerminalAction;
|
|
26
|
+
exports.sendTerminalLiteralText = sendTerminalLiteralText;
|
|
27
|
+
exports.sendTerminalLiteralLine = sendTerminalLiteralLine;
|
|
28
|
+
exports.resizeForegroundTerminal = resizeForegroundTerminal;
|
|
29
|
+
exports.sendForegroundTerminalInput = sendForegroundTerminalInput;
|
|
30
|
+
exports.subscribeForegroundTerminal = subscribeForegroundTerminal;
|
|
31
|
+
exports.stopAllForegroundTerminals = stopAllForegroundTerminals;
|
|
32
|
+
const promises_1 = require("node:fs/promises");
|
|
33
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
34
|
+
const ptyRegistry_1 = require("../terminal/ptyRegistry");
|
|
35
|
+
const ENTER_AFTER_PASTE_DELAY_MS = 75;
|
|
36
|
+
function toPtyConfig(config) {
|
|
37
|
+
return {
|
|
38
|
+
shell: config.shell?.trim() || process.env.SHELL || "bash",
|
|
39
|
+
cols: config.cols ?? 120,
|
|
40
|
+
rows: config.rows ?? 40,
|
|
41
|
+
scrollbackLines: config.scrollbackLines ?? 4000,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function sanitizeFileName(fileName) {
|
|
45
|
+
const baseName = node_path_1.default.basename(fileName).trim();
|
|
46
|
+
const withoutControlChars = Array.from(baseName)
|
|
47
|
+
.map((char) => (char.charCodeAt(0) < 32 ? "-" : char))
|
|
48
|
+
.join("");
|
|
49
|
+
const normalized = withoutControlChars
|
|
50
|
+
.replace(/[/\\]/g, "-")
|
|
51
|
+
.replace(/[<>:"|?*]/g, "-")
|
|
52
|
+
.replace(/\s+/g, " ")
|
|
53
|
+
.trim();
|
|
54
|
+
return normalized || "file.bin";
|
|
55
|
+
}
|
|
56
|
+
function sanitizeRelativeXchangePath(relativePath) {
|
|
57
|
+
const normalized = relativePath
|
|
58
|
+
.split(/[/\\]+/u)
|
|
59
|
+
.map((segment) => segment.trim())
|
|
60
|
+
.filter((segment) => segment.length > 0 && segment !== "." && segment !== "..")
|
|
61
|
+
.join("/");
|
|
62
|
+
if (!normalized) {
|
|
63
|
+
throw new Error("Relative exchange path is required.");
|
|
64
|
+
}
|
|
65
|
+
return normalized;
|
|
66
|
+
}
|
|
67
|
+
function resolvePathInsideRoot(rootDir, relativePath) {
|
|
68
|
+
const safeRelativePath = sanitizeRelativeXchangePath(relativePath);
|
|
69
|
+
const resolvedPath = node_path_1.default.resolve(rootDir, safeRelativePath);
|
|
70
|
+
const relative = node_path_1.default.relative(rootDir, resolvedPath);
|
|
71
|
+
if (relative.startsWith("..") ||
|
|
72
|
+
node_path_1.default.isAbsolute(relative) ||
|
|
73
|
+
relative.trim() === "") {
|
|
74
|
+
throw new Error("Resolved path is outside the exchange directory.");
|
|
75
|
+
}
|
|
76
|
+
return resolvedPath;
|
|
77
|
+
}
|
|
78
|
+
function resolvePathInsideWorkspace(workspaceDir, filePath) {
|
|
79
|
+
const resolvedWorkspaceDir = node_path_1.default.resolve(workspaceDir);
|
|
80
|
+
const resolvedFilePath = node_path_1.default.isAbsolute(filePath)
|
|
81
|
+
? node_path_1.default.resolve(filePath)
|
|
82
|
+
: node_path_1.default.resolve(resolvedWorkspaceDir, filePath);
|
|
83
|
+
const relative = node_path_1.default.relative(resolvedWorkspaceDir, resolvedFilePath);
|
|
84
|
+
if (relative.startsWith("..") ||
|
|
85
|
+
node_path_1.default.isAbsolute(relative) ||
|
|
86
|
+
relative.trim() === "") {
|
|
87
|
+
throw new Error("File path is outside the workspace directory.");
|
|
88
|
+
}
|
|
89
|
+
return resolvedFilePath;
|
|
90
|
+
}
|
|
91
|
+
async function allocateAvailableFilePath(dir, fileName) {
|
|
92
|
+
const safeFileName = sanitizeFileName(fileName);
|
|
93
|
+
const extension = node_path_1.default.extname(safeFileName);
|
|
94
|
+
const baseName = node_path_1.default.basename(safeFileName, extension);
|
|
95
|
+
for (let attempt = 0; attempt < 1000; attempt += 1) {
|
|
96
|
+
const candidateName = attempt === 0
|
|
97
|
+
? safeFileName
|
|
98
|
+
: `${baseName}--${attempt}${extension}`;
|
|
99
|
+
const candidatePath = node_path_1.default.join(dir, candidateName);
|
|
100
|
+
try {
|
|
101
|
+
await (0, promises_1.access)(candidatePath);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return candidatePath;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
throw new Error("Could not allocate a unique file name in exchange directory.");
|
|
108
|
+
}
|
|
109
|
+
function delay(ms) {
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
setTimeout(resolve, ms);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async function ensureXchangeDir(config, workspaceDir, exchangeDirName) {
|
|
115
|
+
const resolvedDir = node_path_1.default.resolve(workspaceDir, exchangeDirName);
|
|
116
|
+
await (0, promises_1.mkdir)(resolvedDir, { recursive: true });
|
|
117
|
+
return resolvedDir;
|
|
118
|
+
}
|
|
119
|
+
async function writeXchangeFile(config, workspaceDir, exchangeDirName, fileName, content) {
|
|
120
|
+
const dir = await ensureXchangeDir(config, workspaceDir, exchangeDirName);
|
|
121
|
+
const outputPath = await allocateAvailableFilePath(dir, fileName);
|
|
122
|
+
await (0, promises_1.writeFile)(outputPath, content);
|
|
123
|
+
return outputPath;
|
|
124
|
+
}
|
|
125
|
+
async function writeXchangeRelativeFile(config, workspaceDir, exchangeDirName, relativePath, content, options) {
|
|
126
|
+
const dir = await ensureXchangeDir(config, workspaceDir, exchangeDirName);
|
|
127
|
+
const outputPath = resolvePathInsideRoot(dir, relativePath);
|
|
128
|
+
await (0, promises_1.mkdir)(node_path_1.default.dirname(outputPath), { recursive: true });
|
|
129
|
+
if (options?.append) {
|
|
130
|
+
await (0, promises_1.appendFile)(outputPath, content);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
await (0, promises_1.writeFile)(outputPath, content);
|
|
134
|
+
}
|
|
135
|
+
return outputPath;
|
|
136
|
+
}
|
|
137
|
+
async function listXchangeFiles(config, workspaceDir, exchangeDirName) {
|
|
138
|
+
const dir = await ensureXchangeDir(config, workspaceDir, exchangeDirName);
|
|
139
|
+
return listFilesRecursively(dir);
|
|
140
|
+
}
|
|
141
|
+
async function listFilesRecursively(rootDir) {
|
|
142
|
+
const files = [];
|
|
143
|
+
const stack = [rootDir];
|
|
144
|
+
while (stack.length > 0) {
|
|
145
|
+
const currentDir = stack.pop();
|
|
146
|
+
if (!currentDir) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const entries = await (0, promises_1.readdir)(currentDir, { withFileTypes: true });
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
const fullPath = node_path_1.default.join(currentDir, entry.name);
|
|
152
|
+
if (entry.isDirectory()) {
|
|
153
|
+
stack.push(fullPath);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (entry.isFile()) {
|
|
157
|
+
files.push(fullPath);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return files.sort((left, right) => right.localeCompare(left));
|
|
162
|
+
}
|
|
163
|
+
async function deleteXchangeFile(config, workspaceDir, exchangeDirName, filePath) {
|
|
164
|
+
const dir = await ensureXchangeDir(config, workspaceDir, exchangeDirName);
|
|
165
|
+
const resolvedFilePath = node_path_1.default.resolve(filePath);
|
|
166
|
+
const relative = node_path_1.default.relative(dir, resolvedFilePath);
|
|
167
|
+
if (relative.startsWith("..") ||
|
|
168
|
+
node_path_1.default.isAbsolute(relative) ||
|
|
169
|
+
relative.trim() === "") {
|
|
170
|
+
throw new Error("File path is outside the exchange directory.");
|
|
171
|
+
}
|
|
172
|
+
await (0, promises_1.rm)(resolvedFilePath, { force: true });
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
async function readWorkspaceFile(config, workspaceDir, filePath) {
|
|
176
|
+
const resolvedFilePath = resolvePathInsideWorkspace(workspaceDir, filePath);
|
|
177
|
+
return (0, promises_1.readFile)(resolvedFilePath);
|
|
178
|
+
}
|
|
179
|
+
function isTerminalUnavailableError(error) {
|
|
180
|
+
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
181
|
+
return message.includes("pty target is unavailable");
|
|
182
|
+
}
|
|
183
|
+
function isTerminalTargetInvalidError(error) {
|
|
184
|
+
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
185
|
+
return message.includes("unknown pty target");
|
|
186
|
+
}
|
|
187
|
+
function getConfiguredTerminalShell(config) {
|
|
188
|
+
return toPtyConfig(config).shell;
|
|
189
|
+
}
|
|
190
|
+
function getConfiguredTerminalShellDisplayName(config) {
|
|
191
|
+
return (0, ptyRegistry_1.getPtyShellDisplayName)(toPtyConfig(config));
|
|
192
|
+
}
|
|
193
|
+
function isStreamableTerminalTarget(target) {
|
|
194
|
+
return (0, ptyRegistry_1.isPtyTarget)(target);
|
|
195
|
+
}
|
|
196
|
+
function ensureTerminalTargetForSession(config, input) {
|
|
197
|
+
return (0, ptyRegistry_1.ensurePtySession)(toPtyConfig(config), {
|
|
198
|
+
sessionId: input.sessionId,
|
|
199
|
+
...(input.cwd ? { cwd: input.cwd } : {}),
|
|
200
|
+
...((0, ptyRegistry_1.isPtyTarget)(input.target) ? { target: input.target } : {}),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
async function resolveTerminalTargetFromHint(config, hint) {
|
|
204
|
+
const target = hint.terminalTarget?.trim() || (0, ptyRegistry_1.buildPtyTarget)("default");
|
|
205
|
+
return (0, ptyRegistry_1.hasPtyTarget)(target) ? target : hint.terminalTarget?.trim() ?? null;
|
|
206
|
+
}
|
|
207
|
+
async function getTerminalWindowHeight(config, target) {
|
|
208
|
+
return (0, ptyRegistry_1.getPtyWindowHeight)(target);
|
|
209
|
+
}
|
|
210
|
+
async function getTerminalWindowSize(config, target) {
|
|
211
|
+
return (0, ptyRegistry_1.getPtyWindowSize)(target);
|
|
212
|
+
}
|
|
213
|
+
async function captureTerminalPaneRange(config, target, start, includeEscapes) {
|
|
214
|
+
void includeEscapes;
|
|
215
|
+
return await (0, ptyRegistry_1.capturePtyRange)(target, start);
|
|
216
|
+
}
|
|
217
|
+
async function captureVisibleTerminal(config, target, fallbackLines, visibleScreens) {
|
|
218
|
+
return await (0, ptyRegistry_1.captureVisiblePty)(target, fallbackLines, visibleScreens);
|
|
219
|
+
}
|
|
220
|
+
async function captureVisibleTerminalHtml(config, target, fallbackLines, visibleScreens) {
|
|
221
|
+
return await (0, ptyRegistry_1.renderVisiblePtyHtml)(target, fallbackLines, visibleScreens);
|
|
222
|
+
}
|
|
223
|
+
async function captureVisibleTerminalAnsi(config, target, fallbackLines, visibleScreens) {
|
|
224
|
+
return await (0, ptyRegistry_1.renderVisiblePtyAnsi)(target, fallbackLines, visibleScreens);
|
|
225
|
+
}
|
|
226
|
+
async function sendAllowedTerminalAction(config, target, action) {
|
|
227
|
+
void config;
|
|
228
|
+
(0, ptyRegistry_1.sendPtyAction)(target, action);
|
|
229
|
+
}
|
|
230
|
+
async function sendTerminalLiteralText(config, target, text) {
|
|
231
|
+
void config;
|
|
232
|
+
const normalized = text.replace(/\r?\n/g, " ");
|
|
233
|
+
if (normalized.length > 0) {
|
|
234
|
+
(0, ptyRegistry_1.sendPtyText)(target, normalized);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function sendTerminalLiteralLine(config, target, text) {
|
|
238
|
+
await sendTerminalLiteralText(config, target, text);
|
|
239
|
+
if (text.replace(/\r?\n/g, " ").length > 0) {
|
|
240
|
+
await delay(ENTER_AFTER_PASTE_DELAY_MS);
|
|
241
|
+
}
|
|
242
|
+
(0, ptyRegistry_1.sendPtyAction)(target, "enter");
|
|
243
|
+
}
|
|
244
|
+
function resizeForegroundTerminal(target, cols, rows) {
|
|
245
|
+
(0, ptyRegistry_1.resizePtyTarget)(target, cols, rows);
|
|
246
|
+
}
|
|
247
|
+
function sendForegroundTerminalInput(target, data) {
|
|
248
|
+
(0, ptyRegistry_1.sendPtyText)(target, data);
|
|
249
|
+
}
|
|
250
|
+
function subscribeForegroundTerminal(target, input) {
|
|
251
|
+
return (0, ptyRegistry_1.subscribePtyTarget)(target, input);
|
|
252
|
+
}
|
|
253
|
+
function stopAllForegroundTerminals() {
|
|
254
|
+
(0, ptyRegistry_1.stopAllPtyTargets)();
|
|
255
|
+
}
|