Compare commits

...
Author SHA1 Message Date
abeatrix cc0675d31f wip: move connectors to hub 2026-07-15 00:39:14 +08:00
89 changed files with 517 additions and 397 deletions
-8
View File
@@ -68,19 +68,11 @@
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.16.1",
"@chat-adapter/discord": "^4.23.0",
"@chat-adapter/gchat": "^4.23.0",
"@chat-adapter/linear": "^4.23.0",
"@chat-adapter/slack": "^4.23.0",
"@chat-adapter/telegram": "^4.23.0",
"@chat-adapter/whatsapp": "^4.23.0",
"@clack/prompts": "^1.2.0",
"@cline/cline-hub": "workspace:*",
"@gramio/format": "^0.7.0",
"@opentui-ui/dialog": "^0.1.2",
"@opentui/core": "0.1.102",
"@opentui/react": "0.1.102",
"chat": "^4.23.0",
"commander": "^14.0.3",
"fzf": "^0.5.2",
"marked": "^15.0.12",
+5 -2
View File
@@ -21,6 +21,10 @@ import type {
StopReason,
} from "@agentclientprotocol/sdk";
import { PROTOCOL_VERSION, RequestError } from "@agentclientprotocol/sdk";
import {
resolveSystemPrompt,
resolveWorkspaceRoot,
} from "@cline/cline-hub/connectors";
import {
type AgentEvent,
type ClineCore,
@@ -30,11 +34,10 @@ import {
} from "@cline/core";
import type { Message } from "@cline/shared";
import { getPersistedProviderApiKey } from "../commands/auth";
import { resolveSystemPrompt } from "../runtime/prompt";
import { subscribeToAgentEvents } from "../runtime/session-events";
import { createCliCore } from "../session/session";
import { getCliBuildInfo } from "../utils/common";
import { randomSessionId, resolveWorkspaceRoot } from "../utils/helpers";
import { randomSessionId } from "../utils/helpers";
import type { Config } from "../utils/types";
import {
ACP_AUTH_METHODS,
+24 -5
View File
@@ -1,5 +1,22 @@
import { getConnector, listConnectors } from "../connectors/registry";
import type { ConnectIo, ConnectStopResult } from "../connectors/types";
import {
type ConnectIo,
type ConnectStopResult,
getConnector,
listConnectors,
} from "@cline/cline-hub/connectors";
import { createCliLoggerAdapter } from "../logging/adapter";
import { resolveCliSessionMetadata } from "../utils/enterprise";
import { ensureOAuthProviderApiKey } from "./auth";
function withConnectorHost(io: ConnectIo): ConnectIo {
return {
...io,
createLogger: createCliLoggerAdapter,
resolveSessionMetadata: resolveCliSessionMetadata,
ensureProviderApiKey: (input) =>
ensureOAuthProviderApiKey({ ...input, io }),
};
}
export async function stopAllConnectors(
io: ConnectIo,
@@ -16,7 +33,7 @@ export async function stopAllConnectors(
continue;
}
executed += 1;
const result = await connector.stopAll(io);
const result = await connector.stopAll(withConnectorHost(io));
stoppedProcesses += result.stoppedProcesses;
stoppedSessions += result.stoppedSessions;
}
@@ -49,7 +66,9 @@ export async function runStopConnector(
io.writeErr(`connect adapter "${adapterName}" does not support stop`);
return 1;
}
const result: ConnectStopResult = await connector.stopAll(io);
const result: ConnectStopResult = await connector.stopAll(
withConnectorHost(io),
);
io.writeln(
`[connect] ${connector.name} stopped processes=${result.stoppedProcesses} sessions=${result.stoppedSessions}`,
);
@@ -66,7 +85,7 @@ export async function runConnectAdapter(
io.writeErr(`unknown connect adapter "${adapterName}"`);
return 1;
}
return connector.run(passthroughArgs, io);
return connector.run(passthroughArgs, withConnectorHost(io));
}
export function formatAdapterList(): string {
+2 -1
View File
@@ -71,8 +71,9 @@ vi.mock("@cline/core", () => ({
ensureFileExists: mockEnsureFileExists,
}));
vi.mock("../connectors/common", () => ({
vi.mock("@cline/cline-hub/connectors", () => ({
isProcessRunning: vi.fn(() => false),
listActiveConnectors: vi.fn(() => []),
}));
vi.mock("./connect", () => ({
+5 -5
View File
@@ -1,6 +1,11 @@
import { spawnSync } from "node:child_process";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { dirname, join } from "node:path";
import {
type ActiveConnectorRecord,
isProcessRunning,
listActiveConnectors,
} from "@cline/cline-hub/connectors";
import {
clearHubDiscovery,
ensureFileExists,
@@ -14,11 +19,6 @@ import {
import { formatUptime, resolveClineBuildEnv } from "@cline/shared";
import { Command } from "commander";
import open from "open";
import { isProcessRunning } from "../connectors/common";
import {
type ActiveConnectorRecord,
listActiveConnectors,
} from "../connectors/status";
import { getCliBuildInfo } from "../utils/common";
import { c, writeln } from "../utils/output";
import { stopAllConnectors } from "./connect";
+2 -2
View File
@@ -11,8 +11,8 @@ vi.mock("@cline/core", () => ({
sendHubCommand: mockSendHubCommand,
}));
vi.mock("../utils/hub-runtime", () => ({
ensureCliHubServer: mockEnsureCliHubServer,
vi.mock("@cline/cline-hub/connectors", () => ({
ensureHubServer: mockEnsureCliHubServer,
parseHubEndpointOverride: (rawAddress: string | undefined) => {
const trimmed = rawAddress?.trim();
if (!trimmed) {
+5 -5
View File
@@ -1,13 +1,13 @@
import {
ensureHubServer,
parseHubEndpointOverride,
} from "@cline/cline-hub/connectors";
import {
createLocalHubScheduleRuntimeHandlers,
HubScheduleCommandService,
HubScheduleService,
sendHubCommand,
} from "@cline/core";
import {
ensureCliHubServer,
parseHubEndpointOverride,
} from "../../utils/hub-runtime";
import type { CommandIo } from "./types";
export class HubScheduleClient {
@@ -190,7 +190,7 @@ export async function ensureSchedulerHub(
}
try {
const requestedEndpoint = parseHubEndpointOverride(address);
const { url: hubUrl } = await ensureCliHubServer(
const { url: hubUrl } = await ensureHubServer(
workspaceRoot,
requestedEndpoint,
);
+2 -2
View File
@@ -1,5 +1,6 @@
import { type ChildProcess, spawn } from "node:child_process";
import { realpathSync } from "node:fs";
import { ensureHubServer } from "@cline/cline-hub/connectors";
import {
clearHubDiscovery,
isAutoUpdateEnabledGlobally,
@@ -11,7 +12,6 @@ import {
} from "@cline/core";
import { resolveClineBuildEnv } from "@cline/shared";
import { version } from "../../package.json";
import { ensureCliHubServer } from "../utils/hub-runtime";
import { c, writeErr, writeln } from "../utils/output";
import {
getInstalledKanbanVersion,
@@ -337,7 +337,7 @@ async function restartHubServerIfRunning(): Promise<void> {
// Re-ensure a fresh hub instance is spawned.
try {
await ensureCliHubServer(process.cwd()); // return value intentionally unused here
await ensureHubServer(process.cwd()); // return value intentionally unused here
writeln(`${c.green}${c.reset} ${c.dim}[hub] server restarted${c.reset}`);
} catch (err) {
writeErr(
-17
View File
@@ -1,17 +0,0 @@
export type ConnectIo = {
writeln: (text?: string) => void;
writeErr: (text: string) => void;
};
export type ConnectStopResult = {
stoppedProcesses: number;
stoppedSessions: number;
};
export interface ConnectCommandDefinition {
name: string;
description: string;
run(args: string[], io: ConnectIo): Promise<number>;
showHelp(io: ConnectIo): void;
stopAll?(io: ConnectIo): Promise<ConnectStopResult>;
}
+8 -7
View File
@@ -108,7 +108,7 @@ const loggingMocks = vi.hoisted(() => ({
flushCliLoggerAdapters: vi.fn(),
}));
const hubRuntimeMocks = vi.hoisted(() => ({
ensureCliHubServer: vi.fn(async () => ({
ensureHubServer: vi.fn(async () => ({
url: "ws://127.0.0.1:25463",
authToken: "test-token",
})),
@@ -192,8 +192,10 @@ vi.mock("./utils/feature-flags", () => ({
setCliFeatureFlagsAccountContext:
featureFlagMocks.setCliFeatureFlagsAccountContext,
}));
vi.mock("./runtime/prompt", () => ({
vi.mock("@cline/cline-hub/connectors", () => ({
resolveSystemPrompt: promptMocks.resolveSystemPrompt,
resolveWorkspaceRoot: (cwd: string) => cwd,
...hubRuntimeMocks,
}));
vi.mock("./commands/kanban", () => kanbanMocks);
vi.mock("./commands/dashboard", () => dashboardMocks);
@@ -202,7 +204,6 @@ vi.mock("./commands/update", () => updateMocks);
vi.mock("./commands/history", () => historyMocks);
vi.mock("./utils/history-resume", () => historyResumeMocks);
vi.mock("./logging/adapter", () => loggingMocks);
vi.mock("./utils/hub-runtime", () => hubRuntimeMocks);
vi.mock("./utils/telemetry", () => telemetryMocks);
vi.mock("./utils/worktree", () => worktreeMocks);
@@ -239,8 +240,8 @@ describe("runCli lightweight command dispatch", () => {
taskId: "task-1",
repoRoot: "/tmp/source",
});
hubRuntimeMocks.ensureCliHubServer.mockReset();
hubRuntimeMocks.ensureCliHubServer.mockResolvedValue({
hubRuntimeMocks.ensureHubServer.mockReset();
hubRuntimeMocks.ensureHubServer.mockResolvedValue({
url: "ws://127.0.0.1:25463",
authToken: "test-token",
});
@@ -1161,7 +1162,7 @@ describe("runCli lightweight command dispatch", () => {
await expect(runCli()).resolves.toBeUndefined();
expect(runtimeMocks.runAgent).toHaveBeenCalledTimes(1);
expect(hubRuntimeMocks.ensureCliHubServer).not.toHaveBeenCalled();
expect(hubRuntimeMocks.ensureHubServer).not.toHaveBeenCalled();
});
it("rejects yolo runs with a single bare prompt token", async () => {
@@ -1179,7 +1180,7 @@ describe("runCli lightweight command dispatch", () => {
expect.stringContaining("Unknown command or unquoted prompt: hello"),
);
expect(runtimeMocks.runAgent).not.toHaveBeenCalled();
expect(hubRuntimeMocks.ensureCliHubServer).not.toHaveBeenCalled();
expect(hubRuntimeMocks.ensureHubServer).not.toHaveBeenCalled();
});
it("rewrites /team prompts and enables teams in single-prompt mode", async () => {
+2 -2
View File
@@ -1,6 +1,7 @@
import { fstatSync } from "node:fs";
import { homedir } from "node:os";
import { basename } from "node:path";
import { resolveWorkspaceRoot } from "@cline/cline-hub/connectors";
import type { ToolPolicy } from "@cline/core";
import { registerDisposable } from "@cline/shared";
@@ -27,7 +28,6 @@ import {
import {
configureSandboxEnvironment,
normalizeAutoApproveArgs,
resolveWorkspaceRoot,
} from "./utils/helpers";
import {
c,
@@ -72,7 +72,7 @@ async function createProviderSettingsManager() {
async function loadCliRuntimeModules() {
const [coreServer, prompt, runAgentModule] = await Promise.all([
import("@cline/core"),
import("./runtime/prompt"),
import("@cline/cline-hub/connectors"),
import("./runtime/run-agent"),
]);
return {
@@ -1,9 +1,9 @@
import { describe, expect, it, vi } from "vitest";
import {
type ChatCommandState,
chatCommandHost,
createChatCommandHost,
} from "../../utils/chat-commands";
} from "@cline/cline-hub/connectors";
import { describe, expect, it, vi } from "vitest";
import type { Config } from "../../utils/types";
import {
type InteractiveChatCommandRuntime,
@@ -1,9 +1,9 @@
import type { InteractiveTurnResult } from "../../tui/types";
import type { ChatCommandHost } from "../../utils/chat-commands";
import type { ChatCommandHost } from "@cline/cline-hub/connectors";
import {
type ChatCommandState,
maybeHandleChatCommand,
} from "../../utils/chat-commands";
} from "@cline/cline-hub/connectors";
import type { InteractiveTurnResult } from "../../tui/types";
import {
enableTeamsForPrompt,
rewriteTeamPrompt,
@@ -1,7 +1,7 @@
import { resolveSystemPrompt } from "@cline/cline-hub/connectors";
import { createTool } from "@cline/shared";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { Config } from "../../utils/types";
import { resolveSystemPrompt } from "../prompt";
import {
ACT_MODE_CONTINUATION_PROMPT,
type AppliedModeChange,
@@ -12,7 +12,7 @@ import {
sendTurnWithActModeContinuation,
} from "./mode";
vi.mock("../prompt", () => ({
vi.mock("@cline/cline-hub/connectors", () => ({
resolveSystemPrompt: vi.fn(async (input: { mode?: string }) => {
return `system prompt for ${input.mode ?? "unknown"}`;
}),
+1 -1
View File
@@ -1,6 +1,6 @@
import { resolveSystemPrompt } from "@cline/cline-hub/connectors";
import { createTool } from "@cline/shared";
import type { Config } from "../../utils/types";
import { resolveSystemPrompt } from "../prompt";
export type InteractiveUiMode = "plan" | "act";
@@ -1,5 +1,5 @@
import type { ChatCommandState } from "@cline/cline-hub/connectors";
import type { TeamEvent } from "@cline/core";
import type { ChatCommandState } from "../../utils/chat-commands";
import type { Config } from "../../utils/types";
import {
CLI_DEFAULT_CHECKPOINT_CONFIG,
@@ -1,3 +1,4 @@
import type { ChatCommandState } from "@cline/cline-hub/connectors";
import {
createSessionCompactionState,
type ProviderSettingsManager,
@@ -9,7 +10,6 @@ import {
} from "@cline/core";
import type { AgentTool, Message } from "@cline/shared";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ChatCommandState } from "../../utils/chat-commands";
import type { Config } from "../../utils/types";
const createCliCoreMock = vi.hoisted(() => vi.fn());
@@ -1,3 +1,7 @@
import type {
ChatCommandState,
ForkSessionResult,
} from "@cline/cline-hub/connectors";
import {
type AgentEvent,
type AgentHooks,
@@ -18,10 +22,6 @@ import {
import type { Message } from "@cline/shared";
import { createCliCore } from "../../session/session";
import { submitAndExitInTerminal } from "../../utils/approval";
import type {
ChatCommandState,
ForkSessionResult,
} from "../../utils/chat-commands";
import { createRuntimeHooks } from "../../utils/hooks";
import { setActiveCliSession } from "../../utils/output";
import { loadInteractiveResumeMessages } from "../../utils/resume";
+1 -1
View File
@@ -105,7 +105,7 @@ vi.mock("./interactive-welcome", () => ({
resolveClineWelcomeLine: vi.fn(async () => undefined),
}));
vi.mock("./prompt", () => ({
vi.mock("@cline/cline-hub/connectors", () => ({
buildUserInputMessage: vi.fn(async () => ({
prompt: "prompt",
userImages: [],
+1 -1
View File
@@ -1,3 +1,4 @@
import { buildUserInputMessage } from "@cline/cline-hub/connectors";
import {
type AgentEvent,
type AgentResult,
@@ -41,7 +42,6 @@ import {
CLI_DEFAULT_LOOP_DETECTION,
} from "./defaults";
import { describeAbortSource, resolveMistakeLimitDecision } from "./format";
import { buildUserInputMessage } from "./prompt";
import { subscribeToAgentEvents } from "./session-events";
function printModelProviderInfo(config: Config): void {
+6 -3
View File
@@ -1,3 +1,9 @@
import {
buildUserInputMessage,
type ChatCommandState,
chatCommandHost,
createWorkspaceChatCommandHost,
} from "@cline/cline-hub/connectors";
import {
getCurrentContextSize,
type ProviderSettings,
@@ -24,7 +30,6 @@ import {
} from "../tui/interactive-welcome";
import { disableOpenTuiGraphicsProbe } from "../tui/opentui-env";
import type { QueuedPromptItem } from "../tui/types";
import { type ChatCommandState, chatCommandHost } from "../utils/chat-commands";
import { applyCliCompactionMode } from "../utils/compaction-mode";
import {
shouldZeroClineFreeModelCost,
@@ -36,7 +41,6 @@ import {
writeErr,
writeln,
} from "../utils/output";
import { createWorkspaceChatCommandHost } from "../utils/plugin-chat-commands";
import { readRepoStatus } from "../utils/repo-status";
import type { Config } from "../utils/types";
import {
@@ -62,7 +66,6 @@ import {
} from "./interactive/mode";
import { assertInteractivePreflight } from "./interactive/preflight";
import { createInteractiveSessionRuntime } from "./interactive/session-runtime";
import { buildUserInputMessage } from "./prompt";
import { getUIEventEmitter } from "./session-events";
type ModelChangeReasoningConfig = {
+5 -8
View File
@@ -6,7 +6,7 @@ const {
startRuntimeSession,
sendRuntimeSession,
buildUserInputMessage,
ensureCliHubServer,
ensureHubServer,
emitJsonLine,
writeErr,
writeln,
@@ -16,7 +16,7 @@ const {
startRuntimeSession: vi.fn(),
sendRuntimeSession: vi.fn(),
buildUserInputMessage: vi.fn(),
ensureCliHubServer: vi.fn(),
ensureHubServer: vi.fn(),
emitJsonLine: vi.fn(),
writeErr: vi.fn(),
writeln: vi.fn(),
@@ -31,12 +31,9 @@ vi.mock("@cline/core", () => ({
},
}));
vi.mock("./prompt", () => ({
vi.mock("@cline/cline-hub/connectors", () => ({
buildUserInputMessage,
}));
vi.mock("../utils/hub-runtime", () => ({
ensureCliHubServer,
ensureHubServer,
}));
vi.mock("../utils/output", () => ({
@@ -69,7 +66,7 @@ describe("runZen", () => {
userImages: [],
userFiles: [],
});
ensureCliHubServer.mockResolvedValue({
ensureHubServer.mockResolvedValue({
url: "ws://127.0.0.1:25463/hub",
authToken: "test-token",
});
+5 -3
View File
@@ -1,11 +1,13 @@
import {
buildUserInputMessage,
ensureHubServer,
} from "@cline/cline-hub/connectors";
import type { UserInstructionConfigService } from "@cline/core";
import { HubSessionClient } from "@cline/core";
import type { ChatStartSessionRequest } from "@cline/shared";
import { resolveCliSessionMetadata } from "../utils/enterprise";
import { ensureCliHubServer } from "../utils/hub-runtime";
import { c, emitJsonLine, writeErr, writeln } from "../utils/output";
import type { Config } from "../utils/types";
import { buildUserInputMessage } from "./prompt";
const ZEN_DISPATCH_ACK_TIMEOUT_MS = 5_000;
@@ -51,7 +53,7 @@ export async function runZen(
let hubUrl: string;
let hubAuthToken: string;
try {
const hub = await ensureCliHubServer(workspaceRoot);
const hub = await ensureHubServer(workspaceRoot);
hubUrl = hub.url;
hubAuthToken = hub.authToken;
} catch (error) {
+1 -1
View File
@@ -1,3 +1,4 @@
import { resolveWorkspaceRoot } from "@cline/cline-hub/connectors";
import type {
AgentConfig,
BasicLogger,
@@ -16,7 +17,6 @@ import {
prepareCliEnterpriseIntegration,
} from "../utils/enterprise";
import { getCliFeatureFlagsService } from "../utils/feature-flags";
import { resolveWorkspaceRoot } from "../utils/helpers";
import { getCliTelemetryService } from "../utils/telemetry";
import type { ConversationHistory } from "./export";
+1 -1
View File
@@ -8,7 +8,7 @@ import {
isImagePath,
loadImageAsDataUrl,
resolveExistingImagePath,
} from "../../utils/image-attachments";
} from "@cline/cline-hub/connectors";
const COMMAND_TIMEOUT_MS = 1500;
const MAX_CLIPBOARD_BYTES = 20 * 1024 * 1024;
-14
View File
@@ -1,4 +1,3 @@
import { spawnSync } from "node:child_process";
import { appendFileSync, existsSync, unlinkSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
@@ -40,19 +39,6 @@ export function randomSessionId(): string {
return `${Date.now()}_${nanoid(5)}_cli`;
}
export function resolveWorkspaceRoot(cwd: string): string {
const result = spawnSync("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
encoding: "utf8",
});
if (result.status === 0) {
const value = result.stdout.trim();
if (value) {
return value;
}
}
return cwd;
}
export function truncate(str: string, maxLen: number): string {
const oneLine = str.replace(/\n/g, " ").trim();
if (oneLine.length <= maxLen) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { resolveCliLaunchSpec } from "./internal-launch";
import { resolveCliLaunchSpec } from "@cline/cline-hub/connectors";
export interface HistoryResumeCommand {
launcher: string;
+13 -2
View File
@@ -5,7 +5,8 @@
"description": "Browser dashboard for the Cline hub: live clients, sessions, streaming chat, and hub restart.",
"type": "module",
"exports": {
".": "./src/server.ts"
".": "./src/server.ts",
"./connectors": "./src/connectors/index.ts"
},
"scripts": {
"build:webview": "bun run --cwd src/webview build",
@@ -16,8 +17,18 @@
"typecheck": "bun tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"@chat-adapter/discord": "^4.23.0",
"@chat-adapter/gchat": "^4.23.0",
"@chat-adapter/linear": "^4.23.0",
"@chat-adapter/slack": "^4.23.0",
"@chat-adapter/telegram": "^4.23.0",
"@chat-adapter/whatsapp": "^4.23.0",
"@cline/core": "workspace:*",
"@cline/llms": "workspace:*",
"@cline/shared": "workspace:*"
"@cline/shared": "workspace:*",
"@gramio/format": "^0.7.0",
"chat": "^4.23.0",
"commander": "^14.0.3",
"zod": "^4.3.6"
}
}
@@ -2,24 +2,15 @@ import {
createDiscordAdapter,
type DiscordAdapter,
} from "@chat-adapter/discord";
import type { ChatStartSessionRequest } from "@cline/core";
import {
createUserInstructionConfigService,
HubSessionClient,
} from "@cline/core";
import { createUserInstructionConfigService } from "@cline/core";
import { HubSessionClient } from "@cline/core/hub";
import type {
ChatStartSessionRequest,
ConnectDiscordOptions,
DiscordConnectorState,
} from "@cline/shared";
import { Chat, ConsoleLogger, type Thread, ThreadImpl } from "chat";
import type { Command } from "commander";
import { createCliLoggerAdapter } from "../../logging/adapter";
import {
ensureCliHubServer,
parseHubEndpointOverride,
resolveDefaultCliRpcAddress,
} from "../../utils/hub-runtime";
import { createWorkspaceChatCommandHost } from "../../utils/plugin-chat-commands";
import { ConnectorBase } from "../base";
import {
createChatSdkLogger,
@@ -33,6 +24,13 @@ import {
maybeHandleConnectorApprovalReply,
} from "../connector-host";
import { dispatchConnectorHook } from "../hooks";
import {
ensureHubServer,
parseHubEndpointOverride,
resolveDefaultHubRpcAddress,
} from "../hub-runtime";
import { createConnectorLogger } from "../logger";
import { createWorkspaceChatCommandHost } from "../plugin-chat-commands";
import {
type PendingConnectorApproval,
truncateConnectorText,
@@ -686,7 +684,7 @@ function isRestorableThread(
async function restoreDiscordThreadSubscriptions(input: {
bot: Pick<Chat, "reviver">;
bindingsPath: string;
logger: ReturnType<typeof createCliLoggerAdapter>;
logger: ReturnType<typeof createConnectorLogger>;
}): Promise<number> {
const bindings = readBindings<DiscordThreadState>(input.bindingsPath);
const restoredThreadIds = new Set<string>();
@@ -761,7 +759,7 @@ class DiscordConnector extends ConnectorBase<
.option(
"--rpc-address <host:port>",
"RPC address",
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultCliRpcAddress(),
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultHubRpcAddress(),
)
.option("--host <host>", "Webhook listen host")
.option("--port <port>", "Webhook listen port")
@@ -859,7 +857,7 @@ class DiscordConnector extends ConnectorBase<
rpcAddress:
opts.rpcAddress?.trim() ||
process.env.CLINE_RPC_ADDRESS?.trim() ||
resolveDefaultCliRpcAddress(),
resolveDefaultHubRpcAddress(),
hookCommand:
opts.hookCommand?.trim() ||
process.env.CLINE_CONNECT_HOOK_COMMAND?.trim(),
@@ -993,7 +991,7 @@ class DiscordConnector extends ConnectorBase<
return 0;
}
const loggerAdapter = createCliLoggerAdapter({
const loggerAdapter = createConnectorLogger(io, {
runtime: "cli",
component: "discord-connect",
});
@@ -1039,11 +1037,10 @@ class DiscordConnector extends ConnectorBase<
cwd: commandCwd,
workspaceRoot: startRequest.workspaceRoot || commandCwd,
});
const { url: rpcAddress, authToken: rpcAuthToken } =
await ensureCliHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const { url: rpcAddress, authToken: rpcAuthToken } = await ensureHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const clientId = `discord-${process.pid}-${Date.now()}`;
const client = new HubSessionClient({
@@ -1139,6 +1136,7 @@ class DiscordConnector extends ConnectorBase<
resolveMuteTarget: ({ target }) => resolveDiscordMuteTarget(target),
createEmptyRuntimeReplyResolver:
createDiscordEmptyRuntimeReplyResolver,
resolveSessionMetadata: io.resolveSessionMetadata,
getSessionMetadata: (currentThread, _clientId, currentState) => ({
userName: options.userName,
applicationId: options.applicationId,
@@ -1,23 +1,13 @@
import { createGoogleChatAdapter } from "@chat-adapter/gchat";
import type { ChatStartSessionRequest } from "@cline/core";
import {
createUserInstructionConfigService,
HubSessionClient,
} from "@cline/core";
import { createUserInstructionConfigService } from "@cline/core";
import { HubSessionClient } from "@cline/core/hub";
import type {
ChatStartSessionRequest,
ConnectGoogleChatOptions,
GoogleChatConnectorState,
} from "@cline/shared";
import { Chat, ConsoleLogger, type Thread } from "chat";
import type { Command } from "commander";
import type { CliLoggerAdapter } from "../../logging/adapter";
import { createCliLoggerAdapter } from "../../logging/adapter";
import {
ensureCliHubServer,
parseHubEndpointOverride,
resolveDefaultCliRpcAddress,
} from "../../utils/hub-runtime";
import { createWorkspaceChatCommandHost } from "../../utils/plugin-chat-commands";
import { ConnectorBase } from "../base";
import {
createChatSdkLogger,
@@ -31,6 +21,13 @@ import {
maybeHandleConnectorApprovalReply,
} from "../connector-host";
import { dispatchConnectorHook } from "../hooks";
import {
ensureHubServer,
parseHubEndpointOverride,
resolveDefaultHubRpcAddress,
} from "../hub-runtime";
import { createConnectorLogger } from "../logger";
import { createWorkspaceChatCommandHost } from "../plugin-chat-commands";
import {
type PendingConnectorApproval,
truncateConnectorText,
@@ -55,6 +52,7 @@ import {
import type {
ConnectCommandDefinition,
ConnectIo,
ConnectorLoggerAdapter,
ConnectStopResult,
} from "../types";
import {
@@ -166,7 +164,7 @@ async function persistGoogleChatThreadContext(input: {
async function deliverScheduledResult(input: {
bot: Chat;
client: HubSessionClient;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
bindingsPath: string;
userName: string;
scheduleId: string;
@@ -253,7 +251,7 @@ class GoogleChatConnector extends ConnectorBase<
.option(
"--rpc-address <host:port>",
"RPC address",
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultCliRpcAddress(),
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultHubRpcAddress(),
)
.option("--host <host>", "Webhook listen host")
.option("--port <port>", "Webhook listen port")
@@ -319,7 +317,7 @@ class GoogleChatConnector extends ConnectorBase<
rpcAddress:
opts.rpcAddress?.trim() ||
process.env.CLINE_RPC_ADDRESS?.trim() ||
resolveDefaultCliRpcAddress(),
resolveDefaultHubRpcAddress(),
hookCommand:
opts.hookCommand?.trim() ||
process.env.CLINE_CONNECT_HOOK_COMMAND?.trim(),
@@ -446,7 +444,7 @@ class GoogleChatConnector extends ConnectorBase<
return 0;
}
const loggerAdapter = createCliLoggerAdapter({
const loggerAdapter = createConnectorLogger(io, {
runtime: "cli",
component: "gchat-connect",
});
@@ -543,11 +541,10 @@ class GoogleChatConnector extends ConnectorBase<
cwd: commandCwd,
workspaceRoot: startRequest.workspaceRoot || commandCwd,
});
const { url: rpcAddress, authToken: rpcAuthToken } =
await ensureCliHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const { url: rpcAddress, authToken: rpcAuthToken } = await ensureHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const clientId = `gchat-${process.pid}-${Date.now()}`;
const client = new HubSessionClient({
@@ -614,6 +611,7 @@ class GoogleChatConnector extends ConnectorBase<
chatCommandHost,
activeTurns,
turnKey: queueKey,
resolveSessionMetadata: io.resolveSessionMetadata,
getSessionMetadata: (currentThread, _clientId, currentState) => ({
userName: options.userName,
gchatThreadId: currentThread.id,
@@ -1,19 +1,12 @@
import type { ChatStartSessionRequest } from "@cline/core";
import {
createUserInstructionConfigService,
HubSessionClient,
} from "@cline/core";
import type { ConnectLinearOptions, LinearConnectorState } from "@cline/shared";
import { createUserInstructionConfigService } from "@cline/core";
import { HubSessionClient } from "@cline/core/hub";
import type {
ChatStartSessionRequest,
ConnectLinearOptions,
LinearConnectorState,
} from "@cline/shared";
import { type Adapter, Chat, ConsoleLogger, type Thread } from "chat";
import type { Command } from "commander";
import type { CliLoggerAdapter } from "../../logging/adapter";
import { createCliLoggerAdapter } from "../../logging/adapter";
import {
ensureCliHubServer,
parseHubEndpointOverride,
resolveDefaultCliRpcAddress,
} from "../../utils/hub-runtime";
import { createWorkspaceChatCommandHost } from "../../utils/plugin-chat-commands";
import { ConnectorBase } from "../base";
import {
createChatSdkLogger,
@@ -27,6 +20,13 @@ import {
maybeHandleConnectorApprovalReply,
} from "../connector-host";
import { dispatchConnectorHook } from "../hooks";
import {
ensureHubServer,
parseHubEndpointOverride,
resolveDefaultHubRpcAddress,
} from "../hub-runtime";
import { createConnectorLogger } from "../logger";
import { createWorkspaceChatCommandHost } from "../plugin-chat-commands";
import {
type PendingConnectorApproval,
truncateConnectorText,
@@ -51,6 +51,7 @@ import {
import type {
ConnectCommandDefinition,
ConnectIo,
ConnectorLoggerAdapter,
ConnectStopResult,
} from "../types";
import { getConnectorSystemPrompt } from "./prompts";
@@ -204,7 +205,7 @@ async function persistLinearThreadContext(input: {
async function deliverScheduledResult(input: {
bot: Chat;
client: HubSessionClient;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
bindingsPath: string;
userName: string;
scheduleId: string;
@@ -313,7 +314,7 @@ class LinearConnector extends ConnectorBase<
.option(
"--rpc-address <host:port>",
"RPC address",
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultCliRpcAddress(),
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultHubRpcAddress(),
)
.option("--host <host>", "Webhook listen host")
.option("--port <port>", "Webhook listen port")
@@ -399,7 +400,7 @@ class LinearConnector extends ConnectorBase<
rpcAddress:
opts.rpcAddress?.trim() ||
process.env.CLINE_RPC_ADDRESS?.trim() ||
resolveDefaultCliRpcAddress(),
resolveDefaultHubRpcAddress(),
hookCommand:
opts.hookCommand?.trim() ||
process.env.CLINE_CONNECT_HOOK_COMMAND?.trim(),
@@ -513,7 +514,7 @@ class LinearConnector extends ConnectorBase<
return 0;
}
const loggerAdapter = createCliLoggerAdapter({
const loggerAdapter = createConnectorLogger(io, {
runtime: "cli",
component: "linear-connect",
});
@@ -578,11 +579,10 @@ class LinearConnector extends ConnectorBase<
cwd: commandCwd,
workspaceRoot: startRequest.workspaceRoot || commandCwd,
});
const { url: rpcAddress, authToken: rpcAuthToken } =
await ensureCliHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const { url: rpcAddress, authToken: rpcAuthToken } = await ensureHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const clientId = `linear-${process.pid}-${Date.now()}`;
const client = new HubSessionClient({
@@ -651,6 +651,7 @@ class LinearConnector extends ConnectorBase<
chatCommandHost,
activeTurns,
turnKey: queueKey,
resolveSessionMetadata: io.resolveSessionMetadata,
getSessionMetadata: (currentThread, _clientId, currentState) => ({
userName: options.userName,
linearThreadId: currentThread.id,
@@ -1,10 +1,11 @@
import { createSlackAdapter, type SlackAdapter } from "@chat-adapter/slack";
import type { ChatStartSessionRequest } from "@cline/core";
import {
createUserInstructionConfigService,
HubSessionClient,
} from "@cline/core";
import type { ConnectSlackOptions, SlackConnectorState } from "@cline/shared";
import { createUserInstructionConfigService } from "@cline/core";
import { HubSessionClient } from "@cline/core/hub";
import type {
ChatStartSessionRequest,
ConnectSlackOptions,
SlackConnectorState,
} from "@cline/shared";
import {
type Adapter,
Chat,
@@ -14,14 +15,6 @@ import {
ThreadImpl,
} from "chat";
import type { Command } from "commander";
import type { CliLoggerAdapter } from "../../logging/adapter";
import { createCliLoggerAdapter } from "../../logging/adapter";
import {
ensureCliHubServer,
parseHubEndpointOverride,
resolveDefaultCliRpcAddress,
} from "../../utils/hub-runtime";
import { createWorkspaceChatCommandHost } from "../../utils/plugin-chat-commands";
import { ConnectorBase } from "../base";
import {
createChatSdkLogger,
@@ -35,6 +28,13 @@ import {
maybeHandleConnectorApprovalReply,
} from "../connector-host";
import { dispatchConnectorHook } from "../hooks";
import {
ensureHubServer,
parseHubEndpointOverride,
resolveDefaultHubRpcAddress,
} from "../hub-runtime";
import { createConnectorLogger } from "../logger";
import { createWorkspaceChatCommandHost } from "../plugin-chat-commands";
import {
type PendingConnectorApproval,
truncateConnectorText,
@@ -61,6 +61,7 @@ import {
import type {
ConnectCommandDefinition,
ConnectIo,
ConnectorLoggerAdapter,
ConnectStopResult,
} from "../types";
import {
@@ -351,7 +352,7 @@ async function deliverScheduledResult(input: {
bot: Chat;
slack: SlackAdapter;
client: HubSessionClient;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
bindingsPath: string;
userName: string;
scheduleId: string;
@@ -481,7 +482,7 @@ class SlackConnector extends ConnectorBase<
.option(
"--rpc-address <host:port>",
"RPC address",
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultCliRpcAddress(),
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultHubRpcAddress(),
)
.option("--host <host>", "Webhook listen host")
.option("--port <port>", "Webhook listen port")
@@ -592,7 +593,7 @@ class SlackConnector extends ConnectorBase<
rpcAddress:
opts.rpcAddress?.trim() ||
process.env.CLINE_RPC_ADDRESS?.trim() ||
resolveDefaultCliRpcAddress(),
resolveDefaultHubRpcAddress(),
hookCommand:
opts.hookCommand?.trim() ||
process.env.CLINE_CONNECT_HOOK_COMMAND?.trim(),
@@ -707,7 +708,7 @@ class SlackConnector extends ConnectorBase<
return 0;
}
const loggerAdapter = createCliLoggerAdapter({
const loggerAdapter = createConnectorLogger(io, {
runtime: "cli",
component: "slack-connect",
});
@@ -772,11 +773,10 @@ class SlackConnector extends ConnectorBase<
cwd: commandCwd,
workspaceRoot: startRequest.workspaceRoot || commandCwd,
});
const { url: rpcAddress, authToken: rpcAuthToken } =
await ensureCliHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const { url: rpcAddress, authToken: rpcAuthToken } = await ensureHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const clientId = `slack-${process.pid}-${Date.now()}`;
const client = new HubSessionClient({
@@ -856,6 +856,7 @@ class SlackConnector extends ConnectorBase<
chatCommandHost,
activeTurns,
turnKey: queueKey,
resolveSessionMetadata: io.resolveSessionMetadata,
getSessionMetadata: (
currentThread,
_clientId,
@@ -1,7 +1,7 @@
import type { SentMessage, Thread } from "chat";
import { describe, expect, it, vi } from "vitest";
import type { CliLoggerAdapter } from "../../logging/adapter";
import type { ConnectorThreadState } from "../thread-bindings";
import type { ConnectorLoggerAdapter } from "../types";
import {
buildTelegramFormattedPayload,
buildTelegramFormattedPayloads,
@@ -16,7 +16,7 @@ function createLogger() {
log: vi.fn(),
error: vi.fn(),
},
} as unknown as CliLoggerAdapter;
} as unknown as ConnectorLoggerAdapter;
}
function createThread(id = "telegram:123") {
@@ -1,7 +1,7 @@
import { markdownToFormattable } from "@gramio/format/markdown";
import type { Thread } from "chat";
import type { CliLoggerAdapter } from "../../logging/adapter";
import type { ConnectorThreadState } from "../thread-bindings";
import type { ConnectorLoggerAdapter } from "../types";
const TELEGRAM_API_BASE = "https://api.telegram.org";
const TELEGRAM_MESSAGE_LIMIT = 4096;
@@ -211,7 +211,7 @@ export async function postTelegramFormattedReply<
thread: Thread<TState>;
text: string;
botToken: string;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
apiBaseUrl?: string;
fetchImpl?: FetchLike;
}): Promise<void> {
@@ -1,23 +1,13 @@
import { createTelegramAdapter } from "@chat-adapter/telegram";
import type { ChatStartSessionRequest } from "@cline/core";
import {
createUserInstructionConfigService,
HubSessionClient,
} from "@cline/core";
import { createUserInstructionConfigService } from "@cline/core";
import { HubSessionClient } from "@cline/core/hub";
import type {
ChatStartSessionRequest,
ConnectTelegramOptions,
TelegramConnectorState,
} from "@cline/shared";
import { Chat, ConsoleLogger, type Thread } from "chat";
import type { Command } from "commander";
import type { CliLoggerAdapter } from "../../logging/adapter";
import { createCliLoggerAdapter } from "../../logging/adapter";
import {
ensureCliHubServer,
parseHubEndpointOverride,
resolveDefaultCliRpcAddress,
} from "../../utils/hub-runtime";
import { createWorkspaceChatCommandHost } from "../../utils/plugin-chat-commands";
import { ConnectorBase } from "../base";
import { createChatSdkLogger, enqueueThreadTurn } from "../chat-runtime";
import { isProcessRunning } from "../common";
@@ -27,6 +17,13 @@ import {
maybeHandleConnectorApprovalReply,
} from "../connector-host";
import { dispatchConnectorHook } from "../hooks";
import {
ensureHubServer,
parseHubEndpointOverride,
resolveDefaultHubRpcAddress,
} from "../hub-runtime";
import { createConnectorLogger } from "../logger";
import { createWorkspaceChatCommandHost } from "../plugin-chat-commands";
import {
type PendingConnectorApproval,
truncateConnectorText,
@@ -51,6 +48,7 @@ import {
import type {
ConnectCommandDefinition,
ConnectIo,
ConnectorLoggerAdapter,
ConnectStopResult,
} from "../types";
import {
@@ -268,7 +266,7 @@ async function persistTelegramThreadContext(input: {
async function deliverScheduledResult(input: {
bot: Chat;
client: HubSessionClient;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
bindingsPath: string;
botUsername: string;
scheduleId: string;
@@ -443,7 +441,7 @@ class TelegramConnector extends ConnectorBase<
.option(
"--rpc-address <host:port>",
"RPC address",
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultCliRpcAddress(),
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultHubRpcAddress(),
)
.addHelpText(
"after",
@@ -508,7 +506,7 @@ class TelegramConnector extends ConnectorBase<
rpcAddress:
opts.rpcAddress?.trim() ||
process.env.CLINE_RPC_ADDRESS?.trim() ||
resolveDefaultCliRpcAddress(),
resolveDefaultHubRpcAddress(),
hookCommand: allowedUserId
? buildTelegramAllowedUserHookCommand(
normalizeAllowedTelegramUserId(allowedUserId),
@@ -670,7 +668,7 @@ class TelegramConnector extends ConnectorBase<
return 0;
}
const loggerAdapter = createCliLoggerAdapter({
const loggerAdapter = createConnectorLogger(io, {
runtime: "cli",
component: "telegram-connect",
});
@@ -713,11 +711,10 @@ class TelegramConnector extends ConnectorBase<
cwd: commandCwd,
workspaceRoot: startRequest.workspaceRoot || commandCwd,
});
const { url: rpcAddress, authToken: rpcAuthToken } =
await ensureCliHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const { url: rpcAddress, authToken: rpcAuthToken } = await ensureHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const clientId = `telegram-${process.pid}-${Date.now()}`;
const client = new HubSessionClient({
@@ -826,6 +823,7 @@ class TelegramConnector extends ConnectorBase<
logger: loggerAdapter,
});
},
resolveSessionMetadata: io.resolveSessionMetadata,
getSessionMetadata: (currentThread, _clientId, currentState) => ({
botUserName: options.botUsername,
telegramThreadId: currentThread.id,
@@ -1,23 +1,13 @@
import { createWhatsAppAdapter } from "@chat-adapter/whatsapp";
import type { ChatStartSessionRequest } from "@cline/core";
import {
createUserInstructionConfigService,
HubSessionClient,
} from "@cline/core";
import { createUserInstructionConfigService } from "@cline/core";
import { HubSessionClient } from "@cline/core/hub";
import type {
ChatStartSessionRequest,
ConnectWhatsAppOptions,
WhatsAppConnectorState,
} from "@cline/shared";
import { Chat, ConsoleLogger, type Thread } from "chat";
import type { Command } from "commander";
import type { CliLoggerAdapter } from "../../logging/adapter";
import { createCliLoggerAdapter } from "../../logging/adapter";
import {
ensureCliHubServer,
parseHubEndpointOverride,
resolveDefaultCliRpcAddress,
} from "../../utils/hub-runtime";
import { createWorkspaceChatCommandHost } from "../../utils/plugin-chat-commands";
import { ConnectorBase } from "../base";
import {
createChatSdkLogger,
@@ -31,6 +21,13 @@ import {
maybeHandleConnectorApprovalReply,
} from "../connector-host";
import { dispatchConnectorHook } from "../hooks";
import {
ensureHubServer,
parseHubEndpointOverride,
resolveDefaultHubRpcAddress,
} from "../hub-runtime";
import { createConnectorLogger } from "../logger";
import { createWorkspaceChatCommandHost } from "../plugin-chat-commands";
import {
type PendingConnectorApproval,
truncateConnectorText,
@@ -55,6 +52,7 @@ import {
import type {
ConnectCommandDefinition,
ConnectIo,
ConnectorLoggerAdapter,
ConnectStopResult,
} from "../types";
import {
@@ -190,7 +188,7 @@ async function persistWhatsAppThreadContext(input: {
async function deliverScheduledResult(input: {
bot: Chat;
client: HubSessionClient;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
bindingsPath: string;
options: ConnectWhatsAppOptions;
scheduleId: string;
@@ -296,7 +294,7 @@ class WhatsAppConnector extends ConnectorBase<
.option(
"--rpc-address <host:port>",
"RPC address",
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultCliRpcAddress(),
process.env.CLINE_RPC_ADDRESS?.trim() || resolveDefaultHubRpcAddress(),
)
.option("--host <host>", "Webhook listen host")
.option("--port <port>", "Webhook listen port")
@@ -367,7 +365,7 @@ class WhatsAppConnector extends ConnectorBase<
rpcAddress:
opts.rpcAddress?.trim() ||
process.env.CLINE_RPC_ADDRESS?.trim() ||
resolveDefaultCliRpcAddress(),
resolveDefaultHubRpcAddress(),
hookCommand:
opts.hookCommand?.trim() ||
process.env.CLINE_CONNECT_HOOK_COMMAND?.trim(),
@@ -485,7 +483,7 @@ class WhatsAppConnector extends ConnectorBase<
return 0;
}
const loggerAdapter = createCliLoggerAdapter({
const loggerAdapter = createConnectorLogger(io, {
runtime: "cli",
component: "whatsapp-connect",
});
@@ -545,11 +543,10 @@ class WhatsAppConnector extends ConnectorBase<
cwd: commandCwd,
workspaceRoot: startRequest.workspaceRoot || commandCwd,
});
const { url: rpcAddress, authToken: rpcAuthToken } =
await ensureCliHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const { url: rpcAddress, authToken: rpcAuthToken } = await ensureHubServer(
startRequest.workspaceRoot || startRequest.cwd || process.cwd(),
parseHubEndpointOverride(options.rpcAddress),
);
const clientId = `whatsapp-${process.pid}-${Date.now()}`;
const client = new HubSessionClient({
@@ -623,6 +620,7 @@ class WhatsAppConnector extends ConnectorBase<
chatCommandHost,
activeTurns,
turnKey: queueKey,
resolveSessionMetadata: io.resolveSessionMetadata,
getSessionMetadata: (currentThread, _clientId, currentState) => ({
userName: options.userName,
phoneNumberId: options.phoneNumberId,
@@ -1,6 +1,6 @@
import { existsSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { resolveClineDataDir } from "@cline/core";
import { resolveClineDataDir } from "@cline/shared/storage";
import { Command, CommanderError } from "commander";
import {
isProcessRunning,
@@ -158,6 +158,7 @@ export abstract class ConnectorBase<Options, State>
["connect", this.name],
input.rawArgs,
input.childEnvVar,
input.io,
);
if (!pid) {
input.io.writeErr(input.launchFailureMessage);
@@ -1,6 +1,6 @@
import { stat } from "node:fs/promises";
import { resolve } from "node:path";
import { resolveWorkspaceRoot } from "./helpers";
import { resolveWorkspaceRoot } from "./workspace";
export type ChatCommandState = {
enableTools: boolean;
@@ -1,6 +1,6 @@
import type { CliLoggerAdapter } from "../logging/adapter";
import type { ConnectorLoggerAdapter } from "./types";
export function createChatSdkLogger(adapter: CliLoggerAdapter) {
export function createChatSdkLogger(adapter: ConnectorLoggerAdapter) {
return {
child(prefix: string) {
return createChatSdkLogger(adapter.child({ chatLogger: prefix }));
@@ -80,18 +80,20 @@ export async function startConnectorWebhookServer(input: {
const hostHeader = req.headers.host || `${input.host}:${input.port}`;
const requestUrl = new URL(req.url || "/", `http://${hostHeader}`);
const body = await readRequestBody(req);
const headers = new Headers();
for (const [key, value] of Object.entries(req.headers)) {
if (Array.isArray(value)) {
for (const entry of value) {
headers.append(key, entry);
}
} else if (typeof value === "string") {
headers.append(key, value);
}
}
const request = new Request(requestUrl.toString(), {
method: req.method,
headers: new Headers(
Object.entries(req.headers).flatMap(([key, value]) => {
if (Array.isArray(value)) {
return value.map((entry) => [key, entry] as [string, string]);
}
return typeof value === "string" ? [[key, value]] : [];
}),
),
headers,
body,
duplex: body ? "half" : undefined,
});
const handler =
input.routes[requestUrl.pathname] ??
@@ -1,4 +1,4 @@
import { dirname, resolve } from "node:path";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import {
@@ -26,16 +26,15 @@ describe("spawnDetachedConnector", () => {
});
it("preserves bun conditions and resolves the cli entrypoint for detached launches", () => {
const connectorsDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(connectorsDir, "../../../../");
const entryPath = fileURLToPath(import.meta.url);
expect(
__test__.buildDetachedConnectorCommand(
["connect", "telegram"],
["-m", "ClineAdapterBot", "-k", "token-123"],
"/Users/test/.bun/bin/bun",
"./apps/cli/src/index.ts",
entryPath,
["--conditions=development"],
repoRoot,
dirname(entryPath),
{},
),
).toEqual({
@@ -44,7 +43,7 @@ describe("spawnDetachedConnector", () => {
"--inspect=127.0.0.1:0",
"--enable-source-maps",
"--conditions=development",
resolve(repoRoot, "apps/cli/src/index.ts"),
entryPath,
"connect",
"telegram",
"-m",
@@ -57,16 +56,15 @@ describe("spawnDetachedConnector", () => {
});
it("uses a dynamic connector inspector port for development node launches", () => {
const connectorsDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(connectorsDir, "../../../../");
const entryPath = fileURLToPath(import.meta.url);
expect(
__test__.buildDetachedConnectorCommand(
["connect", "telegram"],
["-m", "ClineAdapterBot"],
"/usr/local/bin/node",
"./apps/cli/src/index.ts",
entryPath,
[],
repoRoot,
dirname(entryPath),
{ CLINE_BUILD_ENV: "development" },
),
).toEqual({
@@ -74,7 +72,7 @@ describe("spawnDetachedConnector", () => {
childArgs: [
"--inspect=127.0.0.1:0",
"--enable-source-maps",
resolve(repoRoot, "apps/cli/src/index.ts"),
entryPath,
"connect",
"telegram",
"-m",
@@ -8,12 +8,12 @@ import {
writeFileSync,
} from "node:fs";
import { join } from "node:path";
import type { HubSessionClient, HubSessionRow } from "@cline/core";
import { ensureParentDir, resolveClineDataDir } from "@cline/core";
import type { HubSessionClient, HubSessionRow } from "@cline/core/hub";
import { withResolvedClineBuildEnv } from "@cline/shared";
import { createCliLoggerAdapter } from "../logging/adapter";
import { logSpawnedProcess } from "../logging/process";
import { resolveCliLaunchSpec } from "../utils/internal-launch";
import { ensureParentDir, resolveClineDataDir } from "@cline/shared/storage";
import { resolveCliLaunchSpec } from "./internal-launch";
import { createConnectorLogger } from "./logger";
import type { ConnectIo } from "./types";
export function parseBooleanFlag(rawArgs: string[], flag: string): boolean {
return rawArgs.includes(flag);
@@ -154,6 +154,7 @@ export function spawnDetachedConnector(
commandPrefixArgs: string[],
rawArgs: string[],
childEnvKey: string,
io: ConnectIo,
options?: {
logPath?: string;
component?: string;
@@ -163,7 +164,7 @@ export function spawnDetachedConnector(
const command = buildDetachedConnectorCommand(commandPrefixArgs, rawArgs);
if (!command) {
try {
const logger = createCliLoggerAdapter({
const logger = createConnectorLogger(io, {
runtime: "cli",
component: options?.component ?? "connectors",
});
@@ -198,24 +199,26 @@ export function spawnDetachedConnector(
// processes otherwise allocate a new visible console.
windowsHide: true,
});
logSpawnedProcess({
createConnectorLogger(io, {
runtime: "cli",
component: options?.component ?? "connectors",
command: [command.launcher, ...command.childArgs],
}).core.log("Process spawned", {
command: [command.launcher, ...command.childArgs].join(" "),
commandArgs: command.childArgs,
executable: command.launcher,
childPid: child.pid ?? undefined,
cwd: process.cwd(),
detached: true,
metadata: {
childEnvKey,
purpose: "connector.detached",
logPath: options?.logPath,
...options?.metadata,
},
childEnvKey,
purpose: "connector.detached",
logPath: options?.logPath,
...options?.metadata,
});
child.unref();
return child.pid ?? 0;
} catch (error) {
try {
const logger = createCliLoggerAdapter({
const logger = createConnectorLogger(io, {
runtime: "cli",
component: options?.component ?? "connectors",
});
@@ -1,14 +1,12 @@
import { readFileSync } from "node:fs";
import { basename } from "node:path";
import type { UserInstructionConfigService } from "@cline/core";
import type { HubSessionClient } from "@cline/core/hub";
import type {
ChatRunTurnRequest,
ChatStartSessionRequest,
HubSessionClient,
UserInstructionConfigService,
} from "@cline/core";
} from "@cline/shared";
import type { SentMessage, Thread } from "chat";
import type { CliLoggerAdapter } from "../logging/adapter";
import { buildUserInputMessage, resolveSystemPrompt } from "../runtime/prompt";
import {
type ChatCommandHost,
type ChatCommandState,
@@ -16,8 +14,9 @@ import {
type MuteCommandInput,
maybeHandleChatCommand,
normalizeCommandName,
} from "../utils/chat-commands";
} from "./chat-commands";
import { authorizeConnectorEvent, dispatchConnectorHook } from "./hooks";
import { buildUserInputMessage, resolveSystemPrompt } from "./prompt";
import {
createConnectorRuntimeTurnStream,
formatConnectorApprovalPrompt,
@@ -44,6 +43,7 @@ import {
setParticipantMuted,
setThreadMuted,
} from "./thread-bindings";
import type { ConnectIo, ConnectorLoggerAdapter } from "./types";
export type ActiveConnectorTurn = {
sessionId: string;
@@ -211,7 +211,7 @@ export async function handleConnectorUserTurn<
baseStartRequest: ChatStartSessionRequest;
explicitSystemPrompt: string | undefined;
clientId: string;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
transport: string;
botUserName?: string;
ownerParticipantKeys?: string[];
@@ -225,6 +225,7 @@ export async function handleConnectorUserTurn<
clientId: string,
currentState: TState,
) => Record<string, unknown>;
resolveSessionMetadata?: ConnectIo["resolveSessionMetadata"];
getScheduleDeliveryMetadata?: (
thread: Thread<TState>,
) => Record<string, unknown>;
@@ -939,6 +940,7 @@ export async function handleConnectorUserTurn<
input.clientId,
currentState,
),
resolveSessionMetadata: input.resolveSessionMetadata,
reusedLogMessage: input.reusedLogMessage,
startedLogMessage: input.startedLogMessage,
});
@@ -5,7 +5,7 @@ import type {
ConnectorHookEvent,
} from "@cline/shared";
import { z } from "zod";
import type { CliLoggerAdapter } from "../logging/adapter";
import type { ConnectorLoggerAdapter } from "./types";
const ConnectorAuthorizationDecisionSchema = z.object({
action: z.enum(["allow", "deny"]).default("allow"),
@@ -17,7 +17,7 @@ const ConnectorAuthorizationDecisionSchema = z.object({
export async function dispatchConnectorHook(
command: string | undefined,
hookPayload: ConnectorHookEvent,
logger: CliLoggerAdapter,
logger: ConnectorLoggerAdapter,
): Promise<void> {
const trimmed = command?.trim();
if (!trimmed) {
@@ -70,7 +70,7 @@ export async function authorizeConnectorEvent(
botUserName?: string;
request: ConnectorAuthorizationRequest;
},
logger: CliLoggerAdapter,
logger: ConnectorLoggerAdapter,
): Promise<ConnectorAuthorizationDecision> {
const trimmed = command?.trim();
if (!trimmed) {
@@ -4,14 +4,14 @@ import {
type HubEndpointOverrides,
resolveDefaultHubHost,
resolveDefaultHubPort,
} from "@cline/core";
} from "@cline/core/hub";
/**
* Build a `host:port` rpc address string that respects the current build
* environment. In development, this picks the dev hub port to avoid
* colliding with a production Cline hub on the standard port.
*/
export function resolveDefaultCliRpcAddress(): string {
export function resolveDefaultHubRpcAddress(): string {
return `${resolveDefaultHubHost()}:${resolveDefaultHubPort()}`;
}
@@ -39,7 +39,7 @@ export function parseHubEndpointOverride(
}
}
export async function ensureCliHubServer(
export async function ensureHubServer(
workspaceRoot: string,
endpoint: HubEndpointOverrides = {},
): Promise<DetachedHubResolution> {
+12
View File
@@ -0,0 +1,12 @@
export * from "./catalog";
export * from "./chat-commands";
export { isProcessRunning } from "./common";
export * from "./hub-runtime";
export * from "./image-attachments";
export * from "./internal-launch";
export * from "./plugin-chat-commands";
export * from "./prompt";
export * from "./registry";
export * from "./status";
export * from "./types";
export * from "./workspace";
@@ -1,4 +1,4 @@
import { dirname, resolve } from "node:path";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, describe, expect, it } from "vitest";
import {
@@ -14,13 +14,12 @@ describe("internal launch helpers", () => {
});
it("resolves the source entrypoint when running from TypeScript", () => {
const utilsDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(utilsDir, "../../../../");
const entryPath = fileURLToPath(import.meta.url);
const spec = resolveCliLaunchSpec({
execPath: "/Users/test/.bun/bin/bun",
argv: ["bun", "./apps/cli/src/index.ts"],
argv: ["bun", entryPath],
execArgv: ["--conditions=development"],
cwd: repoRoot,
cwd: dirname(entryPath),
env: {},
});
@@ -30,9 +29,9 @@ describe("internal launch helpers", () => {
"--inspect=127.0.0.1:0",
"--enable-source-maps",
"--conditions=development",
resolve(repoRoot, "apps/cli/src/index.ts"),
entryPath,
],
identityPath: resolve(repoRoot, "apps/cli/src/index.ts"),
identityPath: entryPath,
mode: "source",
});
});
@@ -52,13 +51,12 @@ describe("internal launch helpers", () => {
});
it("adds node debug flags for development node launches", () => {
const utilsDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(utilsDir, "../../../../");
const entryPath = fileURLToPath(import.meta.url);
const command = buildCliSubcommandCommand("hub", ["start"], {
execPath: "/usr/local/bin/node",
argv: ["node", "./apps/cli/src/index.ts"],
argv: ["node", entryPath],
execArgv: [],
cwd: repoRoot,
cwd: dirname(entryPath),
env: { CLINE_BUILD_ENV: "development" },
});
@@ -67,7 +65,7 @@ describe("internal launch helpers", () => {
childArgs: [
"--inspect=127.0.0.1:0",
"--enable-source-maps",
resolve(repoRoot, "apps/cli/src/index.ts"),
entryPath,
"hub",
"start",
],
+39
View File
@@ -0,0 +1,39 @@
import { join } from "node:path";
import type { BasicLogger, RuntimeLoggerConfig } from "@cline/shared";
import { noopBasicLogger } from "@cline/shared";
import { resolveClineDataDir } from "@cline/shared/storage";
import type {
ConnectIo,
ConnectorLoggerAdapter,
CreateConnectorLoggerInput,
} from "./types";
function normalizeRuntimeLoggerConfig(
input: CreateConnectorLoggerInput,
): Required<RuntimeLoggerConfig> {
return {
enabled: input.runtimeConfig?.enabled ?? false,
level: input.runtimeConfig?.level ?? "info",
destination:
input.runtimeConfig?.destination ??
join(resolveClineDataDir(), "logs", "cline.log"),
name: input.runtimeConfig?.name ?? `cline.${input.runtime}`,
bindings: input.runtimeConfig?.bindings ?? {},
};
}
export function createConnectorLogger(
io: ConnectIo,
input: CreateConnectorLoggerInput,
): ConnectorLoggerAdapter {
const hosted = io.createLogger?.(input);
if (hosted) {
return hosted;
}
const fallback: ConnectorLoggerAdapter = {
core: noopBasicLogger as BasicLogger,
runtimeConfig: normalizeRuntimeLoggerConfig(input),
child: () => fallback,
};
return fallback;
}
@@ -1,11 +1,12 @@
import { resolveAndLoadAgentPlugins } from "@cline/core";
import {
type AgentExtensionCommand,
type AgentExtensionCommandResult,
type AgentTool,
type BasicLogger,
createContributionRegistry,
resolveAndLoadAgentPlugins,
} from "@cline/core";
import type { AgentTool, Message } from "@cline/shared";
type Message,
} from "@cline/shared";
import {
type ChatCommandDefinition,
type ChatCommandHost,
@@ -1,13 +1,10 @@
import { statSync } from "node:fs";
import { homedir } from "node:os";
import { basename, resolve } from "node:path";
import {
buildWorkspaceMetadata,
mergeRulesForSystemPrompt,
type UserInstructionConfigService,
} from "@cline/core";
import type { UserInstructionConfigService } from "@cline/core";
import { buildWorkspaceMetadata, mergeRulesForSystemPrompt } from "@cline/core";
import { type AgentMode, buildClineSystemPrompt } from "@cline/shared";
import { isImagePath, loadImageAsDataUrl } from "../utils/image-attachments";
import { isImagePath, loadImageAsDataUrl } from "./image-attachments";
const MODE_TAG_INSTRUCTIONS = `# Plan / Act Modes
@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from "vitest";
import type { CliLoggerAdapter } from "../logging/adapter";
import { createConnectorRuntimeTurnStream } from "./runtime-turn";
import type { ConnectorLoggerAdapter } from "./types";
type StreamHandlers = {
onEvent: (event: {
@@ -50,7 +50,7 @@ describe("createConnectorRuntimeTurnStream", () => {
sessionId: "session-1",
request,
clientId: "client-1",
logger: { core: {} } as unknown as CliLoggerAdapter,
logger: { core: {} } as unknown as ConnectorLoggerAdapter,
transport: "telegram",
conversationId: "thread-1",
onToolStatus: async (message) => {
@@ -82,7 +82,7 @@ describe("createConnectorRuntimeTurnStream", () => {
sessionId: "session-1",
request: { config: {} as never, prompt: "hi" },
clientId: "client-1",
logger: { core: { log } } as unknown as CliLoggerAdapter,
logger: { core: { log } } as unknown as ConnectorLoggerAdapter,
transport: "discord",
conversationId: "thread-1",
})) {
@@ -132,7 +132,7 @@ describe("createConnectorRuntimeTurnStream", () => {
sessionId: "session-1",
request: { config: {} as never, prompt: "hi" },
clientId: "client-1",
logger: { core: {} } as unknown as CliLoggerAdapter,
logger: { core: {} } as unknown as ConnectorLoggerAdapter,
transport: "telegram",
conversationId: "thread-1",
onFailed: async (error) => {
@@ -179,7 +179,7 @@ describe("createConnectorRuntimeTurnStream", () => {
sessionId: "session-1",
request: { config: {} as never, prompt: "hi" },
clientId: "client-1",
logger: { core: {} } as unknown as CliLoggerAdapter,
logger: { core: {} } as unknown as ConnectorLoggerAdapter,
transport: "telegram",
conversationId: "thread-1",
onFailed: async (error) => {
@@ -1,5 +1,6 @@
import type { ChatRunTurnRequest, HubSessionClient } from "@cline/core";
import type { CliLoggerAdapter } from "../logging/adapter";
import type { HubSessionClient } from "@cline/core/hub";
import type { ChatRunTurnRequest } from "@cline/shared";
import type { ConnectorLoggerAdapter } from "./types";
export type PendingConnectorApproval = {
approvalId: string;
@@ -136,7 +137,7 @@ export function createConnectorRuntimeTurnStream(input: {
sessionId: string;
request: ChatRunTurnRequest;
clientId: string;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
transport: string;
conversationId: string;
onToolStatus?: (message: string) => Promise<void>;
@@ -1,17 +1,15 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
const {
mockGetLastUsedProviderSettings,
mockGetProviderSettings,
mockResolveSystemPrompt,
mockGetProviderCollection,
mockGetBooleanFlagEnabled,
} = vi.hoisted(() => ({
mockGetLastUsedProviderSettings: vi.fn(),
mockGetProviderSettings: vi.fn(),
mockResolveSystemPrompt: vi.fn(),
mockGetProviderCollection: vi.fn(),
mockGetBooleanFlagEnabled: vi.fn(),
}));
vi.mock("@cline/core", async () => {
@@ -28,47 +26,29 @@ vi.mock("@cline/core", async () => {
return mockGetProviderSettings(providerId);
}
},
CoreSessionService: class {},
SqliteSessionStore: class {},
Llms: {
...actual.Llms,
getProviderCollection: mockGetProviderCollection,
},
};
});
vi.mock("../runtime/prompt", () => ({
vi.mock("@cline/llms", async () => {
const actual =
await vi.importActual<typeof import("@cline/llms")>("@cline/llms");
return {
...actual,
getProviderCollection: mockGetProviderCollection,
};
});
vi.mock("./prompt", () => ({
resolveSystemPrompt: mockResolveSystemPrompt,
}));
vi.mock("../utils/helpers", () => ({
vi.mock("./workspace", () => ({
resolveWorkspaceRoot: vi.fn((cwd: string) => cwd),
}));
vi.mock("../utils/feature-flags", () => ({
getCliFeatureFlagsService: () => ({
getBooleanFlagEnabled: mockGetBooleanFlagEnabled,
}),
}));
vi.mock("../commands/auth", async () => {
const actual =
await vi.importActual<typeof import("../commands/auth")>(
"../commands/auth",
);
return {
...actual,
ensureOAuthProviderApiKey: vi.fn(),
};
});
import { buildConnectorStartRequest } from "./session-runtime";
describe("buildConnectorStartRequest", () => {
beforeEach(() => {
mockGetBooleanFlagEnabled.mockReturnValue(false);
});
afterEach(() => {
vi.clearAllMocks();
delete process.env.OPENROUTER_API_KEY;
@@ -1,22 +1,18 @@
import type { ChatStartSessionRequest, RuntimeLoggerConfig } from "@cline/core";
import {
CoreSessionService,
HubSessionClient,
Llms,
ProviderSettingsManager,
SqliteSessionStore,
} from "@cline/core";
import type { Thread } from "chat";
import {
ensureOAuthProviderApiKey,
getPersistedProviderApiKey,
isOAuthProvider,
normalizeProviderId,
} from "../commands/auth";
import type { CliLoggerAdapter } from "../logging/adapter";
import { resolveSystemPrompt } from "../runtime/prompt";
import { resolveCliSessionMetadata } from "../utils/enterprise";
import { resolveWorkspaceRoot } from "../utils/helpers";
ProviderSettingsManager,
SqliteSessionStore,
} from "@cline/core";
import { HubSessionClient } from "@cline/core/hub";
import * as Llms from "@cline/llms";
import type {
ChatStartSessionRequest,
RuntimeLoggerConfig,
} from "@cline/shared";
import type { Thread } from "chat";
import {
parseLocalRowMetadata,
parseRowMetadata,
@@ -24,12 +20,14 @@ import {
readSessionReplyText,
} from "./common";
import { dispatchConnectorHook } from "./hooks";
import { resolveSystemPrompt } from "./prompt";
import {
type ConnectorThreadState,
loadThreadState,
persistMergedThreadState,
} from "./thread-bindings";
import type { ConnectIo } from "./types";
import type { ConnectIo, ConnectorLoggerAdapter } from "./types";
import { resolveWorkspaceRoot } from "./workspace";
async function resolveProviderApiKeyFromEnv(
provider: string,
@@ -83,12 +81,16 @@ export async function buildConnectorStartRequest(input: {
"";
if (!apiKey && isOAuthProvider(provider)) {
const oauthResult = await ensureOAuthProviderApiKey({
if (!input.io.ensureProviderApiKey) {
throw new Error(
`Connector host cannot authenticate OAuth provider "${provider}"`,
);
}
const oauthResult = await input.io.ensureProviderApiKey({
providerId: provider,
currentApiKey: apiKey,
existingSettings: selectedProviderSettings,
providerSettingsManager,
io: input.io,
});
selectedProviderSettings = oauthResult.selectedProviderSettings;
apiKey = oauthResult.apiKey ?? "";
@@ -143,7 +145,7 @@ export async function getOrCreateSessionId<
thread: Thread<TState>;
client: HubSessionClient;
startRequest: ChatStartSessionRequest;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
clientId: string;
transport: string;
bindingsPath: string;
@@ -151,6 +153,7 @@ export async function getOrCreateSessionId<
hookCommand?: string;
hookBotUserName?: string;
sessionMetadata: Record<string, unknown>;
resolveSessionMetadata?: ConnectIo["resolveSessionMetadata"];
reusedLogMessage: string;
startedLogMessage?: string;
}): Promise<string> {
@@ -219,9 +222,9 @@ export async function getOrCreateSessionId<
if (!sessionId) {
throw new Error("runtime start returned an empty session id");
}
const remoteConfigMetadata = await resolveCliSessionMetadata(sessionId).catch(
() => undefined,
);
const remoteConfigMetadata = await input
.resolveSessionMetadata?.(sessionId)
.catch(() => undefined);
await input.client
.updateSession({
@@ -1,6 +1,6 @@
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { resolveClineDataDir } from "@cline/core";
import { resolveClineDataDir } from "@cline/shared/storage";
function isProcessRunning(pid: number): boolean {
if (!Number.isInteger(pid) || pid <= 0) {
@@ -1,5 +1,5 @@
import { readFileSync, writeFileSync } from "node:fs";
import { ensureParentDir } from "@cline/core";
import { ensureParentDir } from "@cline/shared/storage";
import type { Lock, QueueEntry, StateAdapter } from "chat";
type PersistedStateSnapshot = {
@@ -1,13 +1,13 @@
import type { HubSessionClient } from "@cline/core";
import type { HubSessionClient } from "@cline/core/hub";
import type { TeamProgressProjectionEvent } from "@cline/shared";
import type { Chat, Thread } from "chat";
import type { CliLoggerAdapter } from "../logging/adapter";
import { truncateConnectorText } from "./runtime-turn";
import {
type ConnectorThreadBinding,
type ConnectorThreadState,
readBindings,
} from "./thread-bindings";
import type { ConnectorLoggerAdapter } from "./types";
function formatCountLabel(input: {
count: number;
@@ -139,7 +139,7 @@ export function startConnectorTaskUpdateRelay<
client: HubSessionClient;
clientId: string;
bot: Chat;
logger: CliLoggerAdapter;
logger: ConnectorLoggerAdapter;
bindingsPath: string;
transport: string;
postToThread?: (input: {
+45
View File
@@ -0,0 +1,45 @@
import type { ProviderSettings, ProviderSettingsManager } from "@cline/core";
import type { BasicLogger, RuntimeLoggerConfig } from "@cline/shared";
export type ConnectorLoggerAdapter = {
readonly core: BasicLogger;
readonly runtimeConfig: RuntimeLoggerConfig;
child(bindings: Record<string, unknown>): ConnectorLoggerAdapter;
};
export type CreateConnectorLoggerInput = {
runtime: "cli" | "rpc-runtime";
component?: string;
runtimeConfig?: RuntimeLoggerConfig;
};
export type ConnectIo = {
writeln: (text?: string) => void;
writeErr: (text: string) => void;
createLogger?: (input: CreateConnectorLoggerInput) => ConnectorLoggerAdapter;
resolveSessionMetadata?: (
sessionId: string,
) => Promise<Record<string, unknown> | undefined>;
ensureProviderApiKey?: (input: {
providerId: string;
currentApiKey?: string;
existingSettings?: ProviderSettings;
providerSettingsManager: ProviderSettingsManager;
}) => Promise<{
apiKey?: string;
selectedProviderSettings?: ProviderSettings;
}>;
};
export type ConnectStopResult = {
stoppedProcesses: number;
stoppedSessions: number;
};
export interface ConnectCommandDefinition {
name: string;
description: string;
run(args: string[], io: ConnectIo): Promise<number>;
showHelp(io: ConnectIo): void;
stopAll?(io: ConnectIo): Promise<ConnectStopResult>;
}
@@ -0,0 +1,14 @@
import { spawnSync } from "node:child_process";
export function resolveWorkspaceRoot(cwd: string): string {
const result = spawnSync("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
encoding: "utf8",
});
if (result.status === 0) {
const value = result.stdout.trim();
if (value) {
return value;
}
}
return cwd;
}
+4 -2
View File
@@ -2,9 +2,11 @@ import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
import { basename } from "node:path";
import process from "node:process";
import {
listActiveConnectors,
listConnectorCatalog,
} from "@cline/cline-hub/connectors";
import { withResolvedClineBuildEnv } from "@cline/shared";
import { listConnectorCatalog } from "../../../cli/src/connectors/catalog";
import { listActiveConnectors } from "../../../cli/src/connectors/status";
import {
PLATFORMS,
shouldIncludeField,
+1 -1
View File
@@ -1,4 +1,4 @@
import { listActiveConnectors } from "../../../cli/src/connectors/status";
import { listActiveConnectors } from "@cline/cline-hub/connectors";
import type { WebviewHubState } from "../webview-protocol";
import {
clientSummariesPayload,
+1
View File
@@ -20,6 +20,7 @@
"clean": "rm -rf webview/.next webview/out node_modules dist && (cd src-tauri && rm -rf target node_modules dist)"
},
"dependencies": {
"@cline/cline-hub": "workspace:*",
"@base-ui/react": "^1.2.0",
"@cline/core": "workspace:*",
"@cline/llms": "workspace:*",
@@ -2,9 +2,11 @@ import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
import { basename, join, normalize } from "node:path";
import process from "node:process";
import {
listActiveConnectors,
listConnectorCatalog,
} from "@cline/cline-hub/connectors";
import { withResolvedClineBuildEnv } from "@cline/shared";
import { listConnectorCatalog } from "../../../cli/src/connectors/catalog";
import { listActiveConnectors } from "../../../cli/src/connectors/status";
import {
PLATFORMS,
shouldIncludeField,
@@ -5,6 +5,9 @@
"@/*": ["./*"],
"@cline/agents": ["../../../sdk/packages/agents/src/index.ts"],
"@cline/core": ["../../../sdk/packages/core/src/index.ts"],
"@cline/cline-hub/connectors": [
"../../../apps/cline-hub/src/connectors/index.ts"
],
"@cline/core/hub": ["../../../sdk/packages/core/src/hub/index.ts"],
"@cline/llms": ["../../../sdk/packages/llms/src/index.ts"],
"@cline/shared": ["../../../sdk/packages/shared/src/index.ts"],
+3
View File
@@ -22,6 +22,9 @@
"@/*": ["./*"],
"@cline/agents": ["../../../sdk/packages/agents/src/index.ts"],
"@cline/core": ["../../../sdk/packages/core/src/index.ts"],
"@cline/cline-hub/connectors": [
"../../../apps/cline-hub/src/connectors/index.ts"
],
"@cline/core/hub": ["../../../sdk/packages/core/src/hub/index.ts"],
"@cline/llms": ["../../../sdk/packages/llms/src/index.ts"],
"@cline/shared": ["../../../sdk/packages/shared/src/index.ts"],
+12 -9
View File
@@ -19,25 +19,17 @@
},
"apps/cli": {
"name": "@cline/cli",
"version": "3.0.38",
"version": "3.0.39",
"bin": {
"cline": "src/index.ts",
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.16.1",
"@chat-adapter/discord": "^4.23.0",
"@chat-adapter/gchat": "^4.23.0",
"@chat-adapter/linear": "^4.23.0",
"@chat-adapter/slack": "^4.23.0",
"@chat-adapter/telegram": "^4.23.0",
"@chat-adapter/whatsapp": "^4.23.0",
"@clack/prompts": "^1.2.0",
"@cline/cline-hub": "workspace:*",
"@gramio/format": "^0.7.0",
"@opentui-ui/dialog": "^0.1.2",
"@opentui/core": "0.1.102",
"@opentui/react": "0.1.102",
"chat": "^4.23.0",
"commander": "^14.0.3",
"fzf": "^0.5.2",
"marked": "^15.0.12",
@@ -65,9 +57,19 @@
"name": "@cline/cline-hub",
"version": "0.0.0",
"dependencies": {
"@chat-adapter/discord": "^4.23.0",
"@chat-adapter/gchat": "^4.23.0",
"@chat-adapter/linear": "^4.23.0",
"@chat-adapter/slack": "^4.23.0",
"@chat-adapter/telegram": "^4.23.0",
"@chat-adapter/whatsapp": "^4.23.0",
"@cline/core": "workspace:*",
"@cline/llms": "workspace:*",
"@cline/shared": "workspace:*",
"@gramio/format": "^0.7.0",
"chat": "^4.23.0",
"commander": "^14.0.3",
"zod": "^4.3.6",
},
},
"apps/cline-hub/src/webview": {
@@ -164,6 +166,7 @@
"version": "0.0.1",
"dependencies": {
"@base-ui/react": "^1.2.0",
"@cline/cline-hub": "workspace:*",
"@cline/core": "workspace:*",
"@cline/llms": "workspace:*",
"@cline/shared": "workspace:*",
+6 -1
View File
@@ -21,7 +21,11 @@ Run SDK commands from `sdk/`, not from the legacy repository root. Do not run di
- `@cline/shared`: shared contracts, schemas, path helpers, hook engine, extension registry, low-level utilities
- `@cline/llms`: provider settings/config, model catalogs, provider manifests, gateway contracts, handler creation
- `@cline/agents`: stateless agent loop, tool orchestration, hook/extension runtime, event streaming
- `@cline/core`: stateful orchestration, session lifecycle, storage, config watching, plugin loading, default tools, telemetry. Exposes `@cline/core/hub` for discovery, the detached daemon entry, WebSocket clients, and session/UI client adapters, plus `@cline/core/hub/daemon-entry` for launching the shared daemon
- `@cline/core`: stateful orchestration, session lifecycle, storage, config watching, plugin loading, default tools, and telemetry. Exposes `@cline/core/hub` for discovery, the detached daemon entry, WebSocket clients, and session/UI client adapters, plus `@cline/core/hub/daemon-entry` for launching the shared daemon
### Hub App Package
- `@cline/cline-hub`: Hub dashboard/server behavior and chat connector runtimes exposed through `@cline/cline-hub/connectors`
### Dependency Direction
@@ -47,6 +51,7 @@ Route changes to the package that owns the concern:
- session lifecycle, storage, config watching, default tools, plugin loading, telemetry, hub runtime services, hub discovery, hub daemon spawn, and session-oriented client helpers (`HubSessionClient`, `HubUIClient`, `connectToHub`): `@cline/core` (hub pieces live under `src/hub/`)
- remote-config schemas, managed instruction materialization, blob upload metadata, and OpenTelemetry config normalization: `@cline/shared/src/remote-config`
- host-specific UX or shell behavior: app package
- chat connector runtimes, adapters, command handling, and process state: `@cline/cline-hub/connectors`
## Verifying Changes
+13
View File
@@ -114,6 +114,19 @@ Design rules:
- `server/` contains WebSocket server startup, native/browser socket adapters, server transport, server helpers, and `handlers/` for hub command dispatch
- settings mutations belong in core services and hub commands, not in host-specific file writes. Hosts should call the core settings facade or the `settings.*` hub command family and react to `settings.changed`.
### `@cline/cline-hub`
Owns Hub application behavior:
- chat connector runtimes and transport adapters
- connector command handling, thread bindings, and process state
- connector discovery and management surfaces for Hub hosts
- the `@cline/cline-hub/connectors` entrypoint
Design rule:
- connector behavior belongs in the Hub package; CLI and other hosts provide only host-owned services such as logging, interactive provider authentication, and app-specific session metadata through `ConnectIo`.
## Runtime Flows
### Local In-Process Runtime
+1 -1
View File
@@ -238,7 +238,7 @@ cline connect telegram -k "$TELEGRAM_BOT_TOKEN"
# Then send /help or /start to the bot in Telegram
```
For Telegram-specific connector behavior, see [`apps/cli/src/connectors/adapters/telegram.md`](./apps/cli/src/connectors/adapters/telegram.md).
For Telegram-specific connector behavior, see [`apps/cline-hub/src/connectors/adapters/telegram.md`](../apps/cline-hub/src/connectors/adapters/telegram.md).
## Providers