@surph_ai/cli 0.0.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.
@@ -0,0 +1,8 @@
1
+ const { Router } = require('express')
2
+ const router = Router()
3
+
4
+ router.get('/', (req, res, next) => {
5
+ res.render('index', {})
6
+ })
7
+
8
+ module.exports = router
@@ -0,0 +1,118 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const os = require('os')
4
+ const { Router, response } = require('express')
5
+ const { pathToFileURL } = require('url')
6
+ const manifest = require('../../../../../manifest.json')
7
+
8
+ const SESSION_DIR = path.resolve(__dirname, '../../../../../.surph')
9
+ const SESSION_FILE = path.join(SESSION_DIR, 'session.json')
10
+
11
+ const toolsPath = pathToFileURL(
12
+ path.resolve(__dirname, '../../../../../dist/tools/index.js')
13
+ ).href
14
+
15
+ const loadTool = () => import(toolsPath).then((mod) => mod.default)
16
+
17
+ const router = Router()
18
+
19
+ const handler = async (event, tool) => {
20
+ const { args, context, user } = event
21
+ let payload = null
22
+ let errorMsg = null
23
+ try {
24
+ payload = await tool({ args, context, user })
25
+ } catch (error) {
26
+ errorMsg = error instanceof Error ? error.message : String(error)
27
+ } finally {
28
+ const resp = { success: errorMsg === null ? true : false }
29
+
30
+ if (payload)
31
+ resp.payload = payload
32
+
33
+ if (errorMsg !== null)
34
+ resp.error = errorMsg
35
+
36
+ return resp
37
+ }
38
+ }
39
+
40
+ router.get('/', async (req, res, next) => {
41
+ const surphUser = req.headers['x-surph-user']
42
+ if (surphUser) {
43
+ try {
44
+ // console.log('SAVE SURPH USER: ' + surphUser)
45
+ fs.mkdirSync(SESSION_DIR, { recursive: true, mode: 0o700 })
46
+ fs.writeFileSync(
47
+ SESSION_FILE,
48
+ JSON.stringify({
49
+ userId: surphUser,
50
+ updatedAt: new Date().toISOString()
51
+ }, null, 2),
52
+ { mode: 0o600 }
53
+ )
54
+ } catch (error) {
55
+ console.warn('[sdk] session write failed:', error.message)
56
+ }
57
+ }
58
+
59
+ try {
60
+ res.json({ response: manifest })
61
+ } catch (error) {
62
+ res.json({
63
+ response: {
64
+ success: false,
65
+ error: error.message
66
+ }
67
+ })
68
+ }
69
+ })
70
+
71
+ router.get('/test', async (req, res, next) => {
72
+ try {
73
+ if (Object.keys(req.query).length === 0) {
74
+ throw new Error('No arguments provided.')
75
+ }
76
+
77
+ const context = req.query._context || null
78
+ const user = req.query._user || null
79
+ const params = { args: req.query, context, user }
80
+
81
+ const toolRunner = await loadTool()
82
+ const response = await handler(params, toolRunner)
83
+ res.json({ response })
84
+ } catch (error) {
85
+ res.json({
86
+ response: {
87
+ success: false,
88
+ error: error.message
89
+ }
90
+ })
91
+ }
92
+ })
93
+
94
+ router.post('/custom', async (req, res, next) => {
95
+ try {
96
+ const { name, args, tool, user = null, context = null } = req.body;
97
+ if (process.env.SURPH_TOOL !== tool.slug) {
98
+ throw new Error(`Invalid tool ${tool.slug}. Check .env file.`)
99
+ }
100
+
101
+ const toolRunner = await loadTool()
102
+ const response = await handler({ args, context, user }, toolRunner)
103
+ if (response.error) {
104
+ throw new Error(response.error);
105
+ }
106
+
107
+ res.json({ response })
108
+ } catch (error) {
109
+ res.json({
110
+ response: {
111
+ success: false,
112
+ error: error.message
113
+ }
114
+ })
115
+ }
116
+ })
117
+
118
+ module.exports = router
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html dir="ltr" lang="en-US">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
6
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
7
+ <meta http-equiv="Content-Language" content="en" />
8
+
9
+ <title>Surph AI</title>
10
+ <link rel="shortcut icon" href="https://lh3.googleusercontent.com/MpnmfR3zYPTojA03QbhGgwIHucmH5s4G2vbIlADYhODL488MQt_BuVztv9LYmJbhwkScYN0DIXuh4d10YMsLbWXT">
11
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
12
+ <link rel="stylesheet" href="https://cdn.turbo360-dev.com/admin/assets/scaffold/css/style.min.css" />
13
+ </head>
14
+
15
+ <body>
16
+ <div class="container pt-5">
17
+ <h1>Welcome to Surph AI</h1>
18
+ </div>
19
+ </body>
20
+ </html>
package/types.d.ts ADDED
@@ -0,0 +1,73 @@
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;
64
+
65
+ export interface Surph {
66
+ fetchUser(username: string): Promise<User>;
67
+ fetchContext(slug: string | ToolContext): Promise<unknown>;
68
+ searchContext(ctxSlug: string, query: string): Promise<unknown>;
69
+ search(query: string, ctxSlug?: string | null): Promise<unknown>;
70
+ toolHandler(tool: string, args?: Record<string, unknown>): Promise<unknown>;
71
+ }
72
+
73
+ export declare const surph: Surph;
package/utils/index.js ADDED
@@ -0,0 +1,275 @@
1
+ const fs = require('fs')
2
+ const colors = require('colors/safe')
3
+ const dotenv = require('dotenv')
4
+ const MODE_0666 = parseInt('0666', 8)
5
+
6
+ module.exports = {
7
+
8
+ /*
9
+ isTurboApp: function(){
10
+ var packageJson = null
11
+ var env = null
12
+ try {
13
+ packageJson = fs.readFileSync('package.json', 'utf8')
14
+ var json = JSON.parse(packageJson)
15
+ var dependencies = json.dependencies
16
+ if (dependencies.turbo360==null || dependencies.vertex360==null){
17
+ return {
18
+ isTurboApp: false,
19
+ missing: 'turbo360 and/or vertex 360',
20
+ reason: 'Missing turbo360 and/or vertex 360 dependencies'
21
+ }
22
+ }
23
+
24
+ var envData = fs.readFileSync('.env', 'utf8')
25
+ env = dotenv.parse(envData)
26
+ var turboEnv = env['TURBO_ENV']
27
+ if (turboEnv == null){
28
+ return {
29
+ isTurboApp: false,
30
+ missing: 'TURBO_ENV environment variable',
31
+ reason: 'Missing TURBO_ENV environment variable. Please set "TURBO_ENV=dev" in the .env file'
32
+ }
33
+ }
34
+
35
+ // this is definitely a turbo project
36
+ return {
37
+ isTurboApp: true,
38
+ turboEnv: turboEnv,
39
+ turbo360: dependencies.turbo360,
40
+ vertex360: dependencies.vertex360
41
+ }
42
+ }
43
+ catch(err){
44
+ // console.log('ERR - ' + err.message)
45
+ var missing = (packageJson == null) ? 'package.json' : '.env'
46
+ var reason = (packageJson == null) ? 'Missing package.json file' : 'Missing .env file. Please add .env file with "TURBO_ENV=dev" variable set.'
47
+ return {
48
+ isTurboApp: false,
49
+ missing: missing,
50
+ reason: reason
51
+ }
52
+ }
53
+ },
54
+
55
+ defaultTurboDeploy: function() {
56
+ var turboDeployString = '## Do NOT check this file into version control\n'
57
+ turboDeployString += '\nBUCKET=<AWS_HOST_BUCKET>'
58
+ turboDeployString += '\nAWS_KEY=<AWS_KEY>'
59
+ turboDeployString += '\nAWS_SECRET=<AWS_SECRET>'
60
+ turboDeployString += '\nAWS_ACCT=<AWS_ACCT>'
61
+ turboDeployString += '\nAWS_ROLE=<AWS_ROLE>'
62
+ turboDeployString += '\nAWS_REGION=<AWS_REGION>'
63
+ return turboDeployString
64
+ },
65
+ */
66
+
67
+ defaultEnv: function() {
68
+ var envString = '## Do NOT check this file into version control\n'
69
+ envString += '\nTURBO_ENV=dev'
70
+ envString += '\nTURBO_CDN=https://cdn.turbo360-vertex.com'
71
+ envString += '\nSESSION_SECRET=YOUR_SESSION_SECRET'
72
+ envString += '\nTURBO_APP_ID=<TURBO_APP_ID>'
73
+ envString += '\nTMP_DIR=/tmp'
74
+ envString += '\nTURBO_API_KEY=<TURBO_API_KEY>'
75
+ envString += '\nTURBO_APP_SLUG=<TURBO_APP_SLUG>'
76
+ return envString
77
+ },
78
+
79
+ validateEnv: function(env, fields) { // fields is an array
80
+ if (!fields) {
81
+ // required env values to deploy:
82
+ fields = [
83
+ {text:'TURBO_CDN', min:20, placeholder:'https://cdn.turbo360-vertex.com'},
84
+ {text:'TURBO_APP_SLUG', min:6, placeholder:'<TURBO_APP_SLUG>'},
85
+ {text:'TURBO_API_KEY', min:20, placeholder:'<TURBO_API_KEY>'},
86
+ {text:'TURBO_APP_ID', min:20, placeholder:'<TURBO_APP_ID>'}
87
+ ]
88
+ }
89
+
90
+ var missing = null
91
+ fields.forEach(function(field){
92
+ // e.g. - {text:"TURBO_APP_ID", min:20}
93
+ var key = field.text
94
+ if (env[key] == null){
95
+ missing = key
96
+ return
97
+ }
98
+
99
+ if (env[key] == field.placeholder){ // still default placeholder string
100
+ missing = key
101
+ return
102
+ }
103
+
104
+ if (env[key].length < field.min){ // doesn't meet min str length
105
+ missing = key
106
+ return
107
+ }
108
+ })
109
+
110
+ return missing
111
+ },
112
+
113
+ write: (path, str, mode = MODE_0666) => {
114
+ fs.writeFileSync(path, str, { mode })
115
+ // console.log(' \x1b[36mcreate\x1b[0m : ' + path)
116
+ },
117
+
118
+ // readFile: function(filepath, errMessage){
119
+ // return new Promise(function(resolve, reject){
120
+ // fs.readFile(filepath, 'utf8', function(err, data) {
121
+ // if (err) {
122
+ // var error = (errMessage) ? Error(errMessage) : err
123
+ // reject(error)
124
+ // return
125
+ // }
126
+
127
+ // resolve(data)
128
+ // })
129
+ // })
130
+ // },
131
+
132
+ readFile: (filepath, encoding = 'utf8') => {
133
+ // if (!encoding)
134
+ // encoding = 'utf8'
135
+
136
+ try {
137
+ const file = fs.readFileSync(filepath, encoding)
138
+ return file
139
+ } catch (error) {
140
+ return null
141
+ }
142
+ },
143
+
144
+ printError: (error) => {
145
+ if (error.response) {
146
+ const { data } = error.response
147
+ const errorText = `${colors.red(`\nError: `)} ${colors.white(`${data.error}\n`)}`
148
+ console.log(errorText)
149
+
150
+ return
151
+ }
152
+
153
+ const errorText = `${colors.red(`\nError: `)} ${colors.white(`${error.message}\n`)}`
154
+ console.log(errorText)
155
+ }
156
+
157
+ /*
158
+ postRequest: function(endpoint, params){
159
+ return new Promise(function(resolve, reject){
160
+ superagent
161
+ .post(endpoint)
162
+ .send(params)
163
+ .set('Accept', 'application/json')
164
+ .end(function(err, response){
165
+ // console.log('POST CALLBACK: ' + JSON.stringify(response))
166
+ if (err){
167
+ // console.log('err 1: ' + err)
168
+ if (!response){
169
+ reject(err)
170
+ return
171
+ }
172
+
173
+ if (response.text){
174
+ try {
175
+ var parsed = JSON.parse(response.text)
176
+ var message = parsed.message || 'Something went wrong. Please try again.'
177
+ reject(new Error(message))
178
+ return
179
+ } catch(parseError){
180
+
181
+ }
182
+ }
183
+
184
+ reject(err)
185
+ return
186
+ }
187
+
188
+ if (response.body.confirmation != 'success'){
189
+ // console.log('POST ERROR: ' + JSON.stringify(response))
190
+ reject(new Error(response.body.message))
191
+ return
192
+ }
193
+
194
+ resolve(response.body)
195
+ })
196
+ })
197
+ },
198
+
199
+ putRequest: function(endpoint, params){
200
+ return new Promise(function(resolve, reject){
201
+ superagent
202
+ .put(endpoint)
203
+ .send(params)
204
+ .set('Accept', 'application/json')
205
+ .end(function(err, response){
206
+ if (err){
207
+ if (!response){
208
+ reject(err)
209
+ return
210
+ }
211
+
212
+ if (response.text){
213
+ try {
214
+ var parsed = JSON.parse(response.text)
215
+ var message = parsed.message || 'Something went wrong. Please try again.'
216
+ reject(new Error(message))
217
+ return
218
+ } catch(parseError){
219
+
220
+ }
221
+ }
222
+
223
+ reject(err)
224
+ return
225
+ }
226
+
227
+ if (response.body.confirmation != 'success'){
228
+ // console.log('POST ERROR: ' + JSON.stringify(response))
229
+ reject(new Error(response.body.message))
230
+ return
231
+ }
232
+
233
+ resolve(response.body)
234
+ })
235
+ })
236
+ },
237
+
238
+ getRequest: function(endpoint){
239
+ return new Promise(function(resolve, reject){
240
+ superagent
241
+ .get(endpoint)
242
+ .set('Accept', 'application/json')
243
+ .end(function(err, response){
244
+ if (err){
245
+ if (!response){
246
+ reject(err)
247
+ return
248
+ }
249
+
250
+ if (response.text){
251
+ try {
252
+ var parsed = JSON.parse(response.text)
253
+ var message = parsed.message || 'Something went wrong. Please try again.'
254
+ reject(new Error(message))
255
+ return
256
+ } catch(parseError){
257
+
258
+ }
259
+ }
260
+
261
+ reject(err)
262
+ return
263
+ }
264
+
265
+ if (response.body.confirmation != 'success'){
266
+ reject(new Error(response.body.message))
267
+ return
268
+ }
269
+
270
+ resolve(response.body)
271
+ })
272
+ })
273
+ }
274
+ */
275
+ }