spaps 0.7.2 → 0.7.4
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/AI_TOOLS.json +10 -11
- package/README.md +267 -110
- package/assets/local-runtime/Dockerfile +28 -0
- package/assets/local-runtime/alembic/env.py +101 -0
- package/assets/local-runtime/alembic/path_bootstrap.py +71 -0
- package/assets/local-runtime/alembic/versions/000000000001_baseline_consolidated_schema.py +1076 -0
- package/assets/local-runtime/alembic/versions/000000000002_fix_column_types_to_match_prod.py +83 -0
- package/assets/local-runtime/alembic/versions/000000000003_fix_email_template_key_uniqueness.py +49 -0
- package/assets/local-runtime/alembic/versions/000000000004_add_hold_duration_minutes_to_dayrate_config.py +30 -0
- package/assets/local-runtime/alembic/versions/000000000005_resource_scoped_entitlements.py +77 -0
- package/assets/local-runtime/alembic/versions/000000000006_cfo_rbac_add_is_admin.py +37 -0
- package/assets/local-runtime/alembic/versions/000000000007_agent_approvals.py +158 -0
- package/assets/local-runtime/alembic/versions/000000000008_add_company_id_to_cfo_connections.py +35 -0
- package/assets/local-runtime/alembic/versions/000000000009_tx_signing.py +62 -0
- package/assets/local-runtime/alembic/versions/000000000010_affiliate_referrals.py +235 -0
- package/assets/local-runtime/alembic/versions/000000000011_checkin_call_booking.py +137 -0
- package/assets/local-runtime/alembic/versions/000000000012_subscription_application_scoping.py +55 -0
- package/assets/local-runtime/alembic/versions/000000000013_refresh_token_anomaly_context.py +61 -0
- package/assets/local-runtime/alembic/versions/000000000014_buildooor_dayrate_hire_schedule.py +39 -0
- package/assets/local-runtime/alembic/versions/000000000015_support_telemetry_platform.py +112 -0
- package/assets/local-runtime/alembic/versions/000000000016_issue_reporting_platform.py +54 -0
- package/assets/local-runtime/alembic/versions/000000000017_issue_reporting_platform_import_tracking.py +44 -0
- package/assets/local-runtime/alembic/versions/000000000018_authorization_policy_engine.py +76 -0
- package/assets/local-runtime/alembic.ini +47 -0
- package/assets/local-runtime/docker-compose.yml +61 -0
- package/assets/local-runtime/manifest.json +8 -0
- package/assets/local-runtime/scripts/container-entrypoint.sh +13 -0
- package/assets/local-runtime/scripts/fetch-prod-db.sh +112 -0
- package/assets/local-runtime/scripts/run-migrations.sh +96 -0
- package/package.json +5 -4
- package/src/ai-helper.js +176 -234
- package/src/ai-tool-spec.js +52 -20
- package/src/auth/api-key.js +119 -0
- package/src/auth/client-id.js +136 -0
- package/src/auth/client.js +169 -0
- package/src/auth/credentials.js +110 -0
- package/src/auth/device-flow.js +159 -0
- package/src/auth/env.js +57 -0
- package/src/auth/handlers.js +462 -0
- package/src/auth/http.js +74 -0
- package/src/cli-dispatcher.js +155 -24
- package/src/docs-system.js +7 -7
- package/src/error-handler.js +42 -0
- package/src/fixture-kernel.js +1143 -0
- package/src/handlers.js +252 -15
- package/src/help-system.js +3 -1
- package/src/local-runtime.js +258 -0
- package/src/local-server.js +597 -199
- package/src/project-scaffolder.js +441 -0
package/src/ai-helper.js
CHANGED
|
@@ -1,273 +1,215 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SPAPS AI Agent Helper
|
|
3
|
-
* Provides AI-friendly outputs and quick commands
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const chalk = require('chalk');
|
|
7
|
-
|
|
8
1
|
const { DEFAULT_PORT } = require('./config');
|
|
2
|
+
const { getServerRuntime, readSelfServicePassword } = require('./local-runtime');
|
|
3
|
+
|
|
4
|
+
function buildProvisioningCommand(template, port) {
|
|
5
|
+
return `SELF_SERVICE_PASSWORD=your-password npx spaps create demo-${template === 'node' ? 'api' : 'app'} --template ${template} --port ${port}`;
|
|
6
|
+
}
|
|
9
7
|
|
|
10
|
-
function
|
|
8
|
+
function buildLocalModeInstructions(runtime) {
|
|
11
9
|
return {
|
|
12
10
|
success: true,
|
|
11
|
+
server: {
|
|
12
|
+
running: true,
|
|
13
|
+
url: runtime.url,
|
|
14
|
+
docs: runtime.docs,
|
|
15
|
+
local_mode_active: true,
|
|
16
|
+
environment: runtime.local_mode.environment,
|
|
17
|
+
spaps_local_mode_env: runtime.local_mode.spaps_local_mode_env,
|
|
18
|
+
},
|
|
19
|
+
auth: {
|
|
20
|
+
local_mode: true,
|
|
21
|
+
api_key_required: false,
|
|
22
|
+
mode_source: '/health/local-mode',
|
|
23
|
+
test_users: runtime.local_mode.test_users,
|
|
24
|
+
test_application: runtime.local_mode.test_application,
|
|
25
|
+
hints: runtime.local_mode.hints,
|
|
26
|
+
},
|
|
27
|
+
summary: [
|
|
28
|
+
'Install the SDK with `npm install spaps-sdk`.',
|
|
29
|
+
`Point the client at ${runtime.url}.`,
|
|
30
|
+
'Use the local-mode hints from `/health/local-mode` if you need a specific test persona.',
|
|
31
|
+
],
|
|
13
32
|
instructions: {
|
|
14
|
-
|
|
15
|
-
description:
|
|
16
|
-
command:
|
|
17
|
-
verify: "npm list spaps-sdk"
|
|
33
|
+
install_sdk: {
|
|
34
|
+
description: 'Install the SDK',
|
|
35
|
+
command: 'npm install spaps-sdk',
|
|
18
36
|
},
|
|
19
|
-
|
|
20
|
-
description:
|
|
21
|
-
filename:
|
|
22
|
-
content: `const {
|
|
23
|
-
|
|
24
|
-
async function test() {
|
|
25
|
-
const sdk = new SweetPotatoSDK({ apiUrl: 'http://localhost:${port}' });
|
|
37
|
+
create_client: {
|
|
38
|
+
description: 'Create a client for local mode',
|
|
39
|
+
filename: 'test-spaps.js',
|
|
40
|
+
content: `const { SPAPSClient } = require('spaps-sdk');
|
|
26
41
|
|
|
27
|
-
|
|
28
|
-
const auth = await sdk.auth.signInWithPassword({ email: 'test@example.com', password: 'password' });
|
|
29
|
-
console.log('✅ Login successful:', auth.user.email);
|
|
42
|
+
const spaps = new SPAPSClient({ apiUrl: '${runtime.url}' });
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
console.log(
|
|
34
|
-
|
|
35
|
-
return { success: true, user };
|
|
44
|
+
async function main() {
|
|
45
|
+
const status = await fetch('${runtime.url}/health/local-mode').then((res) => res.json());
|
|
46
|
+
console.log(status.data);
|
|
36
47
|
}
|
|
37
48
|
|
|
38
|
-
|
|
49
|
+
main().catch(console.error);
|
|
50
|
+
`,
|
|
51
|
+
},
|
|
52
|
+
verify_server: {
|
|
53
|
+
description: 'Confirm local mode status',
|
|
54
|
+
command: `curl -s ${runtime.url}/health/local-mode | jq '.'`,
|
|
39
55
|
},
|
|
40
|
-
step3: {
|
|
41
|
-
description: "Run test",
|
|
42
|
-
command: "node test-spaps.js",
|
|
43
|
-
expected_output: {
|
|
44
|
-
success: true,
|
|
45
|
-
user: {
|
|
46
|
-
id: "00000000-0000-0000-0000-000000000002",
|
|
47
|
-
email: "test@example.com"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
56
|
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function buildProvisionedModeInstructions(runtime, port) {
|
|
61
|
+
const hasPassword = Boolean(readSelfServicePassword());
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
success: true,
|
|
65
|
+
server: {
|
|
66
|
+
running: true,
|
|
67
|
+
url: runtime.url,
|
|
68
|
+
docs: runtime.docs,
|
|
69
|
+
local_mode_active: false,
|
|
70
|
+
environment: runtime.local_mode.environment,
|
|
71
|
+
spaps_local_mode_env: runtime.local_mode.spaps_local_mode_env,
|
|
72
|
+
},
|
|
73
|
+
auth: {
|
|
74
|
+
local_mode: false,
|
|
75
|
+
api_key_required: true,
|
|
76
|
+
mode_source: '/health/local-mode',
|
|
77
|
+
header: 'X-API-Key',
|
|
78
|
+
env: 'SPAPS_API_KEY',
|
|
79
|
+
self_service_password_env: 'SELF_SERVICE_PASSWORD',
|
|
80
|
+
},
|
|
81
|
+
summary: [
|
|
82
|
+
'Install the SDK with `npm install spaps-sdk`.',
|
|
83
|
+
'Provision a local application before testing authenticated flows.',
|
|
84
|
+
`Send \`X-API-Key\` when /health/local-mode reports \`local_mode_active: false\`.`,
|
|
85
|
+
],
|
|
86
|
+
instructions: {
|
|
87
|
+
install_sdk: {
|
|
88
|
+
description: 'Install the SDK',
|
|
89
|
+
command: 'npm install spaps-sdk',
|
|
58
90
|
},
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
91
|
+
provision_app: {
|
|
92
|
+
description: hasPassword
|
|
93
|
+
? 'Provision a local browser app'
|
|
94
|
+
: 'Set SELF_SERVICE_PASSWORD, then provision a local browser app',
|
|
95
|
+
command: buildProvisioningCommand('react', port),
|
|
64
96
|
},
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
97
|
+
create_client: {
|
|
98
|
+
description: 'Create a client that uses a provisioned key',
|
|
99
|
+
filename: 'test-spaps.js',
|
|
100
|
+
content: `const { createServerClient } = require('spaps-sdk');
|
|
101
|
+
|
|
102
|
+
const apiUrl = process.env.SPAPS_API_URL || '${runtime.url}';
|
|
103
|
+
const apiKey = process.env.SPAPS_API_KEY;
|
|
104
|
+
|
|
105
|
+
if (!apiKey) {
|
|
106
|
+
throw new Error('Set SPAPS_API_KEY before testing against a non-local-mode server.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const spaps = createServerClient(apiKey, { apiUrl });
|
|
110
|
+
|
|
111
|
+
async function main() {
|
|
112
|
+
const result = await spaps.auth.signInWithPassword({
|
|
113
|
+
email: 'user@example.com',
|
|
114
|
+
password: 'correct-horse-battery-staple',
|
|
115
|
+
});
|
|
116
|
+
console.log(result.user.id);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
main().catch(console.error);
|
|
120
|
+
`,
|
|
70
121
|
},
|
|
71
|
-
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
body: { price_id: "string", success_url: "string", cancel_url: "string" },
|
|
75
|
-
response: { id: "string", url: "string", status: "string" }
|
|
122
|
+
verify_server: {
|
|
123
|
+
description: 'Confirm whether the server is in local mode',
|
|
124
|
+
command: `curl -s ${runtime.url}/health/local-mode | jq '.'`,
|
|
76
125
|
},
|
|
77
|
-
|
|
78
|
-
method: "GET",
|
|
79
|
-
path: "/api/usage/balance",
|
|
80
|
-
headers: { Authorization: "Bearer TOKEN" },
|
|
81
|
-
response: { balance: "number", currency: "string" }
|
|
82
|
-
}
|
|
83
|
-
],
|
|
84
|
-
test_commands: {
|
|
85
|
-
health_check: `curl http://localhost:${port}/health`,
|
|
86
|
-
login: `curl -X POST http://localhost:${port}/api/auth/login -H "Content-Type: application/json" -d '{"email":"test@example.com","password":"password"}'`,
|
|
87
|
-
with_sdk: `node -e "const {SweetPotatoSDK}=require('spaps-sdk');const sdk=new SweetPotatoSDK({apiUrl:'http://localhost:${port}'});sdk.auth.signInWithPassword({email:'test@example.com',password:'password'}).then(r=>console.log(JSON.stringify(r.user))).catch(console.error)"`
|
|
88
|
-
}
|
|
126
|
+
},
|
|
89
127
|
};
|
|
90
128
|
}
|
|
91
129
|
|
|
92
|
-
function
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
return new Promise((resolve) => {
|
|
96
|
-
const options = {
|
|
97
|
-
hostname: 'localhost',
|
|
98
|
-
port: port,
|
|
99
|
-
path: '/health',
|
|
100
|
-
method: 'GET',
|
|
101
|
-
timeout: 1000
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const req = http.request(options, (res) => {
|
|
105
|
-
let data = '';
|
|
106
|
-
res.on('data', chunk => data += chunk);
|
|
107
|
-
res.on('end', () => {
|
|
108
|
-
try {
|
|
109
|
-
const parsed = JSON.parse(data);
|
|
110
|
-
resolve({
|
|
111
|
-
running: true,
|
|
112
|
-
port: port,
|
|
113
|
-
health: parsed,
|
|
114
|
-
url: `http://localhost:${port}`,
|
|
115
|
-
docs: `http://localhost:${port}/docs`
|
|
116
|
-
});
|
|
117
|
-
} catch {
|
|
118
|
-
resolve({ running: true, port: port, error: 'Invalid response' });
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
req.on('error', () => {
|
|
124
|
-
resolve({
|
|
125
|
-
running: false,
|
|
126
|
-
port: port,
|
|
127
|
-
message: 'Server not running',
|
|
128
|
-
start_command: `npx spaps local --port ${port}`
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
req.on('timeout', () => {
|
|
133
|
-
req.destroy();
|
|
134
|
-
resolve({
|
|
135
|
-
running: false,
|
|
136
|
-
port: port,
|
|
137
|
-
message: 'Server timeout',
|
|
138
|
-
start_command: `npx spaps local --port ${port}`
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
req.end();
|
|
143
|
-
});
|
|
144
|
-
}
|
|
130
|
+
async function getQuickStartInstructions(port = DEFAULT_PORT) {
|
|
131
|
+
const runtime = await getServerRuntime({ port });
|
|
145
132
|
|
|
146
|
-
|
|
147
|
-
const results = [];
|
|
148
|
-
|
|
149
|
-
// Check server
|
|
150
|
-
const status = await getServerStatus(port);
|
|
151
|
-
results.push({
|
|
152
|
-
test: 'server_status',
|
|
153
|
-
success: status.running,
|
|
154
|
-
details: status
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
if (!status.running) {
|
|
133
|
+
if (!runtime.running) {
|
|
158
134
|
return {
|
|
159
135
|
success: false,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
136
|
+
server: runtime,
|
|
137
|
+
summary: [
|
|
138
|
+
`Start the local server with \`npx spaps local --port ${port}\`.`,
|
|
139
|
+
'Provision a starter application after the server is healthy.',
|
|
140
|
+
],
|
|
141
|
+
instructions: {
|
|
142
|
+
start_server: {
|
|
143
|
+
description: 'Start the local server',
|
|
144
|
+
command: `npx spaps local --port ${port}`,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
163
147
|
};
|
|
164
148
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
res.on('end', () => {
|
|
190
|
-
try {
|
|
191
|
-
resolve(JSON.parse(data));
|
|
192
|
-
} catch {
|
|
193
|
-
reject(new Error('Invalid JSON response'));
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
req.on('error', reject);
|
|
199
|
-
req.write(postData);
|
|
200
|
-
req.end();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
results.push({
|
|
204
|
-
test: 'login_endpoint',
|
|
205
|
-
success: true,
|
|
206
|
-
response: loginResult
|
|
207
|
-
});
|
|
208
|
-
} catch (error) {
|
|
209
|
-
results.push({
|
|
210
|
-
test: 'login_endpoint',
|
|
149
|
+
|
|
150
|
+
if (runtime.local_mode.active) {
|
|
151
|
+
return buildLocalModeInstructions(runtime);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return buildProvisionedModeInstructions(runtime, port);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function getServerStatus(port = DEFAULT_PORT) {
|
|
158
|
+
return getServerRuntime({ port });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function runQuickTest(port = DEFAULT_PORT) {
|
|
162
|
+
const runtime = await getServerRuntime({ port });
|
|
163
|
+
const results = [
|
|
164
|
+
{
|
|
165
|
+
test: 'server_status',
|
|
166
|
+
success: runtime.running,
|
|
167
|
+
details: runtime,
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
if (!runtime.running) {
|
|
172
|
+
return {
|
|
211
173
|
success: false,
|
|
212
|
-
|
|
213
|
-
|
|
174
|
+
summary: '0/1 tests passed',
|
|
175
|
+
results,
|
|
176
|
+
next_steps: [`Start the server with: npx spaps local --port ${port}`],
|
|
177
|
+
};
|
|
214
178
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
// Try SDK login
|
|
226
|
-
try {
|
|
227
|
-
const { SPAPSClient } = require('spaps-sdk');
|
|
228
|
-
const spaps = new SPAPSClient({ apiUrl: `http://localhost:${port}` });
|
|
229
|
-
const { data } = await spaps.login('test@example.com', 'password');
|
|
230
|
-
|
|
231
|
-
results.push({
|
|
232
|
-
test: 'sdk_login',
|
|
233
|
-
success: true,
|
|
234
|
-
user: data.user
|
|
235
|
-
});
|
|
236
|
-
} catch (error) {
|
|
237
|
-
results.push({
|
|
238
|
-
test: 'sdk_login',
|
|
239
|
-
success: false,
|
|
240
|
-
error: error.message
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
} catch {
|
|
179
|
+
|
|
180
|
+
results.push({
|
|
181
|
+
test: 'local_mode_contract',
|
|
182
|
+
success: runtime.local_mode.known,
|
|
183
|
+
details: runtime.local_mode,
|
|
184
|
+
fix: runtime.local_mode.known ? null : `curl -s ${runtime.url}/health/local-mode`,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (!runtime.local_mode.active) {
|
|
188
|
+
const hasKey = Boolean(process.env.SPAPS_API_KEY);
|
|
244
189
|
results.push({
|
|
245
|
-
test: '
|
|
246
|
-
success:
|
|
247
|
-
message:
|
|
248
|
-
|
|
190
|
+
test: 'api_key_wiring',
|
|
191
|
+
success: hasKey,
|
|
192
|
+
message: hasKey
|
|
193
|
+
? 'SPAPS_API_KEY is set for non-local-mode testing'
|
|
194
|
+
: 'SPAPS_API_KEY is required when local mode is disabled',
|
|
195
|
+
fix: hasKey ? null : buildProvisioningCommand('node', port),
|
|
249
196
|
});
|
|
250
197
|
}
|
|
251
|
-
|
|
252
|
-
const allSuccess = results.every(
|
|
253
|
-
|
|
198
|
+
|
|
199
|
+
const allSuccess = results.every((result) => result.success);
|
|
200
|
+
|
|
254
201
|
return {
|
|
255
202
|
success: allSuccess,
|
|
256
|
-
summary: `${results.filter(
|
|
203
|
+
summary: `${results.filter((result) => result.success).length}/${results.length} tests passed`,
|
|
257
204
|
results,
|
|
258
|
-
next_steps: allSuccess
|
|
259
|
-
|
|
260
|
-
'
|
|
261
|
-
'See docs at http://localhost:' + port + '/docs'
|
|
262
|
-
] : [
|
|
263
|
-
'Fix the failing tests above',
|
|
264
|
-
'Run: npx spaps test --json to retry'
|
|
265
|
-
]
|
|
205
|
+
next_steps: allSuccess
|
|
206
|
+
? [`See docs at ${runtime.docs}`]
|
|
207
|
+
: ['Fix the failing checks above and re-run: npx spaps test --json'],
|
|
266
208
|
};
|
|
267
209
|
}
|
|
268
210
|
|
|
269
211
|
module.exports = {
|
|
270
212
|
getQuickStartInstructions,
|
|
271
213
|
getServerStatus,
|
|
272
|
-
runQuickTest
|
|
214
|
+
runQuickTest,
|
|
273
215
|
};
|
package/src/ai-tool-spec.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI Tool Spec generator for SPAPS
|
|
3
3
|
* - Produces OpenAI-style function schemas for common SPAPS actions
|
|
4
|
-
* -
|
|
4
|
+
* - Derives auth expectations from the running local server when available
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { DEFAULT_PORT } = require('./config');
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
+
const { getServerRuntime } = require('./local-runtime');
|
|
10
11
|
|
|
11
12
|
function tryLoadManifest() {
|
|
12
13
|
const candidates = [
|
|
@@ -41,24 +42,54 @@ function tryLoadOpenAPI() {
|
|
|
41
42
|
return null;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
function
|
|
45
|
+
function buildAuthSection(runtime) {
|
|
46
|
+
if (!runtime.running) {
|
|
47
|
+
return {
|
|
48
|
+
local_mode: false,
|
|
49
|
+
mode_source: '/health/local-mode',
|
|
50
|
+
api_key_required: true,
|
|
51
|
+
header: 'X-API-Key',
|
|
52
|
+
env: 'SPAPS_API_KEY',
|
|
53
|
+
status: 'server_unreachable',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
local_mode: Boolean(runtime.local_mode.active),
|
|
59
|
+
mode_source: '/health/local-mode',
|
|
60
|
+
api_key_required: !runtime.local_mode.active,
|
|
61
|
+
header: 'X-API-Key',
|
|
62
|
+
env: 'SPAPS_API_KEY',
|
|
63
|
+
environment: runtime.local_mode.environment,
|
|
64
|
+
spaps_local_mode_env: runtime.local_mode.spaps_local_mode_env,
|
|
65
|
+
...(runtime.local_mode.active
|
|
66
|
+
? {
|
|
67
|
+
test_users: runtime.local_mode.test_users,
|
|
68
|
+
test_application: runtime.local_mode.test_application,
|
|
69
|
+
hints: runtime.local_mode.hints,
|
|
70
|
+
}
|
|
71
|
+
: {
|
|
72
|
+
self_service_password_env: 'SELF_SERVICE_PASSWORD',
|
|
73
|
+
}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function buildOpenAIToolSpec({ port = DEFAULT_PORT, version = '0.0.0' } = {}) {
|
|
45
78
|
const baseUrl = `http://localhost:${port}`;
|
|
79
|
+
const runtime = await getServerRuntime({ port });
|
|
80
|
+
const auth = buildAuthSection(runtime);
|
|
46
81
|
const spec = {
|
|
47
82
|
name: 'spaps',
|
|
48
|
-
version
|
|
49
|
-
description: 'Auth + payments via SPAPS
|
|
83
|
+
version,
|
|
84
|
+
description: 'Auth + payments via SPAPS. Resolve local auth requirements from /health/local-mode.',
|
|
50
85
|
base_url: baseUrl,
|
|
51
|
-
auth
|
|
52
|
-
local_mode: true,
|
|
53
|
-
production: {
|
|
54
|
-
header: 'X-API-Key',
|
|
55
|
-
env: 'SPAPS_API_KEY'
|
|
56
|
-
}
|
|
57
|
-
},
|
|
86
|
+
auth,
|
|
58
87
|
tools: [
|
|
59
88
|
{
|
|
60
89
|
name: 'login',
|
|
61
|
-
description:
|
|
90
|
+
description: auth.local_mode
|
|
91
|
+
? 'Authenticate a local test user. API key validation is bypassed while local mode is active.'
|
|
92
|
+
: 'Authenticate a user with email/password for a provisioned application. Send X-API-Key when local mode is disabled.',
|
|
62
93
|
method: 'POST',
|
|
63
94
|
path: '/api/auth/login',
|
|
64
95
|
parameters: {
|
|
@@ -72,7 +103,9 @@ function buildOpenAIToolSpec({ port = DEFAULT_PORT } = {}) {
|
|
|
72
103
|
},
|
|
73
104
|
{
|
|
74
105
|
name: 'register',
|
|
75
|
-
description:
|
|
106
|
+
description: auth.local_mode
|
|
107
|
+
? 'Register a user while local mode is active.'
|
|
108
|
+
: 'Register a new user with email/password for a provisioned application.',
|
|
76
109
|
method: 'POST',
|
|
77
110
|
path: '/api/auth/register',
|
|
78
111
|
parameters: {
|
|
@@ -86,7 +119,7 @@ function buildOpenAIToolSpec({ port = DEFAULT_PORT } = {}) {
|
|
|
86
119
|
},
|
|
87
120
|
{
|
|
88
121
|
name: 'get_current_user',
|
|
89
|
-
description: 'Get the currently authenticated user. Uses bearer token from previous login.',
|
|
122
|
+
description: 'Get the currently authenticated user. Uses the bearer token from a previous login.',
|
|
90
123
|
method: 'GET',
|
|
91
124
|
path: '/api/auth/user',
|
|
92
125
|
parameters: {
|
|
@@ -98,7 +131,7 @@ function buildOpenAIToolSpec({ port = DEFAULT_PORT } = {}) {
|
|
|
98
131
|
},
|
|
99
132
|
{
|
|
100
133
|
name: 'create_checkout_session',
|
|
101
|
-
description: 'Create a Stripe Checkout session
|
|
134
|
+
description: 'Create a Stripe Checkout session against the configured SPAPS server.',
|
|
102
135
|
method: 'POST',
|
|
103
136
|
path: '/api/stripe/checkout-sessions',
|
|
104
137
|
parameters: {
|
|
@@ -116,7 +149,7 @@ function buildOpenAIToolSpec({ port = DEFAULT_PORT } = {}) {
|
|
|
116
149
|
},
|
|
117
150
|
{
|
|
118
151
|
name: 'list_products',
|
|
119
|
-
description: 'List products
|
|
152
|
+
description: 'List products exposed by the SPAPS server.',
|
|
120
153
|
method: 'GET',
|
|
121
154
|
path: '/api/stripe/products',
|
|
122
155
|
parameters: {
|
|
@@ -129,7 +162,7 @@ function buildOpenAIToolSpec({ port = DEFAULT_PORT } = {}) {
|
|
|
129
162
|
},
|
|
130
163
|
{
|
|
131
164
|
name: 'request_magic_link',
|
|
132
|
-
description: 'Send a magic link for passwordless login
|
|
165
|
+
description: 'Send a magic link for passwordless login.',
|
|
133
166
|
method: 'POST',
|
|
134
167
|
path: '/api/auth/magic-link',
|
|
135
168
|
parameters: {
|
|
@@ -254,7 +287,6 @@ function buildOpenAIToolSpec({ port = DEFAULT_PORT } = {}) {
|
|
|
254
287
|
}
|
|
255
288
|
}
|
|
256
289
|
if (Object.keys(responses).length) {
|
|
257
|
-
// Merge with default errors for completeness
|
|
258
290
|
const merged = { ...defaultErrors, ...responses };
|
|
259
291
|
t.responses = merged;
|
|
260
292
|
}
|
|
@@ -287,11 +319,11 @@ function buildOpenAIToolSpec({ port = DEFAULT_PORT } = {}) {
|
|
|
287
319
|
return spec;
|
|
288
320
|
}
|
|
289
321
|
|
|
290
|
-
function buildToolSpec({ format = 'openai', port = DEFAULT_PORT } = {}) {
|
|
322
|
+
async function buildToolSpec({ format = 'openai', port = DEFAULT_PORT, version = '0.0.0' } = {}) {
|
|
291
323
|
switch (format) {
|
|
292
324
|
case 'openai':
|
|
293
325
|
default:
|
|
294
|
-
return buildOpenAIToolSpec({ port });
|
|
326
|
+
return buildOpenAIToolSpec({ port, version });
|
|
295
327
|
}
|
|
296
328
|
}
|
|
297
329
|
|