surf-cli 2.12.0 → 2.13.1
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 +6 -0
- package/native/cli.cjs +33 -1
- package/native/host-helpers.cjs +33 -2
- package/native/host-sessions.cjs +1 -0
- package/native/host.cjs +109 -6
- package/native/kimi-client.cjs +912 -0
- package/native/mcp-server.cjs +10 -0
- package/native/private-state.cjs +1 -1
- package/native/workflow-definition.cjs +1 -0
- package/package.json +2 -2
- package/pi-extension/surf.ts +71 -9
- package/skills/README.md +6 -0
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
|
-
|
|
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`);
|
package/native/host-helpers.cjs
CHANGED
|
@@ -77,8 +77,23 @@ function formatToolContent(result, log = () => {}, options = {}) {
|
|
|
77
77
|
return text(output);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
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",
|
package/native/host-sessions.cjs
CHANGED
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
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
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
|
-
|
|
1349
|
+
if (extensionMsg.type === "AISTUDIO_QUERY") {
|
|
1247
1350
|
const { query, model, withPage, timeout } = extensionMsg;
|
|
1248
1351
|
|
|
1249
1352
|
queueAiRequest(async () => {
|