@gguf/claw
Version:
Multi-channel AI gateway with extensible messaging integrations
892 lines (886 loc) • 773 kB
JavaScript
import { _ as expandHomePrefix, g as resolveStateDir, i as isNixMode, l as resolveGatewayLockDir, o as resolveConfigPath, r as STATE_DIR, t as CONFIG_PATH, u as resolveGatewayPort } from "./paths-B4BZAPZh.js";
import { B as theme, D as isPlainObject, E as truncateUtf16Safe, H as getLogger, P as setVerbose, R as colorize, S as shortenHomePath, U as getResolvedLoggerSettings, V as getChildLogger, c as ensureDir, r as clamp, t as CONFIG_DIR, y as resolveUserPath, z as isRich } from "./utils-CP9YLh6M.js";
import { a as normalizeElevatedLevel, c as normalizeUsageDisplay, d as supportsXHighThinking, l as normalizeVerboseLevel, n as formatXHighModelHint, o as normalizeReasoningLevel, s as normalizeThinkLevel, t as formatThinkingLevels } from "./thinking-EAliFiVK.js";
import { $ as normalizeOptionalAgentId, $n as CommandLane, $t as isExternalHookSession, A as resolveOutboundSessionRoute, An as getAgentRunContext, At as sleepWithAbort, B as setGatewaySigusr1RestartPolicy, Bn as resolveTtsPrefsPath, Bt as normalizeSendPolicy, C as loadCombinedSessionStoreForGateway, Cn as countActiveDescendantRuns, Cr as normalizeMimeList, D as resolveSessionModelRef, Dn as runSubagentAnnounceFlow, Dt as readJsonBodyWithLimit, E as resolveGatewaySessionStoreTarget, Er as resolveAgentTimeoutMs, Et as handleSlackHttpRequest, F as deferGatewayRestartUntilIdle, Fn as isTtsEnabled, Gn as OPENAI_TTS_MODELS, Gt as getCliSessionId, H as consumeRestartSentinel, Hn as setTtsEnabled, Ht as createDefaultDeps, I as emitGatewayRestart, In as isTtsProviderConfigured, J as writeRestartSentinel, K as summarizeRestartSentinel, Kn as OPENAI_TTS_VOICES, Kt as setCliSessionId, L as isGatewaySigusr1RestartExternallyAllowed, Ln as resolveTtsApiKey, Lt as resolveOutboundTarget, M as resetDirectoryCache, Mn as registerAgentRunContext, Nn as resolveUserTimezone, Nt as resolveAgentDeliveryPlan, On as clearAgentRunContext, Ot as requestBodyErrorToText, P as consumeGatewaySigusr1RestartAuthorization, Pn as getTtsProvider, Pt as resolveAgentOutboundTarget, Q as inferLegacyName, Qn as waitForActiveTasks, Qt as getHookType, R as markGatewaySigusr1RestartHandled, Rn as resolveTtsAutoMode, Rt as resolveSessionDeliveryTarget, S as listSessionsFromStore, Sr as extractImageContentFromSource, T as pruneLegacyStoreKeys, Tn as listDescendantRunsForRequester, U as formatDoctorNonInteractiveHint, Un as setTtsProvider, Ut as createOutboundSendDeps$1, V as setPreRestartDeferralCheck, Vn as resolveTtsProviderOrder, Vt as resolveSendPolicy, W as formatRestartSentinelMessage, Wn as textToSpeech, Wt as runWithModelFallback, X as normalizeCronJobCreate, Xn as resetAllLanes, Xt as buildSafeExternalPrompt, Y as normalizeHttpWebhookUrl, Yn as getTotalQueueSize, Z as normalizeCronJobPatch, Zn as setCommandLaneConcurrency, Zt as detectSuspiciousPatterns, _ as readLatestAssistantReply, _r as DEFAULT_INPUT_IMAGE_MAX_BYTES, _t as loadOpenClawPlugins, ar as lookupContextTokens, at as buildDeliveryFromLegacyPayload, bn as stopSubagentsForRequester, br as DEFAULT_INPUT_TIMEOUT_MS, c as clearSessionQueues, cn as dispatchInboundMessage, d as getActiveEmbeddedRunCount, dr as applyVerboseOverride, dt as persistBrowserProxyFiles, er as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, et as normalizeOptionalSessionKey, f as waitForEmbeddedPiRunEnd, fr as parseVerboseOverride, ft as requestHeartbeatNow, g as createOpenClawTools, gn as formatZonedTimestamp, ht as getPluginToolMeta, i as resolveCronStyleNow, ir as stripHeartbeatToken, it as migrateLegacyCronPayload, jn as onAgentEvent, jt as agentCommand, k as ensureOutboundSessionEntry, kn as emitAgentEvent, kt as computeBackoff, l as runEmbeddedPiAgent, ln as createReplyDispatcher, m as buildDefaultToolPolicyPipelineSteps, mr as isSystemEventContextChanged, n as BARE_SESSION_RESET_PROMPT, nt as normalizePayloadToSystemText, ot as hasLegacyDeliveryHints, p as applyToolPolicyPipeline, pr as enqueueSystemEvent, qn as getActiveTaskCount, qt as runCliAgent, rn as buildHistoryContextFromEntries, rt as normalizeRequiredName, st as stripLegacyDeliveryFields, tt as normalizeOptionalText, u as abortEmbeddedPiRun, un as getTotalPendingReplies, ur as applyModelOverrideToSessionEntry, ut as applyBrowserProxyPaths, v as resolveAnnounceTargetFromKey, vr as DEFAULT_INPUT_IMAGE_MIMES, w as loadSessionEntry, wn as initSubagentRegistry, wr as resolveInputFileLimits, x as listAgentsForGateway, xr as extractFileContentFromSource, xt as loadProviderUsageSummary, y as canonicalizeSpawnedByForAgent, yn as isAbortTrigger, yr as DEFAULT_INPUT_MAX_REDIRECTS, yt as normalizeGroupActivation, z as scheduleGatewaySigusr1Restart, zn as resolveTtsConfig, zt as createOutboundSendDeps } from "./reply-BylFHBd4.js";
import { b as isSubagentSessionKey, d as resolveAgentIdFromSessionKey, h as toAgentStoreSessionKey, i as buildAgentMainSessionKey, l as normalizeAgentId, m as toAgentRequestSessionKey, n as DEFAULT_AGENT_ID, s as classifySessionKeyShape, t as DEFAULT_ACCOUNT_ID, u as normalizeMainKey, v as isCronRunSessionKey, x as parseAgentSessionKey } from "./session-key-CZ6OwgSB.js";
import { r as matchesSkillFilter, t as resolveOpenClawPackageRoot } from "./openclaw-root-BM-yQZH6.js";
import { C as createInternalHookEvent, S as clearInternalHooks, T as triggerInternalHook, d as getActivePluginRegistry, m as createEmptyPluginRegistry, r as DEFAULT_CHAT_CHANNEL, t as CHANNEL_IDS, w as registerInternalHook } from "./registry-B-j4DRfe.js";
import { a as setConsoleSubsystemFilter, f as defaultRuntime, n as runtimeForLogger, o as setConsoleTimestampPrefix, t as createSubsystemLogger } from "./subsystem-BCQGGxdd.js";
import { i as logDebug, n as runExec, s as logWarn, t as runCommandWithTimeout } from "./exec-DYqRzFbo.js";
import { C as ensureAgentWorkspace, D as resolveDefaultAgentWorkspaceDir, O as resolveWorkspaceTemplateDir, S as DEFAULT_USER_FILENAME, T as isWorkspaceOnboardingCompleted, _ as DEFAULT_IDENTITY_FILENAME, a as resolveAgentModelFallbacksOverride, b as DEFAULT_SOUL_FILENAME, c as resolveAgentWorkspaceDir, d as resolveSessionAgentId, g as DEFAULT_HEARTBEAT_FILENAME, h as DEFAULT_BOOTSTRAP_FILENAME, i as resolveAgentDir, l as resolveDefaultAgentId, n as listAgentIds, p as DEFAULT_AGENTS_FILENAME, r as resolveAgentConfig, s as resolveAgentSkillsFilter, t as listAgentEntries, v as DEFAULT_MEMORY_ALT_FILENAME, x as DEFAULT_TOOLS_FILENAME, y as DEFAULT_MEMORY_FILENAME } from "./agent-scope-BnZW9Gh2.js";
import { Gt as DEFAULT_PROVIDER, Lt as isPidAlive, Ut as DEFAULT_CONTEXT_TOKENS, Wt as DEFAULT_MODEL, _ as resolveSubagentConfiguredModelSelection, a as getModelRefStatus, d as resolveAllowedModelRef, h as resolveHooksGmailModel, m as resolveDefaultModelForAgent, o as isCliProvider, p as resolveConfiguredModelRef, y as resolveThinkingDefault, zt as normalizeSecretInput } from "./model-selection-CqaTAlhy.js";
import "./github-copilot-token-D2zp6kMZ.js";
import { t as formatCliCommand } from "./command-format-DEKzLnLg.js";
import "./boolean-BsqeuxE6.js";
import { n as logAcceptedEnvOption, t as isTruthyEnvValue } from "./env-VriqyjXT.js";
import { _ as normalizeGatewayClientMode, f as GATEWAY_CLIENT_CAPS, g as hasGatewayClientCap, h as GATEWAY_CLIENT_NAMES, i as isGatewayMessageChannel, l as normalizeMessageChannel, m as GATEWAY_CLIENT_MODES, n as isDeliverableMessageChannel, p as GATEWAY_CLIENT_IDS, r as isGatewayCliClient, s as isWebchatClient, t as INTERNAL_MESSAGE_CHANNEL } from "./message-channel-Bena1Tzd.js";
import "./send-CsyONLnQ.js";
import { B as resolveSubagentMaxConcurrent, C as parseDurationMs, H as resolveRuntimeServiceVersion, N as applyMergePatch, P as applyLegacyMigrations, S as sensitive, V as VERSION, a as parseConfigJson5, c as resolveConfigSnapshotHash, d as OpenClawSchema, i as loadConfig, l as writeConfigFile, n as migrateLegacyConfig, o as readConfigFileSnapshot, r as createConfigIO, s as readConfigFileSnapshotForWrite, u as validateConfigObjectWithPlugins, z as resolveAgentMaxConcurrent } from "./config-PQiujvsf.js";
import { a as isPathInsideWithRealpath } from "./legacy-names-DyDKpCnk.js";
import { l as isTestDefaultMemorySlotDisabled } from "./manifest-registry-4k4vkhPS.js";
import { d as registerUnhandledRejectionHandler } from "./runner-U04aiHHC.js";
import "./image-DxEpBZim.js";
import "./models-config-B459BnCS.js";
import "./pi-model-discovery-4uUnLc3n.js";
import "./pi-embedded-helpers-BdscQhyS.js";
import { g as collectExplicitAllowlist, x as resolveToolProfilePolicy, y as mergeAlsoAllowPolicy } from "./sandbox-UAzvS0V6.js";
import { c as detectMime } from "./image-ops-CI1VknD1.js";
import { t as ToolInputError } from "./common-a25M2Kvi.js";
import "./chrome-Dd5zBIFu.js";
import { i as enableTailscaleServe, n as disableTailscaleServe, o as getTailnetHostname, r as enableTailscaleFunnel, t as disableTailscaleFunnel } from "./tailscale-C3JsBEiY.js";
import { t as safeEqualSecret } from "./secret-equal-CJzes24Z.js";
import { n as pickPrimaryTailnetIPv4, r as pickPrimaryTailnetIPv6 } from "./tailnet-FrpQq0gp.js";
import { a as isTrustedProxyAddress, d as resolveGatewayClientIp, f as resolveGatewayListenHosts, l as pickPrimaryLanIPv4, n as isLoopbackAddress, o as isValidIPv4, p as resolveHostName, r as isLoopbackHost, s as normalizeHostHeader, t as rawDataToString, u as resolveGatewayBindHost } from "./ws-CV6gEox3.js";
import { a as AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN, i as resolveGatewayAuth, n as authorizeGatewayConnect, o as AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET, r as isLocalDirectRequest, s as createAuthRateLimiter, t as assertGatewayAuthConfigured } from "./auth-DZSWPd8D.js";
import { g as mergeGatewayTailscaleConfig, h as ensureGatewayStartupAuth } from "./server-context-DAWsUNUs.js";
import { d as hasBinary } from "./frontmatter-6HZ0_y2V.js";
import { i as loadWorkspaceSkillEntries, r as buildWorkspaceSkillSnapshot } from "./skills-CLmzWt48.js";
import "./routes-DIQa_0pt.js";
import "./redact-B7Mjvk0c.js";
import { a as isErrno, n as formatErrorMessage } from "./errors-kfGqPQ4b.js";
import { n as openFileWithinRoot, t as SafeOpenError } from "./fs-safe-_OpKPuZA.js";
import "./paths-D1WZUYry.js";
import { t as SsrFBlockedError } from "./ssrf-6f5m2MMA.js";
import "./store-DSGYY-H9.js";
import { a as inspectPortUsage, r as ensurePortAvailable, s as formatPortDiagnostics } from "./ports-DwDxX6cS.js";
import { t as movePathToTrash } from "./trash-DLzIf__E.js";
import { B as resolveExplicitAgentSessionKey, C as readSessionMessages, D as stripEnvelopeFromMessages, E as resolveSessionTranscriptCandidates, H as resolveMainSessionKeyFromConfig, J as cleanStaleLockFiles, N as resolveSessionResetPolicy, R as canonicalizeMainSessionAlias, S as capArrayByJsonBytes, V as resolveMainSessionKey, W as snapshotSessionOrigin, a as loadSessionStore, b as archiveFileOnDisk, g as mergeDeliveryContext, j as evaluateSessionFreshness, m as deliveryContextFromSession, n as parseSessionThreadInfo, t as extractDeliveryInfo, u as updateSessionStore, v as normalizeSessionDeliveryFields, w as readSessionPreviewItemsFromTranscript, x as archiveSessionTranscripts, z as resolveAgentMainSessionKey } from "./sessions-Z1BZU3xh.js";
import "./dock-7F-MiPtF.js";
import { n as normalizeWhatsAppTarget } from "./normalize-Ci8UyR3Z.js";
import { n as resolveWhatsAppAccount } from "./accounts-Bj1dJ-fd.js";
import "./accounts-DOl1Wkxo.js";
import "./accounts-t6R5_PzC.js";
import { t as buildChannelAccountBindings } from "./bindings-Dja1z-h8.js";
import "./logging-xYH6GmRT.js";
import "./send-uu2Jc-Js.js";
import { n as listChannelPlugins, r as normalizeChannelId, t as getChannelPlugin } from "./plugins-CKbXkuXd.js";
import { c as resolveStorePath, i as resolveSessionTranscriptPath, n as resolveSessionFilePath, r as resolveSessionFilePathOptions, s as resolveSessionTranscriptsDirForAgent } from "./paths-C2NfoGZE.js";
import { i as normalizeInputProvenance } from "./input-provenance-DkoU-lPU.js";
import "./tool-display-Bx4M6uNT.js";
import { t as fetchWithSsrFGuard } from "./fetch-guard-C1sYsWgl.js";
import "./api-key-rotation-CpGefdGL.js";
import "./local-roots-DiDvXIIo.js";
import "./sqlite-JcMMx8Z5.js";
import { n as loadModelCatalog } from "./model-catalog-CIixTnGt.js";
import { n as SILENT_REPLY_TOKEN, r as isSilentReplyText } from "./tokens-D4lZk7-h.js";
import { n as createBrowserRouteDispatcher, r as getMachineDisplayName } from "./with-timeout-DijiQjw6.js";
import { C as getGlobalHookRunner, T as runGlobalGatewayStopSafely, o as normalizeReplyPayloadsForDelivery, t as deliverOutboundPayloads } from "./deliver-DgvS3uCz.js";
import { d as startDiagnosticHeartbeat, f as stopDiagnosticHeartbeat, m as isDiagnosticsEnabled } from "./diagnostic-UUwku4RV.js";
import "./diagnostic-session-state-ByqoIyGn.js";
import "./send-DbwyyQAv.js";
import "./model-VbWjwqaW.js";
import { a as resolveAgentIdentity, t as createReplyPrefixOptions } from "./reply-prefix-DAK7-zK4.js";
import { i as resolveMemoryBackendConfig, r as getMemorySearchManager } from "./memory-cli-DxmtLhh_.js";
import { n as resolveMemorySearchConfig } from "./manager-C0teWocQ.js";
import "./retry-rUEdE6zT.js";
import "./chunk-CMylpCPi.js";
import "./markdown-tables-B9VGUdDc.js";
import "./ir-24qCLTKH.js";
import "./render-CXDO_kgw.js";
import "./commands-registry-bb_U-3FM.js";
import { $ as validateNodePairRejectParams, A as validateCronStatusParams, At as ErrorCodes, B as validateExecApprovalsGetParams, Bt as removePairedDevice, C as validateConfigSetParams, Ct as validateWebLoginStartParams, D as validateCronRemoveParams, Dt as validateWizardStartParams, E as validateCronListParams, Et as validateWizardNextParams, F as validateDevicePairRemoveParams, Ft as approveDevicePairing, G as validateModelsListParams, Gt as updatePairedDeviceMetadata, H as validateExecApprovalsNodeSetParams, Ht as revokeDeviceToken, I as validateDeviceTokenRevokeParams, It as ensureDeviceToken, J as validateNodeInvokeParams, K as validateNodeDescribeParams, Kt as verifyDeviceToken, L as validateDeviceTokenRotateParams, Lt as getPairedDevice, M as validateDevicePairApproveParams, N as validateDevicePairListParams, Nt as parseSessionLabel, O as validateCronRunParams, Ot as validateWizardStatusParams, P as validateDevicePairRejectParams, Pt as buildDeviceAuthPayload, Q as validateNodePairListParams, Qt as verifyDeviceSignature, R as validateExecApprovalRequestParams, Rt as listDevicePairing, S as validateConfigSchemaParams, St as validateWakeParams, T as validateCronAddParams, Tt as validateWizardCancelParams, U as validateExecApprovalsSetParams, Ut as rotateDeviceToken, V as validateExecApprovalsNodeGetParams, Vt as requestDevicePairing, W as validateLogsTailParams, Wt as summarizeDeviceTokens, X as validateNodeListParams, Y as validateNodeInvokeResultParams, Yt as deriveDeviceIdFromPublicKey, Z as validateNodePairApproveParams, Zt as normalizeDevicePublicKeyBase64Url, _ as validateChatInjectParams, _t as validateSkillsStatusParams, a as validateAgentWaitParams, at as validateRequestFrame, b as validateConfigGetParams, bt as validateTalkModeParams, c as validateAgentsFilesGetParams, ct as validateSessionsDeleteParams, d as validateAgentsListParams, dt as validateSessionsPreviewParams, et as validateNodePairRequestParams, f as validateAgentsUpdateParams, ft as validateSessionsResetParams, g as validateChatHistoryParams, gt as validateSkillsInstallParams, h as validateChatAbortParams, ht as validateSkillsBinsParams, i as validateAgentParams, it as validatePushTestParams, j as validateCronUpdateParams, jt as errorShape, k as validateCronRunsParams, kt as PROTOCOL_VERSION, l as validateAgentsFilesListParams, lt as validateSessionsListParams, m as validateChannelsStatusParams, mt as validateSessionsUsageParams, n as formatValidationErrors, nt as validateNodeRenameParams, o as validateAgentsCreateParams, ot as validateSendParams, p as validateChannelsLogoutParams, pt as validateSessionsResolveParams, q as validateNodeEventParams, qt as roleScopesAllow, r as validateAgentIdentityParams, rt as validatePollParams, s as validateAgentsDeleteParams, st as validateSessionsCompactParams, tt as validateNodePairVerifyParams, u as validateAgentsFilesSetParams, ut as validateSessionsPatchParams, v as validateChatSendParams, vt as validateSkillsUpdateParams, w as validateConnectParams, wt as validateWebLoginWaitParams, x as validateConfigPatchParams, xt as validateUpdateRunParams, y as validateConfigApplyParams, yt as validateTalkConfigParams, z as validateExecApprovalResolveParams, zt as rejectDevicePairing } from "./client-BdSkEtCd.js";
import { f as loadGatewayTlsRuntime$1, l as authorizeOperatorScopesForMethod, n as callGateway, s as ADMIN_SCOPE$3, u as isNodeRoleMethod } from "./call-BCz_8mqq.js";
import { c as writeJsonAtomic, o as createAsyncLock, s as readJsonFile } from "./pairing-token-B-_eiWlR.js";
import { n as normalizePollInput } from "./polls-CdvQ578Y.js";
import { t as getChannelActivity } from "./channel-activity-DynIQUB_.js";
import "./fetch-Bu5zBCce.js";
import "./tables-C70h_80U.js";
import "./send-CAfFUUkm.js";
import { a as readChannelAllowFromStoreSync } from "./pairing-store-T5DpOfoL.js";
import "./proxy-BiOWrea2.js";
import { t as formatDocsLink } from "./links-BVCMOGeE.js";
import { n as runCommandWithRuntime } from "./cli-utils-CX1oQ81G.js";
import { t as formatHelpExamples } from "./help-format-dbk2xW0E.js";
import { n as withProgress } from "./progress-CaVMHLaE.js";
import "./resolve-route-2TU_aaLs.js";
import "./replies-NhrZJ3yf.js";
import { S as registerSkillsChangeListener, _ as requestNodePairing, c as recordRemoteNodeInfo, d as removeRemoteNodeInfo, f as setSkillsRemoteRegistry, g as renamePairedNode, h as rejectNodePairing, l as refreshRemoteBinsForConnectedNodes, m as listNodePairing, o as getRemoteSkillEligibility, p as approveNodePairing, s as primeRemoteSkillsCache, u as refreshRemoteNodeBins, v as updatePairedNodeMetadata, x as getSkillsSnapshotVersion, y as verifyNodeToken } from "./skill-commands-N8Sho46-.js";
import { t as listAgentWorkspaceDirs } from "./workspace-dirs-CE3CxabZ.js";
import { a as resolveSubagentToolPolicy, c as isRestartEnabled, i as resolveGroupToolPolicy, r as resolveEffectiveToolPolicy } from "./pi-tools.policy-DTy1DnPK.js";
import "./outbound-attachment-CntoLTI_.js";
import "./delivery-queue-CxDDEWQg.js";
import { c as hasNonzeroUsage, i as loadSessionUsageTimeSeries, n as loadCostUsageSummary, r as loadSessionCostSummary, s as deriveSessionTotalTokens, t as discoverAllSessions } from "./session-cost-usage-BlUDaylg.js";
import "./send-4LoRaltQ.js";
import { C as normalizeControlUiBasePath, S as buildControlUiAvatarUrl, c as handleReset, w as resolveAssistantAvatarUrl, x as CONTROL_UI_AVATAR_PREFIX } from "./onboard-helpers-DbjfBSMX.js";
import "./prompt-style-VqCNjBi8.js";
import "./pairing-labels-DaESq9ML.js";
import { n as formatTokenCount, r as formatUsd } from "./usage-format-BzKEDn0q.js";
import { a as mergeExecApprovalsSocketDefaults, c as readExecApprovalsSnapshot, p as saveExecApprovals, r as ensureExecApprovals, s as normalizeExecApprovals, t as DEFAULT_EXEC_APPROVAL_TIMEOUT_MS } from "./exec-approvals-q0C8VDMT.js";
import "./nodes-screen-5fMfTT2n.js";
import { n as createBrowserControlContext, r as startBrowserControlServiceFromConfig } from "./control-service-BoO2TGOx.js";
import { i as parseAbsoluteTimeMs, n as resolveCronStaggerMs, r as resolveDefaultCronStaggerMs, t as normalizeCronStaggerMs } from "./stagger-B6VQyn1F.js";
import { n as resolveMessageChannelSelection } from "./channel-selection-DuDSoVSC.js";
import { i as onHeartbeatEvent, r as getLastHeartbeatEvent, t as resolveHeartbeatVisibility } from "./heartbeat-visibility-Ckqy-niT.js";
import { t as ensureOpenClawCliOnPath } from "./path-env-BufaTTaN.js";
import "./runtime-guard-DlT3NecY.js";
import { t as forceFreePortAndWait } from "./ports-CJdLonW8.js";
import { t as buildChannelUiCatalog } from "./catalog-CgtgPHMj.js";
import { t as buildWorkspaceSkillStatus } from "./skills-status-kQ1GipOi.js";
import { n as DEFAULT_GATEWAY_HTTP_TOOL_DENY } from "./dangerous-tools-DyngmY3-.js";
import { n as inheritOptionFromParent } from "./command-options-ByE5SNIE.js";
import { t as WizardCancelledError } from "./prompts-xO_GDgJd.js";
import { t as resolveChannelDefaultAccountId } from "./helpers-tGq4L3A8.js";
import { t as applyPluginAutoEnable } from "./plugin-auto-enable-DJOFx9vI.js";
import "./install-safe-path-DSS8Pu5f.js";
import "./skill-scanner-D56xvwQF.js";
import { t as buildChannelAccountSnapshot } from "./status-B7XwQ8iW.js";
import "./channels-status-issues-ClkP4ZUC.js";
import "./daemon-runtime-CZuP7tS4.js";
import "./systemd-B6S4ND7H.js";
import "./service-Bw9h81em.js";
import "./lifecycle-core-BFTIU22P.js";
import "./systemd-hints-B9MFGq86.js";
import { t as parsePort$1 } from "./parse-port-QkaTJ4ru.js";
import { n as addGatewayServiceCommands } from "./daemon-cli-CodVtEmg.js";
import "./diagnostics-CArN76wS.js";
import "./table-D-EOwMue.js";
import { n as resolveWideAreaDiscoveryDomain, r as writeWideAreaGatewayZone } from "./widearea-dns-C8theOFh.js";
import { i as toOptionString, n as extractGatewayMiskeys, r as maybeExplainGatewayServiceStop, t as describeUnknownError } from "./shared-BYHlyn-f.js";
import { c as probeGateway, o as isNodeCommandAllowed, s as resolveNodeCommandAllowlist } from "./audit-BjSHYL1a.js";
import { t as discoverGatewayBeacons } from "./bonjour-discovery-D_DQdYOE.js";
import { n as getStatusSummary, r as pickGatewaySelfPresence } from "./status-DuhTF2p3.js";
import { c as startHeartbeatRunner, i as styleHealthChannelLine, n as getHealthSnapshot, o as runHeartbeatOnce, s as setHeartbeatsEnabled, t as formatHealthChannelLines } from "./health-Bt3xmMjK.js";
import { a as resolveControlUiRootSync, i as resolveControlUiRootOverrideSync, t as ensureControlUiAssetsBuilt } from "./control-ui-assets-BmaUgzFj.js";
import { a as resolveNpmChannelTag, c as DEFAULT_PACKAGE_CHANNEL, m as normalizeUpdateChannel, n as compareSemverStrings, t as checkUpdateStatus } from "./update-check-CHLDbXI4.js";
import { t as runOnboardingWizard } from "./onboarding-DeU1kIgp.js";
import { a as sendApnsAlert, c as parseMessageWithAttachments, d as shouldLogWs, f as summarizeAgentEventForWsLog, i as resolveApnsAuthConfigFromEnv, l as formatForLog, n as normalizeApnsEnvironment, o as sendApnsBackgroundWake, p as setGatewayWsLogStyle, s as normalizeRpcAttachmentsToChatAttachments, t as loadApnsRegistration, u as logWs } from "./push-apns-DAgh5PMd.js";
import { T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-BrHEum7O.js";
import { a as pruneAgentConfig, i as loadAgentIdentity, r as findAgentEntryIndex, s as loadAgentIdentityFromWorkspace, t as applyAgentConfig } from "./agents.config--k-cL9FH.js";
import "./dm-policy-shared-XAIBK3tz.js";
import "./node-service-x4yUQJuC.js";
import "./status.update-BdrAB0yT.js";
import { n as validateSystemRunCommandConsistency, t as formatExecCommand } from "./system-run-command-DIPtJc6g.js";
import { t as installSkill } from "./skills-install-BQCVzl6P.js";
import { n as runGatewayUpdate, t as resolveAgentSessionDirs } from "./session-dirs-BExMNJFf.js";
import { i as shouldIncludeHook, r as resolveHookConfig, t as loadWorkspaceHookEntries } from "./workspace-DekXc039.js";
import { fileURLToPath, pathToFileURL } from "node:url";
import * as fsSync from "node:fs";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import JSON5 from "json5";
import chalk from "chalk";
import fs$1 from "node:fs/promises";
import { spawn, spawnSync } from "node:child_process";
import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
import { z } from "zod";
import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent";
import net from "node:net";
import { createServer } from "node:http";
import { createServer as createServer$1 } from "node:https";
import { WebSocketServer } from "ws";
import { Buffer as Buffer$1 } from "node:buffer";
import chokidar from "chokidar";
import { Cron } from "croner";
//#region src/infra/ssh-config.ts
function parsePort(value) {
if (!value) return;
const parsed = Number.parseInt(value, 10);
if (!Number.isFinite(parsed) || parsed <= 0) return;
return parsed;
}
function parseSshConfigOutput(output) {
const result = { identityFiles: [] };
const lines = output.split("\n");
for (const raw of lines) {
const line = raw.trim();
if (!line) continue;
const [key, ...rest] = line.split(/\s+/);
const value = rest.join(" ").trim();
if (!key || !value) continue;
switch (key) {
case "user":
result.user = value;
break;
case "hostname":
result.host = value;
break;
case "port":
result.port = parsePort(value);
break;
case "identityfile":
if (value !== "none") result.identityFiles.push(value);
break;
default: break;
}
}
return result;
}
async function resolveSshConfig(target, opts = {}) {
const sshPath = "/usr/bin/ssh";
const args = ["-G"];
if (target.port > 0 && target.port !== 22) args.push("-p", String(target.port));
if (opts.identity?.trim()) args.push("-i", opts.identity.trim());
const userHost = target.user ? `${target.user}@${target.host}` : target.host;
args.push("--", userHost);
return await new Promise((resolve) => {
const child = spawn(sshPath, args, { stdio: [
"ignore",
"pipe",
"ignore"
] });
let stdout = "";
child.stdout?.setEncoding("utf8");
child.stdout?.on("data", (chunk) => {
stdout += String(chunk);
});
const timeoutMs = Math.max(200, opts.timeoutMs ?? 800);
const timer = setTimeout(() => {
try {
child.kill("SIGKILL");
} finally {
resolve(null);
}
}, timeoutMs);
child.once("error", () => {
clearTimeout(timer);
resolve(null);
});
child.once("exit", (code) => {
clearTimeout(timer);
if (code !== 0 || !stdout.trim()) {
resolve(null);
return;
}
resolve(parseSshConfigOutput(stdout));
});
});
}
//#endregion
//#region src/infra/ssh-tunnel.ts
function parseSshTarget(raw) {
const trimmed = raw.trim().replace(/^ssh\s+/, "");
if (!trimmed) return null;
const [userPart, hostPart] = trimmed.includes("@") ? (() => {
const idx = trimmed.indexOf("@");
const user = trimmed.slice(0, idx).trim();
const host = trimmed.slice(idx + 1).trim();
return [user || void 0, host];
})() : [void 0, trimmed];
const colonIdx = hostPart.lastIndexOf(":");
if (colonIdx > 0 && colonIdx < hostPart.length - 1) {
const host = hostPart.slice(0, colonIdx).trim();
const portRaw = hostPart.slice(colonIdx + 1).trim();
const port = Number.parseInt(portRaw, 10);
if (!host || !Number.isFinite(port) || port <= 0) return null;
if (host.startsWith("-")) return null;
return {
user: userPart,
host,
port
};
}
if (!hostPart) return null;
if (hostPart.startsWith("-")) return null;
return {
user: userPart,
host: hostPart,
port: 22
};
}
async function pickEphemeralPort() {
return await new Promise((resolve, reject) => {
const server = net.createServer();
server.once("error", reject);
server.listen(0, "127.0.0.1", () => {
const addr = server.address();
server.close(() => {
if (!addr || typeof addr === "string") {
reject(/* @__PURE__ */ new Error("failed to allocate a local port"));
return;
}
resolve(addr.port);
});
});
});
}
async function canConnectLocal(port) {
return await new Promise((resolve) => {
const socket = net.connect({
host: "127.0.0.1",
port
});
const done = (ok) => {
socket.removeAllListeners();
socket.destroy();
resolve(ok);
};
socket.once("connect", () => done(true));
socket.once("error", () => done(false));
socket.setTimeout(250, () => done(false));
});
}
async function waitForLocalListener(port, timeoutMs) {
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
if (await canConnectLocal(port)) return;
await new Promise((r) => setTimeout(r, 50));
}
throw new Error(`ssh tunnel did not start listening on localhost:${port}`);
}
async function startSshPortForward(opts) {
const parsed = parseSshTarget(opts.target);
if (!parsed) throw new Error(`invalid SSH target: ${opts.target}`);
let localPort = opts.localPortPreferred;
try {
await ensurePortAvailable(localPort);
} catch (err) {
if (isErrno(err) && err.code === "EADDRINUSE") localPort = await pickEphemeralPort();
else throw err;
}
const userHost = parsed.user ? `${parsed.user}@${parsed.host}` : parsed.host;
const args = [
"-N",
"-L",
`${localPort}:127.0.0.1:${opts.remotePort}`,
"-p",
String(parsed.port),
"-o",
"ExitOnForwardFailure=yes",
"-o",
"BatchMode=yes",
"-o",
"StrictHostKeyChecking=yes",
"-o",
"UpdateHostKeys=yes",
"-o",
"ConnectTimeout=5",
"-o",
"ServerAliveInterval=15",
"-o",
"ServerAliveCountMax=3"
];
if (opts.identity?.trim()) args.push("-i", opts.identity.trim());
args.push("--", userHost);
const stderr = [];
const child = spawn("/usr/bin/ssh", args, { stdio: [
"ignore",
"ignore",
"pipe"
] });
child.stderr?.setEncoding("utf8");
child.stderr?.on("data", (chunk) => {
const lines = String(chunk).split("\n").map((l) => l.trim()).filter(Boolean);
stderr.push(...lines);
});
const stop = async () => {
if (child.killed) return;
child.kill("SIGTERM");
await new Promise((resolve) => {
const t = setTimeout(() => {
try {
child.kill("SIGKILL");
} finally {
resolve();
}
}, 1500);
child.once("exit", () => {
clearTimeout(t);
resolve();
});
});
};
try {
await Promise.race([waitForLocalListener(localPort, Math.max(250, opts.timeoutMs)), new Promise((_, reject) => {
child.once("exit", (code, signal) => {
reject(/* @__PURE__ */ new Error(`ssh exited (${code ?? "null"}${signal ? `/${signal}` : ""})`));
});
})]);
} catch (err) {
await stop();
const suffix = stderr.length > 0 ? `\n${stderr.join("\n")}` : "";
throw new Error(`${err instanceof Error ? err.message : String(err)}${suffix}`, { cause: err });
}
return {
parsedTarget: parsed,
localPort,
remotePort: opts.remotePort,
pid: typeof child.pid === "number" ? child.pid : null,
stderr,
stop
};
}
//#endregion
//#region src/commands/gateway-status/helpers.ts
function parseIntOrNull(value) {
const s = typeof value === "string" ? value.trim() : typeof value === "number" || typeof value === "bigint" ? String(value) : "";
if (!s) return null;
const n = Number.parseInt(s, 10);
return Number.isFinite(n) ? n : null;
}
function parseTimeoutMs(raw, fallbackMs) {
const value = typeof raw === "string" ? raw.trim() : typeof raw === "number" || typeof raw === "bigint" ? String(raw) : "";
if (!value) return fallbackMs;
const parsed = Number.parseInt(value, 10);
if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(`invalid --timeout: ${value}`);
return parsed;
}
function normalizeWsUrl(value) {
const trimmed = value.trim();
if (!trimmed) return null;
if (!trimmed.startsWith("ws://") && !trimmed.startsWith("wss://")) return null;
return trimmed;
}
function resolveTargets(cfg, explicitUrl) {
const targets = [];
const add = (t) => {
if (!targets.some((x) => x.url === t.url)) targets.push(t);
};
const explicit = typeof explicitUrl === "string" ? normalizeWsUrl(explicitUrl) : null;
if (explicit) add({
id: "explicit",
kind: "explicit",
url: explicit,
active: true
});
const remoteUrl = typeof cfg.gateway?.remote?.url === "string" ? normalizeWsUrl(cfg.gateway.remote.url) : null;
if (remoteUrl) add({
id: "configRemote",
kind: "configRemote",
url: remoteUrl,
active: cfg.gateway?.mode === "remote"
});
add({
id: "localLoopback",
kind: "localLoopback",
url: `ws://127.0.0.1:${resolveGatewayPort(cfg)}`,
active: cfg.gateway?.mode !== "remote"
});
return targets;
}
function resolveProbeBudgetMs(overallMs, kind) {
if (kind === "localLoopback") return Math.min(800, overallMs);
if (kind === "sshTunnel") return Math.min(2e3, overallMs);
return Math.min(1500, overallMs);
}
function sanitizeSshTarget(value) {
if (typeof value !== "string") return null;
const trimmed = value.trim();
if (!trimmed) return null;
return trimmed.replace(/^ssh\\s+/, "");
}
function resolveAuthForTarget(cfg, target, overrides) {
const tokenOverride = overrides.token?.trim() ? overrides.token.trim() : void 0;
const passwordOverride = overrides.password?.trim() ? overrides.password.trim() : void 0;
if (tokenOverride || passwordOverride) return {
token: tokenOverride,
password: passwordOverride
};
if (target.kind === "configRemote" || target.kind === "sshTunnel") {
const token = typeof cfg.gateway?.remote?.token === "string" ? cfg.gateway.remote.token.trim() : "";
const remotePassword = (cfg.gateway?.remote)?.password;
const password = typeof remotePassword === "string" ? remotePassword.trim() : "";
return {
token: token.length > 0 ? token : void 0,
password: password.length > 0 ? password : void 0
};
}
const envToken = process.env.OPENCLAW_GATEWAY_TOKEN?.trim() || "";
const envPassword = process.env.OPENCLAW_GATEWAY_PASSWORD?.trim() || "";
const cfgToken = typeof cfg.gateway?.auth?.token === "string" ? cfg.gateway.auth.token.trim() : "";
const cfgPassword = typeof cfg.gateway?.auth?.password === "string" ? cfg.gateway.auth.password.trim() : "";
return {
token: envToken || cfgToken || void 0,
password: envPassword || cfgPassword || void 0
};
}
function extractConfigSummary(snapshotUnknown) {
const snap = snapshotUnknown;
const path = typeof snap?.path === "string" ? snap.path : null;
const exists = Boolean(snap?.exists);
const valid = Boolean(snap?.valid);
const issuesRaw = Array.isArray(snap?.issues) ? snap.issues : [];
const legacyRaw = Array.isArray(snap?.legacyIssues) ? snap.legacyIssues : [];
const cfg = snap?.config ?? {};
const gateway = cfg.gateway ?? {};
const wideArea = (cfg.discovery ?? {}).wideArea ?? {};
const remote = gateway.remote ?? {};
const auth = gateway.auth ?? {};
const controlUi = gateway.controlUi ?? {};
const tailscale = gateway.tailscale ?? {};
const authMode = typeof auth.mode === "string" ? auth.mode : null;
const authTokenConfigured = typeof auth.token === "string" ? auth.token.trim().length > 0 : false;
const authPasswordConfigured = typeof auth.password === "string" ? auth.password.trim().length > 0 : false;
const remoteUrl = typeof remote.url === "string" ? normalizeWsUrl(remote.url) : null;
const remoteTokenConfigured = typeof remote.token === "string" ? remote.token.trim().length > 0 : false;
const remotePasswordConfigured = typeof remote.password === "string" ? String(remote.password).trim().length > 0 : false;
const wideAreaEnabled = typeof wideArea.enabled === "boolean" ? wideArea.enabled : null;
return {
path,
exists,
valid,
issues: issuesRaw.filter((i) => Boolean(i && typeof i.path === "string" && typeof i.message === "string")).map((i) => ({
path: i.path,
message: i.message
})),
legacyIssues: legacyRaw.filter((i) => Boolean(i && typeof i.path === "string" && typeof i.message === "string")).map((i) => ({
path: i.path,
message: i.message
})),
gateway: {
mode: typeof gateway.mode === "string" ? gateway.mode : null,
bind: typeof gateway.bind === "string" ? gateway.bind : null,
port: parseIntOrNull(gateway.port),
controlUiEnabled: typeof controlUi.enabled === "boolean" ? controlUi.enabled : null,
controlUiBasePath: typeof controlUi.basePath === "string" ? controlUi.basePath : null,
authMode,
authTokenConfigured,
authPasswordConfigured,
remoteUrl,
remoteTokenConfigured,
remotePasswordConfigured,
tailscaleMode: typeof tailscale.mode === "string" ? tailscale.mode : null
},
discovery: { wideAreaEnabled }
};
}
function buildNetworkHints(cfg) {
const tailnetIPv4 = pickPrimaryTailnetIPv4();
const port = resolveGatewayPort(cfg);
return {
localLoopbackUrl: `ws://127.0.0.1:${port}`,
localTailnetUrl: tailnetIPv4 ? `ws://${tailnetIPv4}:${port}` : null,
tailnetIPv4: tailnetIPv4 ?? null
};
}
function renderTargetHeader(target, rich) {
const kindLabel = target.kind === "localLoopback" ? "Local loopback" : target.kind === "sshTunnel" ? "Remote over SSH" : target.kind === "configRemote" ? target.active ? "Remote (configured)" : "Remote (configured, inactive)" : "URL (explicit)";
return `${colorize(rich, theme.heading, kindLabel)} ${colorize(rich, theme.muted, target.url)}`;
}
function renderProbeSummaryLine(probe, rich) {
if (probe.ok) {
const latency = typeof probe.connectLatencyMs === "number" ? `${probe.connectLatencyMs}ms` : "unknown";
return `${colorize(rich, theme.success, "Connect: ok")} (${latency}) · ${colorize(rich, theme.success, "RPC: ok")}`;
}
const detail = probe.error ? ` - ${probe.error}` : "";
if (probe.connectLatencyMs != null) {
const latency = typeof probe.connectLatencyMs === "number" ? `${probe.connectLatencyMs}ms` : "unknown";
return `${colorize(rich, theme.success, "Connect: ok")} (${latency}) · ${colorize(rich, theme.error, "RPC: failed")}${detail}`;
}
return `${colorize(rich, theme.error, "Connect: failed")}${detail}`;
}
//#endregion
//#region src/commands/gateway-status.ts
async function gatewayStatusCommand(opts, runtime) {
const startedAt = Date.now();
const cfg = loadConfig();
const rich = isRich() && opts.json !== true;
const overallTimeoutMs = parseTimeoutMs(opts.timeout, 3e3);
const wideAreaDomain = resolveWideAreaDiscoveryDomain({ configDomain: cfg.discovery?.wideArea?.domain });
const baseTargets = resolveTargets(cfg, opts.url);
const network = buildNetworkHints(cfg);
const discoveryTimeoutMs = Math.min(1200, overallTimeoutMs);
const discoveryPromise = discoverGatewayBeacons({
timeoutMs: discoveryTimeoutMs,
wideAreaDomain
});
let sshTarget = sanitizeSshTarget(opts.ssh) ?? sanitizeSshTarget(cfg.gateway?.remote?.sshTarget);
let sshIdentity = sanitizeSshTarget(opts.sshIdentity) ?? sanitizeSshTarget(cfg.gateway?.remote?.sshIdentity);
const remotePort = resolveGatewayPort(cfg);
let sshTunnelError = null;
let sshTunnelStarted = false;
if (!sshTarget) sshTarget = inferSshTargetFromRemoteUrl(cfg.gateway?.remote?.url);
if (sshTarget) {
const resolved = await resolveSshTarget(sshTarget, sshIdentity, overallTimeoutMs);
if (resolved) {
sshTarget = resolved.target;
if (!sshIdentity && resolved.identity) sshIdentity = resolved.identity;
}
}
const { discovery, probed } = await withProgress({
label: "Inspecting gateways…",
indeterminate: true,
enabled: opts.json !== true
}, async () => {
const tryStartTunnel = async () => {
if (!sshTarget) return null;
try {
const tunnel = await startSshPortForward({
target: sshTarget,
identity: sshIdentity ?? void 0,
localPortPreferred: remotePort,
remotePort,
timeoutMs: Math.min(1500, overallTimeoutMs)
});
sshTunnelStarted = true;
return tunnel;
} catch (err) {
sshTunnelError = err instanceof Error ? err.message : String(err);
return null;
}
};
const discoveryTask = discoveryPromise.catch(() => []);
const tunnelTask = sshTarget ? tryStartTunnel() : Promise.resolve(null);
const [discovery, tunnelFirst] = await Promise.all([discoveryTask, tunnelTask]);
if (!sshTarget && opts.sshAuto) {
const user = process.env.USER?.trim() || "";
const candidates = discovery.map((b) => {
const host = b.tailnetDns || b.lanHost || b.host;
if (!host?.trim()) return null;
const sshPort = typeof b.sshPort === "number" && b.sshPort > 0 ? b.sshPort : 22;
const base = user ? `${user}@${host.trim()}` : host.trim();
return sshPort !== 22 ? `${base}:${sshPort}` : base;
}).filter((candidate) => Boolean(candidate && parseSshTarget(candidate)));
if (candidates.length > 0) sshTarget = candidates[0] ?? null;
}
const tunnel = tunnelFirst || (sshTarget && !sshTunnelStarted && !sshTunnelError ? await tryStartTunnel() : null);
const tunnelTarget = tunnel ? {
id: "sshTunnel",
kind: "sshTunnel",
url: `ws://127.0.0.1:${tunnel.localPort}`,
active: true,
tunnel: {
kind: "ssh",
target: sshTarget ?? "",
localPort: tunnel.localPort,
remotePort,
pid: tunnel.pid
}
} : null;
const targets = tunnelTarget ? [tunnelTarget, ...baseTargets.filter((t) => t.url !== tunnelTarget.url)] : baseTargets;
try {
return {
discovery,
probed: await Promise.all(targets.map(async (target) => {
const auth = resolveAuthForTarget(cfg, target, {
token: typeof opts.token === "string" ? opts.token : void 0,
password: typeof opts.password === "string" ? opts.password : void 0
});
const timeoutMs = resolveProbeBudgetMs(overallTimeoutMs, target.kind);
const probe = await probeGateway({
url: target.url,
auth,
timeoutMs
});
return {
target,
probe,
configSummary: probe.configSnapshot ? extractConfigSummary(probe.configSnapshot) : null,
self: pickGatewaySelfPresence(probe.presence)
};
}))
};
} finally {
if (tunnel) try {
await tunnel.stop();
} catch {}
}
});
const reachable = probed.filter((p) => p.probe.ok);
const ok = reachable.length > 0;
const multipleGateways = reachable.length > 1;
const primary = reachable.find((p) => p.target.kind === "explicit") ?? reachable.find((p) => p.target.kind === "sshTunnel") ?? reachable.find((p) => p.target.kind === "configRemote") ?? reachable.find((p) => p.target.kind === "localLoopback") ?? null;
const warnings = [];
if (sshTarget && !sshTunnelStarted) warnings.push({
code: "ssh_tunnel_failed",
message: sshTunnelError ? `SSH tunnel failed: ${String(sshTunnelError)}` : "SSH tunnel failed to start; falling back to direct probes."
});
if (multipleGateways) warnings.push({
code: "multiple_gateways",
message: "Unconventional setup: multiple reachable gateways detected. Usually one gateway per network is recommended unless you intentionally run isolated profiles, like a rescue bot (see docs: /gateway#multiple-gateways-same-host).",
targetIds: reachable.map((p) => p.target.id)
});
if (opts.json) {
runtime.log(JSON.stringify({
ok,
ts: Date.now(),
durationMs: Date.now() - startedAt,
timeoutMs: overallTimeoutMs,
primaryTargetId: primary?.target.id ?? null,
warnings,
network,
discovery: {
timeoutMs: discoveryTimeoutMs,
count: discovery.length,
beacons: discovery.map((b) => ({
instanceName: b.instanceName,
displayName: b.displayName ?? null,
domain: b.domain ?? null,
host: b.host ?? null,
lanHost: b.lanHost ?? null,
tailnetDns: b.tailnetDns ?? null,
gatewayPort: b.gatewayPort ?? null,
sshPort: b.sshPort ?? null,
wsUrl: (() => {
const host = b.tailnetDns || b.lanHost || b.host;
const port = b.gatewayPort ?? 18789;
return host ? `ws://${host}:${port}` : null;
})()
}))
},
targets: probed.map((p) => ({
id: p.target.id,
kind: p.target.kind,
url: p.target.url,
active: p.target.active,
tunnel: p.target.tunnel ?? null,
connect: {
ok: p.probe.ok,
latencyMs: p.probe.connectLatencyMs,
error: p.probe.error,
close: p.probe.close
},
self: p.self,
config: p.configSummary,
health: p.probe.health,
summary: p.probe.status,
presence: p.probe.presence
}))
}, null, 2));
if (!ok) runtime.exit(1);
return;
}
runtime.log(colorize(rich, theme.heading, "Gateway Status"));
runtime.log(ok ? `${colorize(rich, theme.success, "Reachable")}: yes` : `${colorize(rich, theme.error, "Reachable")}: no`);
runtime.log(colorize(rich, theme.muted, `Probe budget: ${overallTimeoutMs}ms`));
if (warnings.length > 0) {
runtime.log("");
runtime.log(colorize(rich, theme.warn, "Warning:"));
for (const w of warnings) runtime.log(`- ${w.message}`);
}
runtime.log("");
runtime.log(colorize(rich, theme.heading, "Discovery (this machine)"));
const discoveryDomains = wideAreaDomain ? `local. + ${wideAreaDomain}` : "local.";
runtime.log(discovery.length > 0 ? `Found ${discovery.length} gateway(s) via Bonjour (${discoveryDomains})` : `Found 0 gateways via Bonjour (${discoveryDomains})`);
if (discovery.length === 0) runtime.log(colorize(rich, theme.muted, "Tip: if the gateway is remote, mDNS won’t cross networks; use Wide-Area Bonjour (split DNS) or SSH tunnels."));
runtime.log("");
runtime.log(colorize(rich, theme.heading, "Targets"));
for (const p of probed) {
runtime.log(renderTargetHeader(p.target, rich));
runtime.log(` ${renderProbeSummaryLine(p.probe, rich)}`);
if (p.target.tunnel?.kind === "ssh") runtime.log(` ${colorize(rich, theme.muted, "ssh")}: ${colorize(rich, theme.command, p.target.tunnel.target)}`);
if (p.probe.ok && p.self) {
const host = p.self.host ?? "unknown";
const ip = p.self.ip ? ` (${p.self.ip})` : "";
const platform = p.self.platform ? ` · ${p.self.platform}` : "";
const version = p.self.version ? ` · app ${p.self.version}` : "";
runtime.log(` ${colorize(rich, theme.info, "Gateway")}: ${host}${ip}${platform}${version}`);
}
if (p.configSummary) {
const c = p.configSummary;
const wideArea = c.discovery.wideAreaEnabled === true ? "enabled" : c.discovery.wideAreaEnabled === false ? "disabled" : "unknown";
runtime.log(` ${colorize(rich, theme.info, "Wide-area discovery")}: ${wideArea}`);
}
runtime.log("");
}
if (!ok) runtime.exit(1);
}
function inferSshTargetFromRemoteUrl(rawUrl) {
if (typeof rawUrl !== "string") return null;
const trimmed = rawUrl.trim();
if (!trimmed) return null;
let host = null;
try {
host = new URL(trimmed).hostname || null;
} catch {
return null;
}
if (!host) return null;
const user = process.env.USER?.trim() || "";
return user ? `${user}@${host}` : host;
}
function buildSshTarget(input) {
const host = input.host?.trim() ?? "";
if (!host) return null;
const user = input.user?.trim() ?? "";
const base = user ? `${user}@${host}` : host;
const port = input.port ?? 22;
if (port && port !== 22) return `${base}:${port}`;
return base;
}
async function resolveSshTarget(rawTarget, identity, overallTimeoutMs) {
const parsed = parseSshTarget(rawTarget);
if (!parsed) return null;
const config = await resolveSshConfig(parsed, {
identity: identity ?? void 0,
timeoutMs: Math.min(800, overallTimeoutMs)
});
if (!config) return {
target: rawTarget,
identity: identity ?? void 0
};
const target = buildSshTarget({
user: config.user ?? parsed.user,
host: config.host ?? parsed.host,
port: config.port ?? parsed.port
});
if (!target) return {
target: rawTarget,
identity: identity ?? void 0
};
return {
target,
identity: identity ?? config.identityFiles.find((entry) => entry.trim().length > 0)?.trim() ?? void 0
};
}
//#endregion
//#region src/cli/gateway-cli/call.ts
const gatewayCallOpts = (cmd) => cmd.option("--url <url>", "Gateway WebSocket URL (defaults to gateway.remote.url when configured)").option("--token <token>", "Gateway token (if required)").option("--password <password>", "Gateway password (password auth)").option("--timeout <ms>", "Timeout in ms", "10000").option("--expect-final", "Wait for final response (agent)", false).option("--json", "Output JSON", false);
const callGatewayCli = async (method, opts, params) => withProgress({
label: `Gateway ${method}`,
indeterminate: true,
enabled: opts.json !== true
}, async () => await callGateway({
url: opts.url,
token: opts.token,
password: opts.password,
method,
params,
expectFinal: Boolean(opts.expectFinal),
timeoutMs: Number(opts.timeout ?? 1e4),
clientName: GATEWAY_CLIENT_NAMES.CLI,
mode: GATEWAY_CLIENT_MODES.CLI
}));
//#endregion
//#region src/cli/gateway-cli/discover.ts
function parseDiscoverTimeoutMs(raw, fallbackMs) {
if (raw === void 0 || raw === null) return fallbackMs;
const value = typeof raw === "string" ? raw.trim() : typeof raw === "number" || typeof raw === "bigint" ? String(raw) : null;
if (value === null) throw new Error("invalid --timeout");
if (!value) return fallbackMs;
const parsed = Number.parseInt(value, 10);
if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(`invalid --timeout: ${value}`);
return parsed;
}
function pickBeaconHost(beacon) {
const host = beacon.host || beacon.tailnetDns || beacon.lanHost;
return host?.trim() ? host.trim() : null;
}
function pickGatewayPort(beacon) {
const port = beacon.port ?? beacon.gatewayPort ?? 18789;
return port > 0 ? port : 18789;
}
function dedupeBeacons(beacons) {
const out = [];
const seen = /* @__PURE__ */ new Set();
for (const b of beacons) {
const host = pickBeaconHost(b) ?? "";
const key = [
b.domain ?? "",
b.instanceName ?? "",
b.displayName ?? "",
host,
String(b.port ?? ""),
String(b.gatewayPort ?? "")
].join("|");
if (seen.has(key)) continue;
seen.add(key);
out.push(b);
}
return out;
}
function renderBeaconLines(beacon, rich) {
const nameRaw = (beacon.displayName || beacon.instanceName || "Gateway").trim();
const domainRaw = (beacon.domain || "local.").trim();
const title = colorize(rich, theme.accentBright, nameRaw);
const domain = colorize(rich, theme.muted, domainRaw);
const host = pickBeaconHost(beacon);
const gatewayPort = pickGatewayPort(beacon);
const scheme = beacon.gatewayTls ? "wss" : "ws";
const wsUrl = host ? `${scheme}://${host}:${gatewayPort}` : null;
const lines = [`- ${title} ${domain}`];
if (beacon.tailnetDns) lines.push(` ${colorize(rich, theme.info, "tailnet")}: ${beacon.tailnetDns}`);
if (beacon.lanHost) lines.push(` ${colorize(rich, theme.info, "lan")}: ${beacon.lanHost}`);
if (beacon.host) lines.push(` ${colorize(rich, theme.info, "host")}: ${beacon.host}`);
if (wsUrl) lines.push(` ${colorize(rich, theme.muted, "ws")}: ${colorize(rich, theme.command, wsUrl)}`);
if (bea