streamtrust 0.2.0 → 0.3.0
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/README.md +17 -7
- package/examples/librechat/README.md +2 -2
- package/examples/vercel-ai/README.md +108 -0
- package/examples/vercel-ai/adapter.mjs +333 -0
- package/examples/vercel-ai/config.mjs +17 -0
- package/examples/vercel-ai/provider.mjs +79 -0
- package/examples/vercel-ai/reference-app/app/api/chat/route.js +86 -0
- package/examples/vercel-ai/reference-app/app/chat/[id]/chat.jsx +69 -0
- package/examples/vercel-ai/reference-app/app/chat/[id]/page.jsx +20 -0
- package/examples/vercel-ai/reference-app/app/layout.jsx +18 -0
- package/examples/vercel-ai/reference-app/app/page.jsx +8 -0
- package/examples/vercel-ai/reference-app/lib/store.js +37 -0
- package/examples/vercel-ai/reference-app/next.config.mjs +9 -0
- package/examples/vercel-ai/reference-app/npm-shrinkwrap.json +1247 -0
- package/examples/vercel-ai/reference-app/package.json +21 -0
- package/examples/vercel-ai/setup.mjs +27 -0
- package/package.json +13 -2
- package/src/cli.d.ts +2 -0
- package/src/cli.js +28 -16
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "streamtrust-vercel-ai-reference-app",
|
|
3
|
+
"private": true,
|
|
4
|
+
"description": "Minimal Next.js + AI SDK chat used as the StreamTrust vercel-ai preset's application under test",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "next build",
|
|
11
|
+
"start": "next start"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@ai-sdk/openai-compatible": "3.0.68",
|
|
15
|
+
"@ai-sdk/react": "4.0.141",
|
|
16
|
+
"ai": "7.0.138",
|
|
17
|
+
"next": "16.4.3",
|
|
18
|
+
"react": "19.3.0",
|
|
19
|
+
"react-dom": "19.3.0"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { assertIsolatedCheckout } from './config.mjs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const root = resolve(
|
|
7
|
+
process.env.STREAMTRUST_VERCEL_AI_ROOT ||
|
|
8
|
+
fileURLToPath(new URL('./reference-app', import.meta.url)),
|
|
9
|
+
);
|
|
10
|
+
if (Number(process.versions.node.split('.')[0]) < 22)
|
|
11
|
+
throw new Error('The vercel-ai preset requires Node.js 22+ (AI SDK 7)');
|
|
12
|
+
if (!existsSync(resolve(root, 'package.json')))
|
|
13
|
+
throw new Error(`No package.json in ${root}`);
|
|
14
|
+
assertIsolatedCheckout(root);
|
|
15
|
+
const run = (cmd, args) =>
|
|
16
|
+
execFileSync(cmd, args, {
|
|
17
|
+
cwd: root,
|
|
18
|
+
stdio: 'inherit',
|
|
19
|
+
env: { ...process.env, NEXT_TELEMETRY_DISABLED: '1' },
|
|
20
|
+
});
|
|
21
|
+
// The bundled shrinkwrap pins the fixture; external checkouts retain their own lockfile.
|
|
22
|
+
run('npm', [
|
|
23
|
+
existsSync(resolve(root, 'npm-shrinkwrap.json')) || existsSync(resolve(root, 'package-lock.json')) ? 'ci' : 'install',
|
|
24
|
+
'--no-audit',
|
|
25
|
+
'--no-fund',
|
|
26
|
+
]);
|
|
27
|
+
run('npm', ['run', 'build']);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "streamtrust",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Adapter-driven CI verification for streaming application lifecycle guarantees",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,7 +24,18 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"bin/",
|
|
26
26
|
"src/",
|
|
27
|
-
"examples/",
|
|
27
|
+
"examples/http.adapter.mjs",
|
|
28
|
+
"examples/demo-app.mjs",
|
|
29
|
+
"examples/librechat/*.mjs",
|
|
30
|
+
"examples/librechat/README.md",
|
|
31
|
+
"examples/vercel-ai/*.mjs",
|
|
32
|
+
"examples/vercel-ai/README.md",
|
|
33
|
+
"examples/vercel-ai/reference-app/package.json",
|
|
34
|
+
"examples/vercel-ai/reference-app/npm-shrinkwrap.json",
|
|
35
|
+
"examples/vercel-ai/reference-app/next.config.mjs",
|
|
36
|
+
"examples/vercel-ai/reference-app/lib/*.js",
|
|
37
|
+
"examples/vercel-ai/reference-app/app/**/*.js",
|
|
38
|
+
"examples/vercel-ai/reference-app/app/**/*.jsx",
|
|
28
39
|
"README.md",
|
|
29
40
|
"LICENSE"
|
|
30
41
|
],
|
package/src/cli.d.ts
CHANGED
package/src/cli.js
CHANGED
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
import { fork } from 'node:child_process';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { resolve } from 'node:path';
|
|
3
|
+
import { resolve, join, dirname } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { mkdir, copyFile, open, unlink, readFile } from 'node:fs/promises';
|
|
6
6
|
import { constants } from 'node:fs';
|
|
7
7
|
import { VERSION, failureReport } from './index.js';
|
|
8
|
-
const
|
|
8
|
+
const EXAMPLES = fileURLToPath(new URL('../examples/', import.meta.url));
|
|
9
|
+
// Explicit source inventory: never copy local builds, dependencies or configuration.
|
|
10
|
+
const PRESETS = {
|
|
11
|
+
http: [['http.adapter.mjs', 'adapter.mjs'], ['demo-app.mjs', 'demo-app.mjs']],
|
|
12
|
+
librechat: ['adapter.mjs', 'provider.mjs', 'setup.mjs', 'README.md'].map(
|
|
13
|
+
(name) => [`librechat/${name}`, name]),
|
|
14
|
+
'vercel-ai': [
|
|
15
|
+
'adapter.mjs', 'provider.mjs', 'setup.mjs', 'config.mjs', 'README.md',
|
|
16
|
+
'reference-app/package.json', 'reference-app/npm-shrinkwrap.json',
|
|
17
|
+
'reference-app/next.config.mjs', 'reference-app/lib/store.js',
|
|
18
|
+
'reference-app/app/layout.jsx', 'reference-app/app/page.jsx',
|
|
19
|
+
'reference-app/app/api/chat/route.js', 'reference-app/app/chat/[id]/chat.jsx',
|
|
20
|
+
'reference-app/app/chat/[id]/page.jsx',
|
|
21
|
+
].map((name) => [`vercel-ai/${name}`, name]),
|
|
22
|
+
};
|
|
23
|
+
export function presets() { return Object.keys(PRESETS); }
|
|
24
|
+
function presetFiles(preset) { return PRESETS[preset]; }
|
|
25
|
+
const HELP = () =>
|
|
26
|
+
`StreamTrust ${VERSION}\n\nUsage:\n streamtrust init [directory] [--preset <name>]\n streamtrust verify --adapter ./streamtrust/adapter.mjs [options]\n\nOptions:\n --output <directory> Reports (default: reports/streamtrust)\n --repetitions <1-1000> Repetitions per scenario (default: 3)\n --timeout-ms <integer> Total run deadline including setup/cleanup (default: 300000)\n --help Show help\n --version Show version\n\nPresets: ${presets().join(', ')} (default: http)\n\nExit codes: 0 all pass; 1 failed/unsupported checks; 2 setup, timeout or harness error.\nAdapters execute trusted local code. JavaScript ESM (.mjs) works without dependencies.\n`;
|
|
9
27
|
export async function main(args) {
|
|
10
28
|
let outputDir;
|
|
11
29
|
try {
|
|
@@ -14,7 +32,7 @@ export async function main(args) {
|
|
|
14
32
|
return 0;
|
|
15
33
|
}
|
|
16
34
|
if (!args.length || args.includes('--help')) {
|
|
17
|
-
console.log(HELP);
|
|
35
|
+
console.log(HELP());
|
|
18
36
|
return 0;
|
|
19
37
|
}
|
|
20
38
|
if (args[0] === 'init') {
|
|
@@ -24,7 +42,10 @@ export async function main(args) {
|
|
|
24
42
|
if (presetIndex !== -1) {
|
|
25
43
|
preset = initArgs[presetIndex + 1];
|
|
26
44
|
initArgs.splice(presetIndex, 2);
|
|
27
|
-
if (
|
|
45
|
+
if (!presets().includes(preset))
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Unknown init preset; available: ${presets().join(', ')}`,
|
|
48
|
+
);
|
|
28
49
|
}
|
|
29
50
|
if (initArgs.length > 1 || initArgs[0]?.startsWith('-'))
|
|
30
51
|
throw new Error('Invalid init arguments');
|
|
@@ -33,20 +54,11 @@ export async function main(args) {
|
|
|
33
54
|
// Exclusive copies preserve existing adapters. No generated config overrides user files.
|
|
34
55
|
const created = [];
|
|
35
56
|
try {
|
|
36
|
-
for (const [source, target] of preset
|
|
37
|
-
? [
|
|
38
|
-
['librechat/adapter.mjs', 'adapter.mjs'],
|
|
39
|
-
['librechat/provider.mjs', 'provider.mjs'],
|
|
40
|
-
['librechat/setup.mjs', 'setup.mjs'],
|
|
41
|
-
['librechat/README.md', 'README.md'],
|
|
42
|
-
]
|
|
43
|
-
: [
|
|
44
|
-
['http.adapter.mjs', 'adapter.mjs'],
|
|
45
|
-
['demo-app.mjs', 'demo-app.mjs'],
|
|
46
|
-
]) {
|
|
57
|
+
for (const [source, target] of presetFiles(preset)) {
|
|
47
58
|
const destination = resolve(dir, target);
|
|
59
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
48
60
|
await copyFile(
|
|
49
|
-
|
|
61
|
+
join(EXAMPLES, source),
|
|
50
62
|
destination,
|
|
51
63
|
constants.COPYFILE_EXCL,
|
|
52
64
|
);
|