surf-cli 2.12.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -490,6 +490,12 @@ surf aistudio "explain quantum computing"
490
490
  surf aistudio "redteam this" --with-page # Include page context
491
491
  surf aistudio "quick answer" --model gemini-3-flash-preview # Model selection
492
492
 
493
+ # Kimi (queries kimi.com - Moonshot K-series - using your browser login)
494
+ surf kimi "explain quantum computing"
495
+ surf kimi "summarize" --with-page # Include page context
496
+ surf kimi "quick answer" --model thinking # Models: instant (default), thinking, high
497
+ surf kimi --validate # Check kimi.com UI and available models
498
+
493
499
  # AI Studio App Builder (generates full web apps from a prompt)
494
500
  surf aistudio.build "build a portfolio site"
495
501
  surf aistudio.build "todo app" --model gemini-3.1-pro-preview # Model override
package/native/cli.cjs CHANGED
@@ -355,6 +355,22 @@ const TOOLS = {
355
355
  { cmd: 'aistudio "quick answer" --model gemini-3-flash-preview', desc: "Model selection" },
356
356
  ]
357
357
  },
358
+ "kimi": {
359
+ desc: "Query Kimi AI (kimi.com, Moonshot K-series) using your browser session",
360
+ args: ["query"],
361
+ opts: {
362
+ "with-page": "Include current page context",
363
+ model: "Model: instant (default), thinking, high - or any model label shown in kimi.com's model picker",
364
+ timeout: "Timeout in seconds (default: 300)",
365
+ validate: "Check kimi.com UI and available models (no query sent)"
366
+ },
367
+ examples: [
368
+ { cmd: 'kimi "explain quantum computing"', desc: "Basic query" },
369
+ { cmd: 'kimi "summarize" --with-page', desc: "With page context" },
370
+ { cmd: 'kimi "deep dive" --model thinking', desc: "Try the Thinking model" },
371
+ { cmd: 'kimi --validate', desc: "Check UI and list available models" },
372
+ ]
373
+ },
358
374
  "aistudio.build": {
359
375
  desc: "Build an app via Google AI Studio App Builder (uses browser session)",
360
376
  args: ["query"],
@@ -2625,6 +2641,7 @@ const PRIMARY_ARG_MAP = {
2625
2641
  grok: "query",
2626
2642
  aistudio: "query",
2627
2643
  "aistudio.build": "query",
2644
+ kimi: "query",
2628
2645
  navigate: "url",
2629
2646
  go: "url",
2630
2647
  js: "code",
@@ -3311,6 +3328,10 @@ async function handleResponse(response) {
3311
3328
  data = { response: data };
3312
3329
  }
3313
3330
 
3331
+ if (tool === 'kimi' && typeof data === 'string') {
3332
+ data = { response: data };
3333
+ }
3334
+
3314
3335
  if (tool === "page.save" && typeof data?.html === "string") {
3315
3336
  const saveTo = path.resolve(outputPath);
3316
3337
  fs.mkdirSync(path.dirname(saveTo), { recursive: true });
@@ -3530,7 +3551,18 @@ async function handleResponse(response) {
3530
3551
  if (meta.length > 0) {
3531
3552
  console.error(`[${meta.join(" | ")}]`);
3532
3553
  }
3533
- } else if (tool === "perplexity" && data?.response) {
3554
+ } else if (tool === "kimi" && data?.response) {
3555
+ console.log(data.response);
3556
+ const meta = [];
3557
+ if (data.model) meta.push(data.model);
3558
+ if (data.partial) meta.push("partial");
3559
+ if (Number.isFinite(data.tookMs)) meta.push(`${(data.tookMs / 1000).toFixed(1)}s`);
3560
+ if (meta.length > 0) console.error(`\n[${meta.join(' | ')}]`);
3561
+ if (data.url) console.error(`URL: ${data.url}`);
3562
+ if (data.warnings?.length) {
3563
+ for (const w of data.warnings) console.warn(`Warning: ${w}`);
3564
+ }
3565
+ } else if (tool === "perplexity" && data?.response) {
3534
3566
  console.log(data.response);
3535
3567
  const meta = [];
3536
3568
  if (data.sources) meta.push(`${data.sources} sources`);
@@ -77,8 +77,23 @@ function formatToolContent(result, log = () => {}, options = {}) {
77
77
  return text(output);
78
78
  }
79
79
 
80
- // Handle Grok validation results
81
- if (result.authenticated !== undefined && result.models !== undefined && result.expectedModels !== undefined) {
80
+ // Handle Kimi validation results (must precede the Grok branch - same field names)
81
+ if (result.kimiValidate) {
82
+ let output = "## Kimi Validation Results\n\n";
83
+ output += `**Authenticated:** ${result.authenticated ? 'Yes' : 'No'}\n`;
84
+ output += `**Input Field:** ${result.inputFound ? 'Found' : 'Not Found'}\n`;
85
+ output += `**Send Button:** ${result.sendButtonFound ? 'Found' : 'Not Found'}\n\n`;
86
+ output += `**Available Models:** ${result.models.length > 0 ? result.models.join(', ') : 'None found'}\n`;
87
+ output += `**Expected Models:** ${result.expectedModels.join(', ')}\n\n`;
88
+ if (result.errors && result.errors.length > 0) {
89
+ output += `**Errors:**\n${result.errors.map(e => `- ${e}`).join('\n')}\n\n`;
90
+ }
91
+ output += `*Completed in ${result.tookMs}ms*`;
92
+ return text(output);
93
+ }
94
+
95
+ // Handle Grok validation results
96
+ if (result.authenticated !== undefined && result.models !== undefined && result.expectedModels !== undefined) {
82
97
  let output = "## Grok Validation Results\n\n";
83
98
  output += `**Authenticated:** ${result.authenticated ? 'Yes' : 'No'}\n`;
84
99
  output += `**Premium:** ${result.premium ? 'Yes' : 'No'}\n`;
@@ -1200,6 +1215,22 @@ function mapToolToMessage(tool, args, tabId) {
1200
1215
  ...baseMsg,
1201
1216
  };
1202
1217
  }
1218
+ case "kimi":
1219
+ if (a.validate) {
1220
+ return {
1221
+ type: "KIMI_VALIDATE",
1222
+ ...baseMsg
1223
+ };
1224
+ }
1225
+ if (!a.query) throw new Error("query required");
1226
+ return {
1227
+ type: "KIMI_QUERY",
1228
+ query: a.query,
1229
+ model: a.model,
1230
+ withPage: a["with-page"],
1231
+ timeout: a.timeout ? parseInt(a.timeout, 10) * 1000 : 300000,
1232
+ ...baseMsg
1233
+ };
1203
1234
  case "window.new":
1204
1235
  return {
1205
1236
  type: "WINDOW_NEW",
@@ -22,6 +22,7 @@ const PROVIDER_DEFAULT_TIMEOUT_SECONDS = {
22
22
  chatgpt: 2700,
23
23
  gemini: 300,
24
24
  grok: 300,
25
+ kimi: 300,
25
26
  perplexity: 120,
26
27
  "oracle.result": 300,
27
28
  "playbook.run": 600,
package/native/host.cjs CHANGED
@@ -12,6 +12,7 @@ const chatgptClient = require("./chatgpt-client.cjs");
12
12
  const geminiClient = require("./gemini-client.cjs");
13
13
  const perplexityClient = require("./perplexity-client.cjs");
14
14
  const grokClient = require("./grok-client.cjs");
15
+ const kimiClient = require("./kimi-client.cjs");
15
16
  const aistudioClient = require("./aistudio-client.cjs");
16
17
  const aistudioBuild = require("./aistudio-build.cjs");
17
18
  const { mapToolToMessage, mapComputerAction, formatToolContent, formatToolError, buildProviderUploadMessage } = require("./host-helpers.cjs");
@@ -1237,13 +1238,115 @@ function handleToolRequest(msg, socket, requestContext = requestStorage.getStore
1237
1238
  }
1238
1239
  sendToolResponse(socket, originalId, result, null);
1239
1240
  }).catch((err) => {
1240
- sendToolResponse(socket, originalId, null, err.message);
1241
- });
1242
-
1243
- return;
1244
- }
1241
+ sendToolResponse(socket, originalId, null, err.message);
1242
+ });
1243
+
1244
+ return;
1245
+ }
1246
+
1247
+ if (extensionMsg.type === "KIMI_QUERY") {
1248
+ const { query, model, withPage, timeout } = extensionMsg;
1249
+
1250
+ queueAiRequest(async () => {
1251
+ // 1. Get page context if requested
1252
+ let pageContext = null;
1253
+ if (withPage) {
1254
+ const pageResult = await requestCallExtension(
1255
+ requestContext,
1256
+ "get_page_text",
1257
+ { type: "GET_PAGE_TEXT", tabId: extensionMsg.tabId },
1258
+ 45000,
1259
+ );
1260
+ if (pageResult && !pageResult.error) {
1261
+ pageContext = {
1262
+ url: pageResult.url,
1263
+ text: pageResult.text || pageResult.pageContent || ""
1264
+ };
1265
+ }
1266
+ }
1267
+
1268
+ // 2. Build full prompt
1269
+ let fullPrompt = query || "";
1270
+ if (pageContext) {
1271
+ fullPrompt = `Page: ${pageContext.url}\n\n${pageContext.text}\n\n---\n\n${fullPrompt}`;
1272
+ }
1273
+
1274
+ // 3. Call Kimi client (uses generic browser primitives, no provider CDP types)
1275
+ const result = await kimiClient.query({
1276
+ prompt: fullPrompt,
1277
+ extractionPrompt: query,
1278
+ signal: requestContext.signal,
1279
+ model: model,
1280
+ timeout: timeout || 300000,
1281
+ createTab: () => requestCallExtension(
1282
+ requestContext,
1283
+ "create_tab",
1284
+ { type: "NEW_TAB", url: "https://www.kimi.com/" },
1285
+ ),
1286
+ closeTab: (tabIdToClose) => requestCallExtension(requestContext, "close_tab", { type: "CLOSE_TAB", tabId: tabIdToClose }, 45000, true),
1287
+ jsEval: (tabId, code) => requestCallExtension(
1288
+ requestContext,
1289
+ "js_eval",
1290
+ { type: "EXECUTE_JAVASCRIPT", tabId, code },
1291
+ ),
1292
+ log: (msg) => log(`[kimi] ${msg}`)
1293
+ });
1294
+
1295
+ return result;
1296
+ }).then((result) => {
1297
+ const response = {
1298
+ response: result.response,
1299
+ model: result.model,
1300
+ tookMs: result.tookMs
1301
+ };
1302
+ if (result.partial) {
1303
+ response.partial = true;
1304
+ }
1305
+ if (result.warnings && result.warnings.length > 0) {
1306
+ response.warnings = result.warnings;
1307
+ }
1308
+ if (result.modelSelectionFailed) {
1309
+ response.modelSelectionFailed = true;
1310
+ }
1311
+ if (result.url) {
1312
+ response.url = result.url;
1313
+ }
1314
+ sendToolResponse(socket, originalId, response, null);
1315
+ }).catch((err) => {
1316
+ sendToolResponse(socket, originalId, null, err.message);
1317
+ });
1318
+
1319
+ return;
1320
+ }
1321
+
1322
+ if (extensionMsg.type === "KIMI_VALIDATE") {
1323
+ queueAiRequest(async () => {
1324
+ const result = await kimiClient.validate({
1325
+ signal: requestContext.signal,
1326
+ createTab: () => requestCallExtension(
1327
+ requestContext,
1328
+ "create_tab",
1329
+ { type: "NEW_TAB", url: "https://www.kimi.com/" },
1330
+ ),
1331
+ closeTab: (tabIdToClose) => requestCallExtension(requestContext, "close_tab", { type: "CLOSE_TAB", tabId: tabIdToClose }, 45000, true),
1332
+ jsEval: (tabId, code) => requestCallExtension(
1333
+ requestContext,
1334
+ "js_eval",
1335
+ { type: "EXECUTE_JAVASCRIPT", tabId, code },
1336
+ ),
1337
+ log: (msg) => log(`[kimi] ${msg}`)
1338
+ });
1339
+ return result;
1340
+ }).then((result) => {
1341
+ sendToolResponse(socket, originalId, result, null);
1342
+ }).catch((err) => {
1343
+ sendToolResponse(socket, originalId, null, err.message);
1344
+ });
1345
+
1346
+ return;
1347
+ }
1245
1348
 
1246
- if (extensionMsg.type === "AISTUDIO_QUERY") {
1349
+ if (extensionMsg.type === "AISTUDIO_QUERY") {
1247
1350
  const { query, model, withPage, timeout } = extensionMsg;
1248
1351
 
1249
1352
  queueAiRequest(async () => {