@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 CHANGED
@@ -321,7 +321,7 @@ program.command('publish')
321
321
  })
322
322
 
323
323
  const { payload } = await resp.json()
324
- console.log('\nSurph tool registered. ')
324
+ console.log(`\nSurph tool registered. To view, visit:\n\nhttps://surph.ai/tool/${slug}\n`)
325
325
  return
326
326
  }
327
327
 
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@surph_ai/sdk",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "description": "",
5
5
  "main": "index.js",
6
+ "types": "./types.d.ts",
6
7
  "publishConfig": {
7
8
  "access": "public"
8
9
  },
@@ -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
- const demoTool = (args: Record<string, any>) => {
6
- return `This is a sample tool: ${JSON.stringify(args)}. Remove this tool and replace it with your own.`
7
- }
6
+ type YourToolArgs = {
7
+ // your custom tool args goes here
8
+ };
8
9
 
9
- export default demoTool
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;
@@ -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
- user: user ? { id: user.id || user._id, username: user.username } : null
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 (args, tool) => {
20
- console.log('HANDLER: ' + JSON.stringify(Object.keys(tool)))
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;