@surph_ai/sdk 0.0.21 → 0.0.22
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/index.js +1 -1
- package/package.json +2 -1
- package/scaffold/tool/tools/index.ts +11 -4
- package/toolserver/chat.js +44 -5
- package/toolserver/routes/tool.js +5 -11
- package/types.d.ts +63 -0
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
// Your tools code goes here. When ready, export
|
|
2
2
|
// the tool name in exports at bottom of this file
|
|
3
3
|
// as shown in this demo code:
|
|
4
|
+
import type { ToolHandler, ToolContext, User } from '@surph_ai/sdk';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
6
|
+
type YourToolArgs = {
|
|
7
|
+
// your custom tool args goes here
|
|
8
|
+
};
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
type ToolArgs = YourToolArgs;
|
|
11
|
+
|
|
12
|
+
const handler: ToolHandler<ToolArgs> = async ({ args, user, context }) => {
|
|
13
|
+
// your custom tool code goes here
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default handler;
|
package/toolserver/chat.js
CHANGED
|
@@ -46497,6 +46497,33 @@ var require_customTool = __commonJS({
|
|
|
46497
46497
|
"server/shared/tools/customTool.js"(exports2, module2) {
|
|
46498
46498
|
var { SURPH_RPC_URL } = require_config();
|
|
46499
46499
|
var TOOL_TIMEOUT_MS = 60 * 1e3;
|
|
46500
|
+
var WEB_API_BASE = process.env.SURPH_ENV === "aws" ? "https://surph.ai" : "http://localhost:3000";
|
|
46501
|
+
async function refreshGoogleOauthViaWeb(userId) {
|
|
46502
|
+
const internalToken = process.env.SURPH_INTERNAL_TOKEN;
|
|
46503
|
+
if (!internalToken) {
|
|
46504
|
+
console.warn("[customTool] SURPH_INTERNAL_TOKEN not set; skipping Google token refresh");
|
|
46505
|
+
return null;
|
|
46506
|
+
}
|
|
46507
|
+
try {
|
|
46508
|
+
const res = await fetch(`${WEB_API_BASE}/api/oauth/google/refresh`, {
|
|
46509
|
+
method: "POST",
|
|
46510
|
+
headers: {
|
|
46511
|
+
"Content-Type": "application/json",
|
|
46512
|
+
"X-Internal-Token": internalToken
|
|
46513
|
+
},
|
|
46514
|
+
body: JSON.stringify({ userId })
|
|
46515
|
+
});
|
|
46516
|
+
if (!res.ok) {
|
|
46517
|
+
console.warn(`[customTool] refresh endpoint returned HTTP ${res.status}`);
|
|
46518
|
+
return null;
|
|
46519
|
+
}
|
|
46520
|
+
const json2 = await res.json().catch(() => null);
|
|
46521
|
+
return json2?.oauth || null;
|
|
46522
|
+
} catch (err) {
|
|
46523
|
+
console.warn("[customTool] refresh endpoint call failed:", err.message);
|
|
46524
|
+
return null;
|
|
46525
|
+
}
|
|
46526
|
+
}
|
|
46500
46527
|
function validateCustomTool(entry) {
|
|
46501
46528
|
if (!entry || typeof entry !== "object") {
|
|
46502
46529
|
return { ok: false, reason: "not an object" };
|
|
@@ -46556,11 +46583,19 @@ var require_customTool = __commonJS({
|
|
|
46556
46583
|
if (!Array.isArray(userTools)) return null;
|
|
46557
46584
|
return userTools.find((t) => t && t.name === name) || null;
|
|
46558
46585
|
}
|
|
46559
|
-
async function executeCustomTool({ name, args, user, tools }) {
|
|
46586
|
+
async function executeCustomTool({ name, args, user, tools, contextSlug = null }) {
|
|
46560
46587
|
const definition = findCustomToolDefinition(tools, name);
|
|
46561
46588
|
if (!definition) {
|
|
46562
46589
|
return { error: `Custom tool '${name}' not found on user profile` };
|
|
46563
46590
|
}
|
|
46591
|
+
let effectiveOauth = user?.oauth || null;
|
|
46592
|
+
if (user?.oauth?.google?.refreshToken) {
|
|
46593
|
+
const userId = user.id || user._id;
|
|
46594
|
+
if (userId) {
|
|
46595
|
+
const fresh = await refreshGoogleOauthViaWeb(userId);
|
|
46596
|
+
if (fresh) effectiveOauth = fresh;
|
|
46597
|
+
}
|
|
46598
|
+
}
|
|
46564
46599
|
const controller = new AbortController();
|
|
46565
46600
|
const timer = setTimeout(() => controller.abort(), TOOL_TIMEOUT_MS);
|
|
46566
46601
|
try {
|
|
@@ -46570,9 +46605,10 @@ var require_customTool = __commonJS({
|
|
|
46570
46605
|
headers: { "Content-Type": "application/json" },
|
|
46571
46606
|
body: JSON.stringify({
|
|
46572
46607
|
name,
|
|
46573
|
-
args: args || {},
|
|
46574
46608
|
tool: definition,
|
|
46575
|
-
|
|
46609
|
+
context: contextSlug || null,
|
|
46610
|
+
args: args || {},
|
|
46611
|
+
user: user ? { id: user.id || user._id, username: user.username, oauth: effectiveOauth || {} } : null
|
|
46576
46612
|
}),
|
|
46577
46613
|
signal: controller.signal
|
|
46578
46614
|
});
|
|
@@ -46658,7 +46694,7 @@ var require_executeTool = __commonJS({
|
|
|
46658
46694
|
// 'deleteFile'
|
|
46659
46695
|
]);
|
|
46660
46696
|
var executeTool = async (name, args, context = {}) => {
|
|
46661
|
-
const { runtime = "remote", session } = context;
|
|
46697
|
+
const { runtime = "remote", session, contextSlug = null } = context;
|
|
46662
46698
|
console.log(`
|
|
46663
46699
|
|
|
46664
46700
|
server/chat/executeTool.js: EXECUTE TOOL: ${session}
|
|
@@ -46709,7 +46745,7 @@ server/chat/executeTool.js: EXECUTE TOOL: ${session}
|
|
|
46709
46745
|
default: {
|
|
46710
46746
|
const userTools = session?.user?.tools;
|
|
46711
46747
|
if (findCustomToolDefinition(userTools, name)) {
|
|
46712
|
-
return await executeCustomTool({ name, args, user: session?.user, tools: userTools });
|
|
46748
|
+
return await executeCustomTool({ name, args, user: session?.user, tools: userTools, contextSlug });
|
|
46713
46749
|
}
|
|
46714
46750
|
return { error: `Unknown tool: ${name}` };
|
|
46715
46751
|
}
|
|
@@ -94676,6 +94712,9 @@ Always use absolute paths for file operations. When the user asks to create a fi
|
|
|
94676
94712
|
runtime: "remote",
|
|
94677
94713
|
projectPath: workingDirectory,
|
|
94678
94714
|
session,
|
|
94715
|
+
// Currently-selected context slug, forwarded to custom tools so their RPC
|
|
94716
|
+
// knows which context the invocation was scoped to.
|
|
94717
|
+
contextSlug: params?.context?.slug || params?.currentContextSlug || null,
|
|
94679
94718
|
// Passed to tools that emit real-time SSE frames (e.g. searchContext
|
|
94680
94719
|
// → `references.added`). Null on paths where no stream is bound.
|
|
94681
94720
|
outputStream
|
|
@@ -16,12 +16,12 @@ const loadTool = () => import(toolsPath).then((mod) => mod.default)
|
|
|
16
16
|
|
|
17
17
|
const router = Router()
|
|
18
18
|
|
|
19
|
-
const handler = async (
|
|
20
|
-
|
|
19
|
+
const handler = async (event, tool) => {
|
|
20
|
+
const { args, context, user } = event
|
|
21
21
|
let payload = null
|
|
22
22
|
let errorMsg = null
|
|
23
23
|
try {
|
|
24
|
-
payload = await tool(args)
|
|
24
|
+
payload = await tool({ args, context, user })
|
|
25
25
|
} catch (error) {
|
|
26
26
|
errorMsg = error instanceof Error ? error.message : String(error)
|
|
27
27
|
} finally {
|
|
@@ -88,22 +88,16 @@ router.get('/test', async (req, res, next) => {
|
|
|
88
88
|
})
|
|
89
89
|
|
|
90
90
|
router.post('/custom', async (req, res, next) => {
|
|
91
|
-
// console.log('TOOL CALL: ' + JSON.stringify(req.body))
|
|
92
91
|
try {
|
|
93
|
-
const { name, args, tool, user = null } = req.body;
|
|
94
|
-
// console.log('TOOL CALL: ' + name)
|
|
95
|
-
// console.log('TOOL ARGS: ' + JSON.stringify(args))
|
|
96
|
-
|
|
92
|
+
const { name, args, tool, user = null, context = null } = req.body;
|
|
97
93
|
if (process.env.SURPH_TOOL !== tool.slug) {
|
|
98
94
|
throw new Error(`Invalid tool ${tool.slug}. Check .env file.`)
|
|
99
95
|
}
|
|
100
96
|
|
|
101
97
|
const tools = await loadTool()
|
|
102
|
-
const response = await handler(args, tools)
|
|
103
|
-
// console.log('TOOL RESP: ' + JSON.stringify(response))
|
|
98
|
+
const response = await handler({ args, context, user }, tools)
|
|
104
99
|
res.json({ response })
|
|
105
100
|
} catch (error) {
|
|
106
|
-
// console.log('TOOL ERR: ' + error.message)
|
|
107
101
|
res.json({
|
|
108
102
|
response: {
|
|
109
103
|
success: false,
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Type definitions for @surph_ai/sdk
|
|
2
|
+
// These describe the runtime contract the tool server hands to each tool's default export.
|
|
3
|
+
|
|
4
|
+
export type OAuthProvider = 'google' | 'github' | (string & {});
|
|
5
|
+
|
|
6
|
+
export interface GoogleOAuth {
|
|
7
|
+
accessToken: string;
|
|
8
|
+
refreshToken?: string;
|
|
9
|
+
/** ISO-8601 timestamp for when accessToken expires. */
|
|
10
|
+
expiresAt: string;
|
|
11
|
+
/** OAuth scopes granted by the user. */
|
|
12
|
+
scopes: string[];
|
|
13
|
+
/** ISO-8601 timestamp for when the user first connected the provider. */
|
|
14
|
+
connectedAt: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UserOAuth {
|
|
18
|
+
google?: GoogleOAuth;
|
|
19
|
+
[provider: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface UserAuth {
|
|
23
|
+
type: OAuthProvider;
|
|
24
|
+
/** Provider-issued stable identifier for the user (e.g. Google 'sub'). */
|
|
25
|
+
id: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface User {
|
|
29
|
+
_id: string;
|
|
30
|
+
firstName?: string;
|
|
31
|
+
lastName?: string;
|
|
32
|
+
email: string;
|
|
33
|
+
username: string;
|
|
34
|
+
avatar?: string;
|
|
35
|
+
tags?: string[];
|
|
36
|
+
following?: string[];
|
|
37
|
+
notifications?: unknown[];
|
|
38
|
+
type?: 'claimed' | 'anonymous' | (string & {});
|
|
39
|
+
auth?: UserAuth;
|
|
40
|
+
timestamp?: string;
|
|
41
|
+
lastVisit?: string;
|
|
42
|
+
identifiers?: string[];
|
|
43
|
+
tools?: string[];
|
|
44
|
+
oauth?: UserOAuth;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Per-invocation context. Currently opaque — populated by the caller (chat
|
|
49
|
+
* server, deployed harness, etc.). Cast to a more specific shape inside the
|
|
50
|
+
* tool if you know what your caller sends.
|
|
51
|
+
*/
|
|
52
|
+
export type ToolContext = Record<string, unknown> | null;
|
|
53
|
+
|
|
54
|
+
export interface ToolHandlerInput<TArgs = Record<string, unknown>> {
|
|
55
|
+
args: TArgs;
|
|
56
|
+
context: ToolContext;
|
|
57
|
+
user: User;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type ToolHandler<
|
|
61
|
+
TArgs = Record<string, unknown>,
|
|
62
|
+
TResult = unknown,
|
|
63
|
+
> = (input: ToolHandlerInput<TArgs>) => Promise<TResult> | TResult;
|