nolo-cli 0.1.30 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/agent-runtime/agentRecordConfig.ts +0 -2
  2. package/agent-runtime/hostAdapter.ts +0 -1
  3. package/agent-runtime/hostRuntimeCapabilities.ts +7 -0
  4. package/agent-runtime/hostRuntimeConfig.ts +86 -0
  5. package/agent-runtime/index.ts +13 -0
  6. package/agent-runtime/localToolPolicy.ts +13 -6
  7. package/agent-runtime/localWorkspaceTools.ts +333 -30
  8. package/agent-runtime/platformChatProvider.ts +4 -1
  9. package/agent-runtime/runtimeToolPolicy.ts +32 -47
  10. package/agent-runtime/shellCommandPolicy.ts +117 -0
  11. package/agent-runtime/toolGate.ts +38 -0
  12. package/agentRecordCommands.ts +1 -1
  13. package/agentRecordHelpers.ts +35 -20
  14. package/agentRunParsing.ts +18 -5
  15. package/agentRuntimeLocal.ts +10 -0
  16. package/ai/agent/agentRunDialogLineage.ts +56 -0
  17. package/ai/agent/agentSlice.ts +45 -10
  18. package/ai/agent/avatarUtils.ts +1 -1
  19. package/ai/agent/buildSystemPrompt.ts +1 -2
  20. package/ai/agent/cliExecutor.ts +330 -19
  21. package/ai/agent/createAgentSchema.ts +22 -0
  22. package/ai/agent/hooks/fetchPublicAgents.ts +14 -39
  23. package/ai/agent/hooks/usePublicAgents.ts +90 -192
  24. package/ai/agent/publicAgentCatalog.ts +359 -0
  25. package/ai/agent/publicAgentIdentity.ts +76 -0
  26. package/ai/agent/server/fetchPublicAgents.ts +21 -46
  27. package/ai/agent/serverOrigin.ts +7 -0
  28. package/ai/agent/streamAgentChatTurn.ts +23 -21
  29. package/ai/agent/streamAgentChatTurnUtils.ts +292 -6
  30. package/ai/agent/types.ts +2 -0
  31. package/ai/agent/web/agentDisplayUtils.ts +171 -0
  32. package/ai/agent/web/useAgentModelAvatarComponent.ts +32 -0
  33. package/ai/ai.locale.ts +8 -0
  34. package/ai/chat/sendOpenAICompletionsRequest.native.ts +15 -0
  35. package/ai/chat/sendOpenAICompletionsRequest.ts +93 -1
  36. package/ai/chat/sendOpenAIResponseRequest.ts +86 -9
  37. package/ai/chat/updateTotalUsage.ts +7 -0
  38. package/ai/llm/deepinfra.ts +30 -15
  39. package/ai/llm/mistral.ts +3 -4
  40. package/ai/llm/providers.ts +2 -0
  41. package/ai/llm/types.ts +15 -0
  42. package/ai/memory/capture.ts +3 -3
  43. package/ai/memory/consolidate.ts +2 -2
  44. package/ai/memory/delete.ts +4 -3
  45. package/ai/memory/query.ts +3 -3
  46. package/ai/memory/remember.ts +2 -2
  47. package/ai/memory/store.ts +3 -3
  48. package/ai/memory/storeShared.ts +3 -2
  49. package/ai/policy/commercialMultiplier.ts +16 -0
  50. package/ai/token/billingAdminAuditEvent.ts +68 -0
  51. package/ai/token/billingAdminAuditEventWriter.ts +41 -0
  52. package/ai/token/billingAnomaly.ts +90 -0
  53. package/ai/token/billingAnomalyLifecycle.ts +54 -0
  54. package/ai/token/billingAnomalyLifecycleWriter.ts +44 -0
  55. package/ai/token/billingAnomalyWriter.ts +41 -0
  56. package/ai/token/calculatePrice.ts +3 -2
  57. package/ai/token/deepinfraReconciliation.ts +153 -0
  58. package/ai/token/deepinfraRequestIdQuery.ts +92 -0
  59. package/ai/token/externalToolCost.ts +54 -41
  60. package/ai/token/missingUsageEstimate.ts +12 -3
  61. package/ai/token/normalizeUsage.ts +45 -5
  62. package/ai/token/openaiReconciliation.ts +188 -0
  63. package/ai/token/providerBillableEvent.ts +84 -0
  64. package/ai/token/providerBillableEventWriter.ts +36 -0
  65. package/ai/token/providerBillingAnomalyDrilldown.ts +308 -0
  66. package/ai/token/providerBillingDrilldown.ts +361 -0
  67. package/ai/token/providerBillingHealthReport.ts +366 -0
  68. package/ai/token/providerCall.ts +144 -0
  69. package/ai/token/providerCallSweeper.ts +168 -0
  70. package/ai/token/providerCallWriter.ts +33 -0
  71. package/ai/token/providerCredential.ts +89 -0
  72. package/ai/token/providerCredentialAnomaly.ts +154 -0
  73. package/ai/token/providerCredentialLifecycle.ts +59 -0
  74. package/ai/token/providerCredentialLifecycleWriter.ts +44 -0
  75. package/ai/token/providerCredentialRegistry.ts +83 -0
  76. package/ai/token/providerCredentialRegistryCommand.ts +97 -0
  77. package/ai/token/providerCredentialRegistryList.ts +124 -0
  78. package/ai/token/providerCredentialRegistryResolver.ts +150 -0
  79. package/ai/token/providerCredentialRegistryWriter.ts +41 -0
  80. package/ai/token/providerCredentialRotationCommand.ts +102 -0
  81. package/ai/token/providerCredentialRuntime.ts +56 -0
  82. package/ai/token/providerDispatchIntent.ts +67 -0
  83. package/ai/token/providerDispatchIntentWriter.ts +36 -0
  84. package/ai/token/providerGateway.ts +260 -0
  85. package/ai/token/providerReconciliation.ts +148 -0
  86. package/ai/token/providerReconciliationLocal.ts +97 -0
  87. package/ai/token/providerReconciliationRunner.ts +165 -0
  88. package/ai/token/providerReconciliationWriter.ts +41 -0
  89. package/ai/token/ratingResult.ts +202 -0
  90. package/ai/token/ratingResultWriter.ts +33 -0
  91. package/ai/token/serverDialogProjection.ts +5 -3
  92. package/ai/token/serverTokenWriter.ts +11 -3
  93. package/ai/token/types.ts +10 -0
  94. package/ai/tools/agent/callAgentTool.ts +3 -4
  95. package/ai/tools/agent/presets/appBuilderPreset.ts +5 -7
  96. package/ai/tools/agent/runStreamingAgentTool.ts +1 -2
  97. package/ai/tools/agent/streamParallelAgentsTool.ts +2 -3
  98. package/ai/tools/readXPostTool.ts +1 -5
  99. package/app/admin/adminPages.ts +9 -0
  100. package/app/i18n/i18n.config.ts +5 -0
  101. package/app/i18n/translations/pricing.locale.ts +397 -0
  102. package/app/utils/desktopAgentRuntimeStatusClient.ts +5 -4
  103. package/app/utils/desktopAgentRuntimeTurnClient.ts +4 -1
  104. package/app/utils/imageUtils.ts +64 -34
  105. package/auth/adminPermissions.ts +18 -0
  106. package/auth/authSlice.ts +26 -12
  107. package/auth/hooks/useFetchUsers.ts +3 -0
  108. package/auth/routes.ts +46 -0
  109. package/auth/server/accessActivity.ts +4 -3
  110. package/auth/server/adminPermissions.ts +43 -0
  111. package/auth/server/adminUsageReport.ts +375 -0
  112. package/auth/server/billingAnomalies.ts +235 -0
  113. package/auth/server/billingUsageReport.ts +41 -2
  114. package/auth/server/creatorEarningCreditConversion.ts +10 -2
  115. package/auth/server/deduct.ts +14 -7
  116. package/auth/server/delete.ts +10 -2
  117. package/auth/server/disable.ts +4 -3
  118. package/auth/server/emailAutomation.ts +7 -5
  119. package/auth/server/emailDbConfig.ts +4 -2
  120. package/auth/server/emailDelivery.ts +24 -20
  121. package/auth/server/emailDeliveryGuards.ts +10 -7
  122. package/auth/server/emailPreferences.ts +6 -4
  123. package/auth/server/emailReport.ts +6 -4
  124. package/auth/server/emailRuntimeConfig.ts +3 -3
  125. package/auth/server/emailScenarioNotifications.ts +9 -8
  126. package/auth/server/enable.ts +4 -3
  127. package/auth/server/getRechargeHistory.ts +3 -2
  128. package/auth/server/getUser.ts +3 -2
  129. package/auth/server/gptProAccess.ts +44 -0
  130. package/auth/server/inviteReward.ts +10 -7
  131. package/auth/server/listusers.ts +6 -4
  132. package/auth/server/login.ts +2 -2
  133. package/auth/server/productEntitlements.ts +7 -4
  134. package/auth/server/productPurchase.ts +10 -2
  135. package/auth/server/providerBillingDrilldown.ts +47 -0
  136. package/auth/server/providerBillingHealth.ts +71 -0
  137. package/auth/server/providerCredentials.ts +136 -0
  138. package/auth/server/recharge.ts +14 -8
  139. package/auth/server/revokeSessions.ts +4 -3
  140. package/auth/server/route.ts +77 -0
  141. package/auth/server/sendEmail.ts +5 -4
  142. package/auth/server/signup.ts +10 -3
  143. package/auth/server/token.ts +53 -55
  144. package/auth/server/tokenUsageBilling.ts +18 -7
  145. package/auth/server/usageManagementAccess.ts +43 -0
  146. package/auth/server/usageReport.ts +59 -0
  147. package/auth/types.ts +12 -0
  148. package/auth/utils.ts +24 -22
  149. package/authCommands.ts +36 -2
  150. package/buildPublish.ts +3 -1
  151. package/buildPublishArtifactAssembly.ts +62 -1
  152. package/buildPublishImportAnalysis.ts +21 -16
  153. package/chat/dialog/dialogLaunchScope.ts +32 -0
  154. package/chat/dialog/objectAssistantRegistry.ts +2 -2
  155. package/chat/dialog/useCreateDialog.ts +1 -1
  156. package/chat/hooks/useChatInput.ts +41 -10
  157. package/chat/messages/messageSlice.ts +5 -1
  158. package/chat/messages/pendingAttachmentParts.ts +28 -2
  159. package/chat/messages/sendFirstMessage.ts +76 -79
  160. package/chat/messages/types.ts +9 -0
  161. package/chat/messages/web/streamingMarkdownModel.ts +41 -0
  162. package/chat/messages/web/useStreamingReveal.ts +91 -0
  163. package/chat/web/messageInputAgentUi.ts +94 -6
  164. package/cliEnvHelpers.ts +8 -0
  165. package/cliRuntimeDoctorReport.ts +193 -0
  166. package/client/agentConfigResolver.test.ts +0 -2
  167. package/client/agentRun.test.ts +171 -10
  168. package/client/agentRun.ts +44 -637
  169. package/client/agentRunInput.ts +10 -0
  170. package/client/agentRunLocalExecutor.ts +179 -0
  171. package/client/agentRunServerTransport.ts +394 -0
  172. package/client/agentRunTaskRunRecorder.ts +176 -0
  173. package/client/localRuntimeAdapter.test.ts +236 -9
  174. package/client/localRuntimeAdapter.ts +221 -23
  175. package/client/localToolPolicy.test.ts +28 -4
  176. package/connector-experimental/daemonLauncher.ts +38 -4
  177. package/connectorRunArtifact.ts +34 -10
  178. package/connectorWebSocketTarget.ts +9 -5
  179. package/core/builtinAgents.ts +155 -11
  180. package/create/space/contentLabels.ts +1 -1
  181. package/create/space/contentTypeMeta.ts +3 -2
  182. package/create/space/sidebarVisibleTypes.ts +27 -0
  183. package/create/space/space.locale.ts +76 -4
  184. package/database/actions/agentReadResolution.ts +2 -8
  185. package/database/actions/read.ts +11 -9
  186. package/database/actions/readAndWait.ts +10 -5
  187. package/database/server/agentDelegation.ts +3 -2
  188. package/database/server/authorityStoreConformance.ts +209 -0
  189. package/database/server/authorityStoreTypes.ts +31 -0
  190. package/database/server/coreDataProxy.ts +1 -1
  191. package/database/server/dataHandlers.ts +13 -5
  192. package/database/server/db.ts +15 -34
  193. package/database/server/delete.ts +88 -71
  194. package/database/server/emailRepository.ts +11 -9
  195. package/database/server/fileService.ts +2 -1
  196. package/database/server/legacyServerDb.ts +58 -0
  197. package/database/server/levelAuthorityStore.ts +75 -0
  198. package/database/server/memoryAuthorityStore.ts +133 -0
  199. package/database/server/patch.ts +13 -12
  200. package/database/server/query.ts +3 -2
  201. package/database/server/read.ts +33 -30
  202. package/database/server/resourceAccess.ts +7 -5
  203. package/database/server/serverStoreFactory.ts +124 -0
  204. package/database/server/spaceMemberAuthority.ts +3 -2
  205. package/database/server/storeBatchCompat.ts +37 -0
  206. package/database/server/testAuthorityStore.ts +76 -0
  207. package/database/server/write.ts +29 -27
  208. package/integrations/anthropic/anthropicModels.ts +12 -12
  209. package/integrations/deepseek/models.ts +67 -67
  210. package/integrations/google/models.ts +111 -53
  211. package/integrations/openai/models.ts +22 -22
  212. package/integrations/xai/models.ts +1 -1
  213. package/localRuntimeDb.ts +39 -2
  214. package/machineCommandPrompts.ts +2 -0
  215. package/machineCommands.ts +193 -63
  216. package/machineWsRunDispatch.ts +80 -15
  217. package/machineWsSession.ts +39 -2
  218. package/noloReadBridgePrompt.ts +16 -0
  219. package/package.json +99 -6
  220. package/render/layout/mainLayoutViewMode.ts +25 -0
  221. package/render/page/server/createPage.ts +3 -1
  222. package/render/page/server/handleCreatePage.ts +2 -11
  223. package/runtimeDoctorCommands.ts +15 -66
  224. package/server/api/methods.ts +34 -12
  225. package/server/api/tableHandlers.ts +6 -3
  226. package/server/authenticateToken.ts +47 -2
  227. package/server/billingLedgerRoutes.ts +15 -0
  228. package/server/buildManifest.ts +54 -0
  229. package/server/coreReadiness.ts +34 -25
  230. package/server/coreRuntime.ts +15 -10
  231. package/server/handlers/activityStatsHandler.ts +5 -2
  232. package/server/handlers/agentEmailRegistrationE2EHandler.ts +25 -23
  233. package/server/handlers/agentRun/agentLookup.ts +1 -7
  234. package/server/handlers/agentRun/autonomousRuntime.ts +11 -10
  235. package/server/handlers/agentRun/builtinAgents.ts +12 -88
  236. package/server/handlers/agentRun/continueDialogContext.ts +15 -3
  237. package/server/handlers/agentRun/{inheritedDialogPrompt.ts → conversationReference.ts} +3 -3
  238. package/server/handlers/agentRun/dialogLookup.ts +4 -3
  239. package/server/handlers/agentRun/index.ts +864 -404
  240. package/server/handlers/agentRun/localRuntimeBootstrap.ts +17 -1
  241. package/server/handlers/agentRun/loop.ts +920 -508
  242. package/server/handlers/agentRun/machineConnector.ts +56 -8
  243. package/server/handlers/agentRun/machineRunAudit.ts +3 -2
  244. package/server/handlers/agentRun/modelUsageToolHandlers.ts +13 -10
  245. package/server/handlers/agentRun/requestContext.ts +12 -8
  246. package/server/handlers/agentRun/runtimeExecutionContext.ts +15 -3
  247. package/server/handlers/agentRun/runtimePreparation.ts +26 -24
  248. package/server/handlers/agentRun/runtimeRouting.ts +25 -40
  249. package/server/handlers/agentRun/runtimeSystemMessages.ts +65 -0
  250. package/server/handlers/agentRun/saveDialog.ts +97 -87
  251. package/server/handlers/agentRun/taskRunToolHandler.ts +9 -7
  252. package/server/handlers/agentRun/toolExecutor.ts +42 -78
  253. package/server/handlers/agentRun/types.ts +2 -1
  254. package/server/handlers/appDeployStatusStore.ts +17 -10
  255. package/server/handlers/appDomainHandler.ts +21 -15
  256. package/server/handlers/appLegacyInfraCleanup.ts +5 -2
  257. package/server/handlers/appSandboxHandler.ts +29 -8
  258. package/server/handlers/appSpaceAccess.ts +15 -8
  259. package/server/handlers/chatHandler.ts +22 -2
  260. package/server/handlers/chatUpstreamRetry.ts +225 -2
  261. package/server/handlers/connector/connectorConnectionRegistry.ts +55 -7
  262. package/server/handlers/connector/connectorWebSocket.ts +14 -10
  263. package/server/handlers/desktopAgentRuntimeAdapter.ts +4 -4
  264. package/server/handlers/desktopAgentRuntimeHostCapabilities.ts +25 -0
  265. package/server/handlers/desktopAgentRuntimeReadiness.ts +62 -0
  266. package/server/handlers/desktopAgentRuntimeStatusHandler.ts +38 -30
  267. package/server/handlers/desktopAgentRuntimeTurnHandler.ts +7 -3
  268. package/server/handlers/desktopAgentRuntimeTurnService.ts +39 -45
  269. package/server/handlers/desktopLlamaRuntimeHandler.ts +85 -51
  270. package/server/handlers/desktopUpdaterHandler.ts +132 -24
  271. package/server/handlers/favoriteHandlers.ts +8 -2
  272. package/server/handlers/getDialogBillingHandler.ts +4 -3
  273. package/server/handlers/getTransactionsHandler.ts +5 -2
  274. package/server/handlers/machines/machineBinding.ts +1 -0
  275. package/server/handlers/machines/machineHandlers.ts +0 -14
  276. package/server/handlers/machines/machineInstallAssets.ts +4 -2
  277. package/server/handlers/notificationEmitter.ts +5 -3
  278. package/server/handlers/notificationsHandler.ts +5 -2
  279. package/server/handlers/providerCallsRecentHandler.ts +234 -0
  280. package/server/handlers/providerReconciliationRunHandler.ts +180 -0
  281. package/server/handlers/providerRequestIdsHandler.ts +105 -0
  282. package/server/handlers/tableAccess.ts +6 -2
  283. package/server/handlers/tableToolHandlers.ts +28 -14
  284. package/server/handlers/userSecretsHandler.ts +17 -6
  285. package/server/handlers/versionHandler.ts +9 -4
  286. package/server/handlers/versionStore.ts +18 -10
  287. package/server/html/ssrPreload.ts +5 -3
  288. package/server/payments/crypto/cryptoUsdcBasePaymentProvider.ts +11 -8
  289. package/server/payments/crypto/cryptoUsdcBaseScanner.ts +12 -7
  290. package/server/payments/crypto/cryptoUsdtTronPaymentProvider.ts +11 -8
  291. package/server/payments/crypto/cryptoUsdtTronScanner.ts +9 -7
  292. package/server/payments/waffo/waffoPaymentProvider.ts +12 -9
  293. package/server/runtimeRole.ts +15 -0
  294. package/server/scheduler/scheduledTaskRuntime.ts +4 -2
  295. package/server/scheduler/schedulerService.ts +2 -2
  296. package/server/taskRunBridgeRoutes.ts +8 -5
  297. package/share/server/fetchCommunityShares.ts +1 -1
  298. package/share/server/helpers.ts +19 -17
  299. package/share/server/importShare.ts +9 -13
  300. package/share/server/listCommunityShares.ts +4 -4
  301. package/share/server/purchaseDoc.ts +8 -16
  302. package/share/server/readCommunityShareCover.ts +3 -2
  303. package/share/server/readSharedTable.ts +14 -13
  304. package/share/server/tableReplication.ts +42 -39
  305. package/spaceAttachCommands.ts +135 -0
  306. package/spaceInternalCommandEntries.ts +2 -0
  307. package/ai/agent/runtimeServerBase.ts +0 -37
  308. package/server/handlers/desktopAgentRuntimeHostFacts.ts +0 -40
@@ -3,8 +3,11 @@ import type {
3
3
  AgentRuntimeHostAdapter,
4
4
  AgentRuntimeSaveTurnInput,
5
5
  } from "../agentRuntimeLocal";
6
+ import { request as httpRequest } from "node:http";
7
+ import { request as httpsRequest } from "node:https";
6
8
  import {
7
9
  buildLocalWorkspacePolicyToolNames,
10
+ resolveLocalWorkspaceShellToolExposure,
8
11
  buildLocalWorkspaceToolset,
9
12
  buildLocalWorkspaceOpenAiTools,
10
13
  buildOpenAiCompatibleChatCompletionRequest,
@@ -13,8 +16,11 @@ import {
13
16
  parseOpenAiCompatibleChatCompletionResponse,
14
17
  parsePlatformChatCompletionData,
15
18
  parsePlatformChatCompletionResponse,
19
+ resolveHostRuntimeConfigFromEnv,
16
20
  resolvePlatformChatProviderConfig,
21
+ resolveRequestedRuntimeToolNames,
17
22
  shouldUsePlatformChatProvider,
23
+ type HostRuntimeConfig,
18
24
  } from "../agentRuntimeLocal";
19
25
  import type { AgentRuntimeChatMessage } from "../agent-runtime";
20
26
  import { getDefaultCliLocalRuntimeDb } from "../localRuntimeDb";
@@ -46,6 +52,29 @@ import {
46
52
  type EnvLike = Record<string, string | undefined>;
47
53
  type FetchInput = Parameters<typeof fetch>[0];
48
54
  type FetchInit = Parameters<typeof fetch>[1];
55
+ type CliLocalRuntimeCliExecution = {
56
+ text: string;
57
+ raw?: string;
58
+ elapsed?: number;
59
+ warnings?: string[];
60
+ };
61
+ type CliLocalRuntimeCliExecutor = (
62
+ provider: string,
63
+ prompt: string,
64
+ options: {
65
+ model?: string;
66
+ timeout?: number;
67
+ cwd?: string;
68
+ yolo?: boolean;
69
+ env?: EnvLike;
70
+ systemPrompt?: string;
71
+ reasoningEffort?: "low" | "medium" | "high" | "xhigh" | "max";
72
+ temperature?: number;
73
+ topP?: number;
74
+ frequencyPenalty?: number;
75
+ maxTokens?: number;
76
+ }
77
+ ) => Promise<CliLocalRuntimeCliExecution>;
49
78
  const TRANSIENT_FETCH_MAX_ATTEMPTS = 8;
50
79
  const TRANSIENT_FETCH_RETRY_BASE_DELAY_MS = 250;
51
80
 
@@ -53,23 +82,34 @@ export type CliLocalRuntimeDb = CliKvDb;
53
82
 
54
83
  export type CliLocalRuntimeAdapterDeps = {
55
84
  env: EnvLike;
85
+ hostRuntimeConfig?: HostRuntimeConfig;
56
86
  db?: CliLocalRuntimeDb;
57
87
  store?: HybridRecordStore;
58
88
  now?: () => number;
59
89
  createId?: () => string;
60
90
  fetchImpl?: typeof fetch;
61
91
  cwd?: string;
62
- useCwdAsTaskWorkspaceBase?: boolean;
63
92
  output?: { write(chunk: string): unknown };
64
93
  prepareTaskWorktree?: typeof prepareTaskWorktree;
65
94
  localToolExecutors?: Record<string, (call: any) => Promise<{ content: string; metadata?: Record<string, unknown> }>>;
95
+ executeCli?: CliLocalRuntimeCliExecutor;
66
96
  sleep?: (ms: number) => Promise<void>;
97
+ loopbackRequest?: (input: FetchInput, init?: FetchInit) => Promise<Response>;
67
98
  };
68
99
 
69
100
  async function defaultLocalRuntimeDb(): Promise<CliLocalRuntimeDb> {
70
101
  return getDefaultCliLocalRuntimeDb();
71
102
  }
72
103
 
104
+ async function defaultExecuteCli(
105
+ provider: string,
106
+ prompt: string,
107
+ options: Parameters<CliLocalRuntimeCliExecutor>[2]
108
+ ) {
109
+ const { executeCli } = await import("../ai/agent/cliExecutor");
110
+ return executeCli(provider as any, prompt, options);
111
+ }
112
+
73
113
  function createFallbackId() {
74
114
  return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`.toUpperCase();
75
115
  }
@@ -78,10 +118,6 @@ function resolveLocalUserId(env: EnvLike) {
78
118
  return env.NOLO_LOCAL_USER_ID || env.NOLO_USER_ID || "local";
79
119
  }
80
120
 
81
- function shellModeEnabled(env: EnvLike) {
82
- return ["worktree", "dangerous", "1", "true"].includes(env.NOLO_LOCAL_SHELL_MODE || "");
83
- }
84
-
85
121
  function parseLocalToolBudgets(env: EnvLike) {
86
122
  const raw = env.NOLO_LOCAL_TOOL_BUDGETS?.trim();
87
123
  if (!raw) return {};
@@ -122,15 +158,70 @@ function transientFetchRetryDelayMs(attempt: number) {
122
158
  return Math.min(attempt * TRANSIENT_FETCH_RETRY_BASE_DELAY_MS, 2_000);
123
159
  }
124
160
 
161
+ function isLoopbackUrl(input: FetchInput) {
162
+ try {
163
+ const target = typeof input === "string" || input instanceof URL
164
+ ? new URL(String(input))
165
+ : new URL(input.url);
166
+ const hostname = target.hostname.toLowerCase();
167
+ return hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1";
168
+ } catch {
169
+ return false;
170
+ }
171
+ }
172
+
173
+ function toNodeRequestBody(body: FetchInit["body"]) {
174
+ if (typeof body === "string") return Buffer.from(body);
175
+ if (body instanceof Uint8Array) return Buffer.from(body);
176
+ if (body instanceof ArrayBuffer) return Buffer.from(body);
177
+ return null;
178
+ }
179
+
180
+ async function defaultLoopbackRequest(input: FetchInput, init?: FetchInit) {
181
+ const target = typeof input === "string" || input instanceof URL
182
+ ? new URL(String(input))
183
+ : new URL(input.url);
184
+ const headers = new Headers(init?.headers);
185
+ const body = toNodeRequestBody(init?.body);
186
+ if (body && !headers.has("Content-Length")) {
187
+ headers.set("Content-Length", String(body.byteLength));
188
+ }
189
+ return await new Promise<Response>((resolve, reject) => {
190
+ const requestImpl = target.protocol === "https:" ? httpsRequest : httpRequest;
191
+ const req = requestImpl(target, {
192
+ method: init?.method ?? "GET",
193
+ headers: Object.fromEntries(headers.entries()),
194
+ }, (res) => {
195
+ const chunks: Buffer[] = [];
196
+ res.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
197
+ res.on("end", () => {
198
+ resolve(new Response(Buffer.concat(chunks), {
199
+ status: res.statusCode ?? 500,
200
+ headers: res.headers as Record<string, string>,
201
+ }));
202
+ });
203
+ });
204
+ req.on("error", reject);
205
+ if (body) req.write(body);
206
+ req.end();
207
+ });
208
+ }
209
+
125
210
  async function fetchWithTransientRetry(
126
211
  fetchImpl: typeof fetch,
127
212
  input: FetchInput,
128
213
  init?: FetchInit,
129
- options: { sleep?: (ms: number) => Promise<void> } = {}
214
+ options: {
215
+ sleep?: (ms: number) => Promise<void>;
216
+ loopbackRequest?: (input: FetchInput, init?: FetchInit) => Promise<Response>;
217
+ } = {}
130
218
  ) {
131
219
  let lastError: unknown;
132
220
  for (let attempt = 1; attempt <= TRANSIENT_FETCH_MAX_ATTEMPTS; attempt += 1) {
133
221
  try {
222
+ if (options.loopbackRequest && isLoopbackUrl(input)) {
223
+ return await options.loopbackRequest(input, init);
224
+ }
134
225
  return await fetchImpl(input, init);
135
226
  } catch (error) {
136
227
  if (!isTransientFetchError(error)) throw error;
@@ -143,32 +234,52 @@ async function fetchWithTransientRetry(
143
234
  throw lastError;
144
235
  }
145
236
 
146
- function buildOpenAiTools(args: { toolNames?: string[]; env: EnvLike }) {
147
- const toolset = buildLocalWorkspaceToolsetForEnv(args);
237
+ function buildOpenAiTools(args: {
238
+ requestedToolNames?: string[];
239
+ platformToolNames?: string[];
240
+ hostRuntimeConfig: HostRuntimeConfig;
241
+ }) {
242
+ const toolset = buildLocalWorkspaceToolsetForHost(args);
148
243
  return [
149
244
  ...buildLocalWorkspaceOpenAiTools({
150
245
  toolNames: toolset.toolNames,
151
246
  exposeShellTools: toolset.exposeShellTools,
152
247
  }),
153
- ...buildServerPlatformOpenAiTools({ toolNames: args.toolNames }),
248
+ ...buildServerPlatformOpenAiTools({ toolNames: args.platformToolNames }),
154
249
  ];
155
250
  }
156
251
 
157
- function buildLocalWorkspaceToolsetForEnv(args: { toolNames?: string[]; env: EnvLike }) {
252
+ function buildLocalWorkspaceToolsetForHost(args: {
253
+ requestedToolNames?: string[];
254
+ hostRuntimeConfig: HostRuntimeConfig;
255
+ }) {
256
+ const requestedToolNames = args.requestedToolNames ?? [];
257
+ const exposeShellTools = resolveLocalWorkspaceShellToolExposure({
258
+ declaredToolNames: requestedToolNames,
259
+ hostRuntimeConfig: args.hostRuntimeConfig,
260
+ });
158
261
  const toolset = buildLocalWorkspaceToolset({
159
- declaredToolNames: args.toolNames,
160
- exposeShellTools: shellModeEnabled(args.env),
262
+ declaredToolNames: requestedToolNames,
263
+ exposeShellTools,
161
264
  });
162
265
  return toolset;
163
266
  }
164
267
 
165
- function buildLocalPolicyToolNames(args: { toolNames?: string[]; env: EnvLike }) {
268
+ function buildLocalPolicyToolNames(args: {
269
+ requestedToolNames?: string[];
270
+ platformToolNames?: string[];
271
+ hostRuntimeConfig: HostRuntimeConfig;
272
+ }) {
273
+ const exposeShellTools = resolveLocalWorkspaceShellToolExposure({
274
+ declaredToolNames: args.requestedToolNames,
275
+ hostRuntimeConfig: args.hostRuntimeConfig,
276
+ });
166
277
  return [
167
278
  ...buildLocalWorkspacePolicyToolNames({
168
- declaredToolNames: args.toolNames,
169
- exposeShellTools: shellModeEnabled(args.env),
279
+ declaredToolNames: args.requestedToolNames,
280
+ exposeShellTools,
170
281
  }),
171
- ...((args.toolNames ?? []).includes("taskRun") ? ["taskRun"] : []),
282
+ ...((args.platformToolNames ?? []).includes("taskRun") ? ["taskRun"] : []),
172
283
  ];
173
284
  }
174
285
 
@@ -254,6 +365,33 @@ function readLocalWorkspaceMode(agentConfig: AgentRuntimeAgentConfig | null) {
254
365
  return value === "task-worktree" ? "task-worktree" : "current";
255
366
  }
256
367
 
368
+ function extractRuntimeMessageText(content: AgentRuntimeChatMessage["content"]) {
369
+ if (typeof content === "string") return content;
370
+ if (!Array.isArray(content)) return "";
371
+ return content
372
+ .filter((part): part is Extract<typeof part, { type: "text" }> => part?.type === "text")
373
+ .map((part) => part.text ?? "")
374
+ .join("");
375
+ }
376
+
377
+ function buildCliTaskPromptFromMessages(messages: AgentRuntimeChatMessage[]) {
378
+ const history = messages
379
+ .filter((message) => message.role === "user" || message.role === "assistant")
380
+ .map((message) => {
381
+ const text = extractRuntimeMessageText(message.content).trim();
382
+ if (!text) return null;
383
+ return `[${message.role === "user" ? "用户" : "助手"}]\n${text}`;
384
+ })
385
+ .filter((message): message is string => Boolean(message))
386
+ .slice(-6);
387
+
388
+ if (history.length === 0) return "";
389
+ if (history.length === 1 && history[0]?.startsWith("[用户]\n")) {
390
+ return history[0].slice("[用户]\n".length);
391
+ }
392
+ return `请基于以下最近对话继续完成当前任务。\n\n${history.join("\n\n")}`;
393
+ }
394
+
257
395
  async function resolveStore(deps: CliLocalRuntimeAdapterDeps) {
258
396
  if (deps.store) return deps.store;
259
397
  return createCliHybridRecordStore({
@@ -323,12 +461,19 @@ export function createCliLocalRuntimeAdapter(
323
461
  const now = deps.now ?? Date.now;
324
462
  const createId = deps.createId ?? createFallbackId;
325
463
  const fetchImpl = deps.fetchImpl ?? fetch;
464
+ const loopbackRequest = deps.loopbackRequest ?? (deps.fetchImpl ? undefined : defaultLoopbackRequest);
465
+ const executeCli = deps.executeCli ?? defaultExecuteCli;
326
466
  const userId = resolveLocalUserId(deps.env);
467
+ const hostRuntimeConfig = deps.hostRuntimeConfig ?? resolveHostRuntimeConfigFromEnv({
468
+ env: deps.env,
469
+ surface: "cli-local",
470
+ workspaceBinding: "explicit-cwd",
471
+ });
327
472
  const localToolBudgets = parseLocalToolBudgets(deps.env);
328
473
  const localToolUsage = new Map<string, number>();
329
474
  let activeAgentToolNames: string[] = [];
330
475
  let workspaceSession: WorkspaceSession = createWorkspaceSession(
331
- deps.useCwdAsTaskWorkspaceBase
476
+ hostRuntimeConfig.workspaceBinding === "default-cwd"
332
477
  ? { defaultCwd: deps.cwd }
333
478
  : { cwd: deps.cwd }
334
479
  );
@@ -369,9 +514,13 @@ export function createCliLocalRuntimeAdapter(
369
514
  store: await resolveStore(deps),
370
515
  userId,
371
516
  });
517
+ const requestedToolNames = agentConfig
518
+ ? resolveRequestedRuntimeToolNames({ agentConfig })
519
+ : [];
372
520
  activeAgentToolNames = buildLocalPolicyToolNames({
373
- toolNames: agentConfig?.toolNames,
374
- env: deps.env,
521
+ requestedToolNames,
522
+ platformToolNames: agentConfig?.toolNames,
523
+ hostRuntimeConfig,
375
524
  });
376
525
  await activateAgentWorkspaceSession({ agentRef, agentConfig });
377
526
  return agentConfig;
@@ -389,6 +538,48 @@ export function createCliLocalRuntimeAdapter(
389
538
  cwd: workspaceSession.workspaceRoot,
390
539
  }),
391
540
  resolveProvider: async (agentConfig) => {
541
+ if (agentConfig.apiSource === "cli") {
542
+ const cliProvider = String(agentConfig.cliProvider || "copilot").trim() || "copilot";
543
+ return {
544
+ model: agentConfig.model || cliProvider,
545
+ complete: async (messages) => {
546
+ const [firstMessage, ...remainingMessages] = messages;
547
+ const systemPrompt = firstMessage?.role === "system"
548
+ ? extractRuntimeMessageText(firstMessage.content).trim()
549
+ : "";
550
+ const taskPrompt = buildCliTaskPromptFromMessages(
551
+ firstMessage?.role === "system" ? remainingMessages : messages
552
+ );
553
+ const result = await executeCli(cliProvider, taskPrompt, {
554
+ ...(agentConfig.model ? { model: agentConfig.model } : {}),
555
+ cwd: workspaceSession.workspaceRoot,
556
+ yolo: true,
557
+ env: deps.env,
558
+ ...(systemPrompt ? { systemPrompt } : {}),
559
+ ...(typeof agentConfig.reasoning_effort === "string"
560
+ ? { reasoningEffort: agentConfig.reasoning_effort as "low" | "medium" | "high" | "xhigh" | "max" }
561
+ : {}),
562
+ ...(typeof agentConfig.temperature === "number"
563
+ ? { temperature: agentConfig.temperature }
564
+ : {}),
565
+ ...(typeof agentConfig.top_p === "number" ? { topP: agentConfig.top_p } : {}),
566
+ ...(typeof agentConfig.frequency_penalty === "number"
567
+ ? { frequencyPenalty: agentConfig.frequency_penalty }
568
+ : {}),
569
+ ...(typeof agentConfig.max_tokens === "number"
570
+ ? { maxTokens: agentConfig.max_tokens }
571
+ : {}),
572
+ });
573
+ return {
574
+ content: result.text,
575
+ model: agentConfig.model || cliProvider,
576
+ provider: cliProvider,
577
+ trace: messages,
578
+ };
579
+ },
580
+ };
581
+ }
582
+
392
583
  if (shouldUsePlatformChatProvider(deps.env, agentConfig)) {
393
584
  const providerConfig = resolvePlatformChatProviderConfig({
394
585
  agentConfig,
@@ -397,9 +588,11 @@ export function createCliLocalRuntimeAdapter(
397
588
  return {
398
589
  model: providerConfig.model,
399
590
  complete: async (messages) => {
591
+ const requestedToolNames = resolveRequestedRuntimeToolNames({ agentConfig });
400
592
  const tools = buildOpenAiTools({
401
- toolNames: agentConfig.toolNames,
402
- env: deps.env,
593
+ requestedToolNames,
594
+ platformToolNames: agentConfig.toolNames,
595
+ hostRuntimeConfig,
403
596
  });
404
597
  const request = buildPlatformChatCompletionRequest({
405
598
  providerConfig,
@@ -408,6 +601,7 @@ export function createCliLocalRuntimeAdapter(
408
601
  });
409
602
  const res = await fetchWithTransientRetry(fetchImpl, request.url, request.init, {
410
603
  sleep: deps.sleep,
604
+ loopbackRequest,
411
605
  });
412
606
  const data = parsePlatformChatCompletionData(await res.text().catch(() => ""));
413
607
  if (!res.ok) {
@@ -429,9 +623,11 @@ export function createCliLocalRuntimeAdapter(
429
623
  return {
430
624
  model: providerConfig.model,
431
625
  complete: async (messages) => {
626
+ const requestedToolNames = resolveRequestedRuntimeToolNames({ agentConfig });
432
627
  const tools = buildOpenAiTools({
433
- toolNames: agentConfig.toolNames,
434
- env: deps.env,
628
+ requestedToolNames,
629
+ platformToolNames: agentConfig.toolNames,
630
+ hostRuntimeConfig,
435
631
  });
436
632
  const request = buildOpenAiCompatibleChatCompletionRequest({
437
633
  providerConfig,
@@ -440,6 +636,7 @@ export function createCliLocalRuntimeAdapter(
440
636
  });
441
637
  const res = await fetchWithTransientRetry(fetchImpl, request.url, request.init, {
442
638
  sleep: deps.sleep,
639
+ loopbackRequest,
443
640
  });
444
641
  const data = await res.json().catch(() => ({}));
445
642
  if (!res.ok) {
@@ -461,6 +658,7 @@ export function createCliLocalRuntimeAdapter(
461
658
  });
462
659
  const result = await executeLocalToolWithPolicy({
463
660
  env: deps.env,
661
+ hostRuntimeConfig,
464
662
  agentToolNames: activeAgentToolNames,
465
663
  call,
466
664
  executors: localToolExecutors,
@@ -1,24 +1,47 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
3
  import { executeLocalToolWithPolicy, resolveLocalToolPolicy } from "./localToolPolicy";
4
+ import { createHostRuntimeConfig } from "../agent-runtime";
5
+
6
+ function createCliHostRuntimeConfig(allowShell = false) {
7
+ return createHostRuntimeConfig({
8
+ surface: "cli-local",
9
+ workspaceBinding: "explicit-cwd",
10
+ allowShell,
11
+ });
12
+ }
4
13
 
5
14
  describe("CLI local tool policy", () => {
6
- test("allows execShell in explicit worktree shell mode", () => {
15
+ test("allows execShell when the host grants shell capability", () => {
7
16
  expect(resolveLocalToolPolicy({
8
- env: { NOLO_LOCAL_SHELL_MODE: "worktree" },
9
- agentToolNames: [],
17
+ env: {},
18
+ hostRuntimeConfig: createCliHostRuntimeConfig(true),
19
+ agentToolNames: ["execShell"],
10
20
  toolName: "execShell",
11
21
  })).toEqual({ allowed: true, toolName: "execShell" });
12
22
  });
13
23
 
14
- test("blocks execShell unless explicit shell mode is enabled", () => {
24
+ test("blocks execShell unless the host grants shell capability", () => {
15
25
  expect(resolveLocalToolPolicy({
16
26
  env: { NOLO_LOCAL_ALLOWED_TOOLS: "execShell" },
27
+ hostRuntimeConfig: createCliHostRuntimeConfig(),
17
28
  agentToolNames: ["execShell"],
18
29
  toolName: "execShell",
19
30
  })).toMatchObject({ allowed: false });
20
31
  });
21
32
 
33
+ test("blocks execShell when shell was not requested by the runtime", () => {
34
+ expect(resolveLocalToolPolicy({
35
+ env: {},
36
+ hostRuntimeConfig: createCliHostRuntimeConfig(true),
37
+ agentToolNames: [],
38
+ toolName: "execShell",
39
+ })).toMatchObject({
40
+ allowed: false,
41
+ reason: "execShell is not requested by this agent runtime.",
42
+ });
43
+ });
44
+
22
45
  test("requires both env allowlist and agent declaration", () => {
23
46
  expect(resolveLocalToolPolicy({
24
47
  env: { NOLO_LOCAL_ALLOWED_TOOLS: "readFile" },
@@ -36,6 +59,7 @@ describe("CLI local tool policy", () => {
36
59
  test("executes only registered tools after policy allows them", async () => {
37
60
  const result = await executeLocalToolWithPolicy({
38
61
  env: { NOLO_LOCAL_ALLOWED_TOOLS: "readFile" },
62
+ hostRuntimeConfig: createCliHostRuntimeConfig(),
39
63
  agentToolNames: ["readFile"],
40
64
  call: { id: "call-1", name: "readFile", arguments: "{\"path\":\"README.md\"}" },
41
65
  executors: {
@@ -127,8 +127,42 @@ export function resolveDaemonLogPath(env: ConnectorDaemonEnv) {
127
127
  return env.NOLO_CONNECT_LOG || join(homedir(), ".nolo", "logs", "connector.log");
128
128
  }
129
129
 
130
- export function buildDaemonCommand(cliEntrypointPath: string | undefined, fallbackEntrypointPath = import.meta.path) {
131
- return [process.execPath, cliEntrypointPath || fallbackEntrypointPath, "connect", "--ws"];
130
+ export function buildConnectorDaemonEnv(env: ConnectorDaemonEnv, cwd: string) {
131
+ return {
132
+ ...env,
133
+ NOLO_CONNECTOR_WORKDIR: env.NOLO_CONNECTOR_WORKDIR?.trim() || cwd,
134
+ NOLO_AGENT_WORKDIR: env.NOLO_AGENT_WORKDIR?.trim() || cwd,
135
+ NOLO_LOCAL_WORKTREE: env.NOLO_LOCAL_WORKTREE?.trim() || cwd,
136
+ };
137
+ }
138
+
139
+ function firstNonEmptyValue(...values: Array<string | undefined>) {
140
+ for (const value of values) {
141
+ if (typeof value === "string" && value.trim()) return value.trim();
142
+ }
143
+ return "";
144
+ }
145
+
146
+ export function buildDaemonCommand(
147
+ cliEntrypointPath: string | undefined,
148
+ env: ConnectorDaemonEnv,
149
+ fallbackEntrypointPath = import.meta.path,
150
+ ) {
151
+ const command = [process.execPath, cliEntrypointPath || fallbackEntrypointPath, "connect", "--ws"];
152
+ const serverUrl = firstNonEmptyValue(env.NOLO_SERVER, env.NOLO_SERVER_URL, env.BASE_URL);
153
+ const authToken = firstNonEmptyValue(
154
+ env.NOLO_MACHINE_API_KEY,
155
+ env.AUTH_TOKEN,
156
+ env.AUTH,
157
+ env.BENCHMARK_AUTH_TOKEN,
158
+ );
159
+ if (serverUrl) {
160
+ command.push("--server-url", serverUrl);
161
+ }
162
+ if (authToken) {
163
+ command.push("--api-key", authToken);
164
+ }
165
+ return command;
132
166
  }
133
167
 
134
168
  export function defaultSpawnDaemon(args: {
@@ -179,9 +213,9 @@ export async function runConnectorDaemon(deps: ConnectorDaemonDeps = {}) {
179
213
 
180
214
  const logPath = resolveDaemonLogPath(env);
181
215
  const result = (deps.spawnDaemon ?? defaultSpawnDaemon)({
182
- cmd: buildDaemonCommand(deps.cliEntrypointPath, deps.fallbackEntrypointPath),
216
+ cmd: buildDaemonCommand(deps.cliEntrypointPath, env, deps.fallbackEntrypointPath),
183
217
  cwd,
184
- env,
218
+ env: buildConnectorDaemonEnv(env, cwd),
185
219
  logPath,
186
220
  });
187
221
  output.write(`Connector daemon started${result.pid ? ` pid=${result.pid}` : ""}. Log: ${logPath}\n`);
@@ -24,22 +24,46 @@ function normalizePath(value: string) {
24
24
  return resolve(value.trim());
25
25
  }
26
26
 
27
+ function resolveExistingCandidate(
28
+ value: string | undefined,
29
+ checked: string[],
30
+ label: string
31
+ ) {
32
+ const trimmed = value?.trim();
33
+ if (!trimmed) return null;
34
+ const normalized = normalizePath(trimmed);
35
+ if (existsSync(normalized)) return normalized;
36
+ checked.push(`${label}=${normalized}`);
37
+ return null;
38
+ }
39
+
27
40
  export function resolveConnectorRunCwd(args: {
28
41
  env?: EnvLike;
29
42
  policy: MachineRunPermissionPolicy;
30
43
  fallbackCwd?: string;
31
44
  }) {
32
- const explicit =
33
- args.env?.NOLO_CONNECTOR_WORKDIR ||
34
- args.env?.NOLO_AGENT_WORKDIR ||
35
- args.env?.NOLO_LOCAL_WORKTREE ||
36
- "";
37
- const candidate = explicit || args.policy.writableRoots[0] || args.fallbackCwd || process.cwd();
38
- const cwd = normalizePath(candidate);
39
- if (!existsSync(cwd)) {
40
- throw new Error(`Connector run cwd does not exist: ${cwd}`);
45
+ const checked: string[] = [];
46
+ const candidates: Array<[string | undefined, string]> = [
47
+ [args.env?.NOLO_CONNECTOR_WORKDIR, "NOLO_CONNECTOR_WORKDIR"],
48
+ [args.env?.NOLO_AGENT_WORKDIR, "NOLO_AGENT_WORKDIR"],
49
+ [args.env?.NOLO_LOCAL_WORKTREE, "NOLO_LOCAL_WORKTREE"],
50
+ [args.policy.writableRoots[0], "writableRoots[0]"],
51
+ [args.fallbackCwd, "fallbackCwd"],
52
+ [process.env.PWD, "PWD"],
53
+ [process.cwd(), "process.cwd()"],
54
+ ];
55
+
56
+ for (const [value, label] of candidates) {
57
+ const resolved = resolveExistingCandidate(value, checked, label);
58
+ if (resolved) return resolved;
59
+ }
60
+
61
+ if (checked.length > 0) {
62
+ throw new Error(
63
+ `Connector run cwd does not exist. Checked: ${checked.join(", ")}`
64
+ );
41
65
  }
42
- return cwd;
66
+ throw new Error("Connector run cwd could not be resolved.");
43
67
  }
44
68
 
45
69
  async function runGit(
@@ -15,14 +15,18 @@ export function isConnectorWebSocketAuthError(error: unknown): error is Connecto
15
15
  export async function resolveConnectorWebSocketTarget(input: {
16
16
  serverUrl: string;
17
17
  machineId: string;
18
- headers: Record<string, string>;
19
- fetchImpl?: typeof fetch;
20
- }) {
18
+ connectorSurface?: string;
19
+ headers: Record<string, string>;
20
+ fetchImpl?: typeof fetch;
21
+ }) {
21
22
  const directWsUrl = new URL(input.serverUrl);
22
23
  directWsUrl.protocol = directWsUrl.protocol === "https:" ? "wss:" : "ws:";
23
24
  directWsUrl.pathname = "/api/connector/ws";
24
- directWsUrl.search = "";
25
- directWsUrl.searchParams.set("machineId", input.machineId);
25
+ directWsUrl.search = "";
26
+ directWsUrl.searchParams.set("machineId", input.machineId);
27
+ if (input.connectorSurface?.trim()) {
28
+ directWsUrl.searchParams.set("connectorSurface", input.connectorSurface.trim().toLowerCase());
29
+ }
26
30
 
27
31
  const probeUrl = new URL(input.serverUrl);
28
32
  probeUrl.pathname = "/api/connector/ws";