@superinterface/server 1.0.0 → 1.0.2

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/bin/index.cjs ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ const path = require('node:path')
3
+ const { register, require: tsxRequire } = require('tsx/cjs/api')
4
+
5
+ register()
6
+
7
+ const cliPath = path.resolve(__dirname, '../scripts/cli.ts')
8
+
9
+ tsxRequire(cliPath, __filename)
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "@superinterface/server",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
+ "bin": {
5
+ "superinterface-server": "./bin/index.cjs"
6
+ },
4
7
  "exports": {
5
8
  "./prisma/enums/*": "./prisma/enums/*",
6
9
  "./prisma/*": "./prisma/*",
@@ -131,6 +134,7 @@
131
134
  },
132
135
  "dependencies": {
133
136
  "@anthropic-ai/sdk": "^0.65.0",
137
+ "@clack/prompts": "^0.11.0",
134
138
  "@mendable/firecrawl-js": "^1.22.0",
135
139
  "@mistralai/mistralai": "^1.10.0",
136
140
  "@modelcontextprotocol/sdk": "^1.19.1",
@@ -141,18 +145,22 @@
141
145
  "@upstash/redis": "^1.35.5",
142
146
  "@upstash/workflow": "^0.2.20",
143
147
  "@vercel/functions": "^3.1.1",
148
+ "commander": "^14.0.1",
144
149
  "dayjs": "^1.11.18",
150
+ "dotenv": "^16.4.5",
145
151
  "groq-sdk": "^0.33.0",
146
152
  "p-iteration": "^1.1.8",
147
153
  "prisma-json-types-generator": "^3.6.2",
148
154
  "replicate": "^1.2.0",
149
155
  "supercompat": "^3.8.0",
150
- "uuid": "^13.0.0"
156
+ "tsx": "^4.11.0",
157
+ "uuid": "^13.0.0",
158
+ "zod": "^3.23.8"
151
159
  },
152
160
  "peerDependencies": {
153
161
  "next": "15.5.4",
154
- "react": "19.1.1",
155
- "react-dom": "19.1.1"
162
+ "react": "^19.1.0",
163
+ "react-dom": "^19.1.0"
156
164
  },
157
165
  "devDependencies": {
158
166
  "@eslint/eslintrc": "^3",
@@ -163,14 +171,9 @@
163
171
  "eslint-config-next": "15.5.4",
164
172
  "next": "15.5.4",
165
173
  "prisma": "^6.16.3",
166
- "react": "19.1.1",
167
- "react-dom": "19.1.1",
174
+ "react": "^19.1.0",
175
+ "react-dom": "^19.1.0",
168
176
  "tsc-alias": "^1.8.12",
169
- "typescript": "^5",
170
- "@clack/prompts": "^0.11.0",
171
- "dotenv": "^16.4.5",
172
- "tsx": "^4.11.0",
173
- "zod": "^3.23.8",
174
- "commander": "^14.0.1"
177
+ "typescript": "^5"
175
178
  }
176
179
  }
package/scripts/cli.ts CHANGED
@@ -4,16 +4,29 @@ import type { PrismaClient } from '@prisma/client'
4
4
  import { Command } from 'commander'
5
5
  import { createOrganization } from './commands/organizations/create'
6
6
  import { createOrganizationApiKey } from './commands/organizations/api-keys/create'
7
+ import { runServer } from './commands/run/server'
7
8
  import { CliError } from './utils/errors'
8
9
  import { ensureEnv, ensureDatabaseUrl } from './utils/env'
9
10
 
10
11
  const program = new Command()
11
- .name('superinterface-cli')
12
+ .name('@superinterface/server')
12
13
  .description('Administrative helpers for @superinterface/server')
13
14
  .showHelpAfterError('(add --help for additional information)')
14
15
 
15
16
  type ActionOptions<Options> = Options & { prisma: PrismaClient }
16
17
 
18
+ const handleCliError = (error: unknown) => {
19
+ if (error instanceof CliError) {
20
+ console.error(error.message)
21
+ if (error.cause) {
22
+ console.error(error.cause)
23
+ }
24
+ } else {
25
+ console.error(error)
26
+ }
27
+ process.exitCode = 1
28
+ }
29
+
17
30
  const withAction = <Options extends Record<string, unknown>>(
18
31
  action: (options: ActionOptions<Options>) => Promise<void>,
19
32
  ) => {
@@ -31,15 +44,7 @@ const withAction = <Options extends Record<string, unknown>>(
31
44
 
32
45
  await action(actionOptions)
33
46
  } catch (error) {
34
- if (error instanceof CliError) {
35
- console.error(error.message)
36
- if (error.cause) {
37
- console.error(error.cause)
38
- }
39
- } else {
40
- console.error(error)
41
- }
42
- process.exitCode = 1
47
+ handleCliError(error)
43
48
  } finally {
44
49
  await prisma?.$disconnect()
45
50
  }
@@ -67,18 +72,39 @@ organizationApiKeys
67
72
  .option('-n, --name <name>', 'API key display name')
68
73
  .action(withAction(createOrganizationApiKey))
69
74
 
75
+ const run = program.command('run').description('Run local services')
76
+
77
+ run
78
+ .command('server')
79
+ .description('Start the server runtime')
80
+ .option('-r, --runtime <runtime>', 'Runtime driver (default: next)', 'next')
81
+ .option('-p, --port <port>', 'Port to listen on')
82
+ .action(async (options: { runtime?: string; port?: string }) => {
83
+ try {
84
+ const portOption = options.port
85
+ let parsedPort: number | undefined
86
+
87
+ if (typeof portOption === 'string') {
88
+ const port = Number.parseInt(portOption, 10)
89
+ if (!Number.isInteger(port) || port <= 0) {
90
+ throw new CliError('Port must be a positive integer.')
91
+ }
92
+ parsedPort = port
93
+ }
94
+
95
+ await runServer({
96
+ runtime: options.runtime,
97
+ port: parsedPort,
98
+ })
99
+ } catch (error) {
100
+ handleCliError(error)
101
+ }
102
+ })
103
+
70
104
  if (process.argv.length <= 2) {
71
105
  program.outputHelp()
72
106
  }
73
107
 
74
108
  program.parseAsync(process.argv).catch((error) => {
75
- if (error instanceof CliError) {
76
- console.error(error.message)
77
- if (error.cause) {
78
- console.error(error.cause)
79
- }
80
- } else {
81
- console.error(error)
82
- }
83
- process.exitCode = 1
109
+ handleCliError(error)
84
110
  })
@@ -0,0 +1,146 @@
1
+ import { spawn } from 'node:child_process'
2
+ import fs from 'node:fs'
3
+ import { createRequire } from 'node:module'
4
+ import path from 'node:path'
5
+ import process from 'node:process'
6
+ import { fileURLToPath } from 'node:url'
7
+ import { CliError } from '../../utils/errors'
8
+ import { ensureDatabaseUrl, ensureEnv } from '../../utils/env'
9
+
10
+ const supportedRuntimes = ['next'] as const
11
+
12
+ type SupportedRuntime = (typeof supportedRuntimes)[number]
13
+
14
+ export type RunServerOptions = {
15
+ runtime?: string
16
+ port?: number
17
+ }
18
+
19
+ const isSupportedRuntime = (runtime: string): runtime is SupportedRuntime => {
20
+ return supportedRuntimes.includes(runtime as SupportedRuntime)
21
+ }
22
+
23
+ export const runServer = async ({
24
+ runtime = 'next',
25
+ port,
26
+ }: RunServerOptions) => {
27
+ const normalizedRuntime = runtime.toLowerCase()
28
+
29
+ if (!isSupportedRuntime(normalizedRuntime)) {
30
+ throw new CliError(`Unsupported runtime "${runtime}".`, {
31
+ cause: `Supported runtimes: ${supportedRuntimes.join(', ')}`,
32
+ })
33
+ }
34
+
35
+ await ensureEnv()
36
+ ensureDatabaseUrl()
37
+
38
+ switch (normalizedRuntime) {
39
+ case 'next':
40
+ await runNextRuntime({ port })
41
+ return
42
+ }
43
+ }
44
+
45
+ type RunCommandOptions = {
46
+ label: string
47
+ cwd: string
48
+ env?: NodeJS.ProcessEnv
49
+ }
50
+
51
+ const runCommand = async (
52
+ command: string,
53
+ args: string[],
54
+ { label, cwd, env }: RunCommandOptions,
55
+ ) => {
56
+ await new Promise<void>((resolve, reject) => {
57
+ const child = spawn(command, args, {
58
+ cwd,
59
+ stdio: 'inherit',
60
+ env,
61
+ })
62
+
63
+ const forwardSignal = (signal: NodeJS.Signals) => {
64
+ if (!child.killed) {
65
+ child.kill(signal)
66
+ }
67
+ }
68
+
69
+ const onSigint = () => forwardSignal('SIGINT')
70
+ const onSigterm = () => forwardSignal('SIGTERM')
71
+
72
+ const cleanup = () => {
73
+ process.off('SIGINT', onSigint)
74
+ process.off('SIGTERM', onSigterm)
75
+ }
76
+
77
+ process.on('SIGINT', onSigint)
78
+ process.on('SIGTERM', onSigterm)
79
+
80
+ child.once('error', (error) => {
81
+ cleanup()
82
+ reject(new CliError(`${label} failed to start.`, { cause: error }))
83
+ })
84
+
85
+ child.once('exit', (code, signal) => {
86
+ cleanup()
87
+
88
+ if (signal) {
89
+ process.kill(process.pid, signal)
90
+ return
91
+ }
92
+
93
+ if (typeof code === 'number' && code !== 0) {
94
+ reject(new CliError(`${label} exited with code ${code}.`))
95
+ return
96
+ }
97
+
98
+ resolve()
99
+ })
100
+ })
101
+ }
102
+
103
+ type RunNextRuntimeOptions = {
104
+ port?: number
105
+ }
106
+
107
+ const runNextRuntime = async ({ port }: RunNextRuntimeOptions) => {
108
+ const packageRoot = fileURLToPath(new URL('../../..', import.meta.url))
109
+ const require = createRequire(import.meta.url)
110
+
111
+ let nextBinPath: string
112
+ try {
113
+ const nextPackageJson = require.resolve('next/package.json', {
114
+ paths: [packageRoot],
115
+ })
116
+ nextBinPath = path.resolve(path.dirname(nextPackageJson), 'dist/bin/next')
117
+ } catch (error) {
118
+ throw new CliError('Next runtime is not installed or cannot be resolved.', {
119
+ cause: error,
120
+ })
121
+ }
122
+
123
+ const environment = {
124
+ ...process.env,
125
+ NODE_ENV: process.env.NODE_ENV ?? 'production',
126
+ }
127
+
128
+ const buildIdPath = path.join(packageRoot, '.next', 'BUILD_ID')
129
+ if (!fs.existsSync(buildIdPath)) {
130
+ throw new CliError(
131
+ 'No existing Next build found. Run `next build` before starting.',
132
+ )
133
+ }
134
+
135
+ const startArgs = ['start']
136
+
137
+ if (typeof port === 'number') {
138
+ startArgs.push('-p', String(port))
139
+ }
140
+
141
+ await runCommand(process.execPath, [nextBinPath, ...startArgs], {
142
+ label: 'Next runtime',
143
+ cwd: packageRoot,
144
+ env: environment,
145
+ })
146
+ }
@@ -10,15 +10,25 @@ export const ensureEnv = async () => {
10
10
  if (envLoaded) return
11
11
 
12
12
  const proc = process as ProcessWithLoadEnvFile
13
+ let handled = false
13
14
 
14
15
  if (typeof proc.loadEnvFile === 'function') {
15
- proc.loadEnvFile()
16
- envLoaded = true
17
- return
16
+ try {
17
+ proc.loadEnvFile()
18
+ handled = true
19
+ } catch (error) {
20
+ const code = (error as NodeJS.ErrnoException | undefined)?.code
21
+ if (code && code !== 'ENOENT') {
22
+ throw error
23
+ }
24
+ }
25
+ }
26
+
27
+ if (!handled) {
28
+ const dotenv = await import('dotenv')
29
+ dotenv.config({ path: '.env' })
18
30
  }
19
31
 
20
- const dotenv = await import('dotenv')
21
- dotenv.config({ path: '.env' })
22
32
  envLoaded = true
23
33
  }
24
34