From c51fee14848b724cdc601ee002283a46c24ae790 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Sun, 25 Jan 2026 12:00:25 +1000 Subject: [PATCH 01/54] chore: remove build script from video package --- apps/web/next-env.d.ts | 2 +- packages/cli/oclif.manifest.json | 334 +++++++++++++++---------------- packages/video/package.json | 3 +- 3 files changed, 169 insertions(+), 170 deletions(-) diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts index c4b7818..9edff1c 100644 --- a/apps/web/next-env.d.ts +++ b/apps/web/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 26946c9..d7602b2 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -227,173 +227,6 @@ "update.js" ] }, - "auth:login": { - "aliases": [], - "args": { - "key": { - "description": "API key (alternative to --api-key flag)", - "name": "key", - "required": false - } - }, - "description": "Authenticate with TPMJS", - "examples": [ - "<%= config.bin %> <%= command.id %> --api-key tpm_xxxxx", - "<%= config.bin %> <%= command.id %> --browser" - ], - "flags": { - "api-key": { - "char": "k", - "description": "API key (or set TPMJS_API_KEY environment variable)", - "name": "api-key", - "hasDynamicHelp": false, - "multiple": false, - "type": "option" - }, - "browser": { - "char": "b", - "description": "Open browser for OAuth authentication", - "name": "browser", - "allowNo": false, - "type": "boolean" - }, - "json": { - "description": "Output in JSON format", - "name": "json", - "allowNo": false, - "type": "boolean" - }, - "verbose": { - "char": "v", - "description": "Show verbose output", - "name": "verbose", - "allowNo": false, - "type": "boolean" - } - }, - "hasDynamicHelp": false, - "hiddenAliases": [], - "id": "auth:login", - "pluginAlias": "@tpmjs/cli", - "pluginName": "@tpmjs/cli", - "pluginType": "core", - "strict": true, - "enableJsonFlag": false, - "isESM": true, - "relativePath": [ - "dist", - "commands", - "auth", - "login.js" - ] - }, - "auth:logout": { - "aliases": [], - "args": {}, - "description": "Log out from TPMJS", - "examples": [ - "<%= config.bin %> <%= command.id %>" - ], - "flags": { - "json": { - "description": "Output in JSON format", - "name": "json", - "allowNo": false, - "type": "boolean" - } - }, - "hasDynamicHelp": false, - "hiddenAliases": [], - "id": "auth:logout", - "pluginAlias": "@tpmjs/cli", - "pluginName": "@tpmjs/cli", - "pluginType": "core", - "strict": true, - "enableJsonFlag": false, - "isESM": true, - "relativePath": [ - "dist", - "commands", - "auth", - "logout.js" - ] - }, - "auth:status": { - "aliases": [], - "args": {}, - "description": "Show authentication status", - "examples": [ - "<%= config.bin %> <%= command.id %>" - ], - "flags": { - "json": { - "description": "Output in JSON format", - "name": "json", - "allowNo": false, - "type": "boolean" - }, - "verbose": { - "char": "v", - "description": "Show verbose output", - "name": "verbose", - "allowNo": false, - "type": "boolean" - } - }, - "hasDynamicHelp": false, - "hiddenAliases": [], - "id": "auth:status", - "pluginAlias": "@tpmjs/cli", - "pluginName": "@tpmjs/cli", - "pluginType": "core", - "strict": true, - "enableJsonFlag": false, - "isESM": true, - "relativePath": [ - "dist", - "commands", - "auth", - "status.js" - ] - }, - "auth:whoami": { - "aliases": [], - "args": {}, - "description": "Show current user information", - "examples": [ - "<%= config.bin %> <%= command.id %>" - ], - "flags": { - "json": { - "description": "Output in JSON format", - "name": "json", - "allowNo": false, - "type": "boolean" - }, - "verbose": { - "char": "v", - "description": "Show verbose output", - "name": "verbose", - "allowNo": false, - "type": "boolean" - } - }, - "hasDynamicHelp": false, - "hiddenAliases": [], - "id": "auth:whoami", - "pluginAlias": "@tpmjs/cli", - "pluginName": "@tpmjs/cli", - "pluginType": "core", - "strict": true, - "enableJsonFlag": false, - "isESM": true, - "relativePath": [ - "dist", - "commands", - "auth", - "whoami.js" - ] - }, "agent:chat": { "aliases": [], "args": { @@ -792,6 +625,173 @@ "update.js" ] }, + "auth:login": { + "aliases": [], + "args": { + "key": { + "description": "API key (alternative to --api-key flag)", + "name": "key", + "required": false + } + }, + "description": "Authenticate with TPMJS", + "examples": [ + "<%= config.bin %> <%= command.id %> --api-key tpm_xxxxx", + "<%= config.bin %> <%= command.id %> --browser" + ], + "flags": { + "api-key": { + "char": "k", + "description": "API key (or set TPMJS_API_KEY environment variable)", + "name": "api-key", + "hasDynamicHelp": false, + "multiple": false, + "type": "option" + }, + "browser": { + "char": "b", + "description": "Open browser for OAuth authentication", + "name": "browser", + "allowNo": false, + "type": "boolean" + }, + "json": { + "description": "Output in JSON format", + "name": "json", + "allowNo": false, + "type": "boolean" + }, + "verbose": { + "char": "v", + "description": "Show verbose output", + "name": "verbose", + "allowNo": false, + "type": "boolean" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [], + "id": "auth:login", + "pluginAlias": "@tpmjs/cli", + "pluginName": "@tpmjs/cli", + "pluginType": "core", + "strict": true, + "enableJsonFlag": false, + "isESM": true, + "relativePath": [ + "dist", + "commands", + "auth", + "login.js" + ] + }, + "auth:logout": { + "aliases": [], + "args": {}, + "description": "Log out from TPMJS", + "examples": [ + "<%= config.bin %> <%= command.id %>" + ], + "flags": { + "json": { + "description": "Output in JSON format", + "name": "json", + "allowNo": false, + "type": "boolean" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [], + "id": "auth:logout", + "pluginAlias": "@tpmjs/cli", + "pluginName": "@tpmjs/cli", + "pluginType": "core", + "strict": true, + "enableJsonFlag": false, + "isESM": true, + "relativePath": [ + "dist", + "commands", + "auth", + "logout.js" + ] + }, + "auth:status": { + "aliases": [], + "args": {}, + "description": "Show authentication status", + "examples": [ + "<%= config.bin %> <%= command.id %>" + ], + "flags": { + "json": { + "description": "Output in JSON format", + "name": "json", + "allowNo": false, + "type": "boolean" + }, + "verbose": { + "char": "v", + "description": "Show verbose output", + "name": "verbose", + "allowNo": false, + "type": "boolean" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [], + "id": "auth:status", + "pluginAlias": "@tpmjs/cli", + "pluginName": "@tpmjs/cli", + "pluginType": "core", + "strict": true, + "enableJsonFlag": false, + "isESM": true, + "relativePath": [ + "dist", + "commands", + "auth", + "status.js" + ] + }, + "auth:whoami": { + "aliases": [], + "args": {}, + "description": "Show current user information", + "examples": [ + "<%= config.bin %> <%= command.id %>" + ], + "flags": { + "json": { + "description": "Output in JSON format", + "name": "json", + "allowNo": false, + "type": "boolean" + }, + "verbose": { + "char": "v", + "description": "Show verbose output", + "name": "verbose", + "allowNo": false, + "type": "boolean" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [], + "id": "auth:whoami", + "pluginAlias": "@tpmjs/cli", + "pluginName": "@tpmjs/cli", + "pluginType": "core", + "strict": true, + "enableJsonFlag": false, + "isESM": true, + "relativePath": [ + "dist", + "commands", + "auth", + "whoami.js" + ] + }, "collection:add": { "aliases": [], "args": { diff --git a/packages/video/package.json b/packages/video/package.json index fe85e1d..49e111e 100644 --- a/packages/video/package.json +++ b/packages/video/package.json @@ -5,8 +5,7 @@ "description": "Marketing videos for TPMJS", "scripts": { "dev": "remotion studio", - "build": "remotion render MarketingVideo out/marketing-video.mp4", - "render": "remotion render", + "render": "remotion render MarketingVideo out/marketing-video.mp4", "upgrade": "remotion upgrade" }, "dependencies": { From 4e3cb78ba4c4ab194049a9ae0a71f8ac1a075385 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Sun, 25 Jan 2026 12:23:43 +1000 Subject: [PATCH 02/54] feat(sprites): add SPRITES_TOKEN env requirement to package metadata All sprites tools require a SPRITES_TOKEN to authenticate with sprites.dev. This adds the env field to the tpmjs metadata so Omega can warn users about missing API keys. --- .../official/sprites-checkpoint-create/CHANGELOG.md | 7 +++++++ .../official/sprites-checkpoint-create/package.json | 9 ++++++++- .../tools/official/sprites-checkpoint-list/CHANGELOG.md | 7 +++++++ .../tools/official/sprites-checkpoint-list/package.json | 9 ++++++++- .../official/sprites-checkpoint-restore/CHANGELOG.md | 7 +++++++ .../official/sprites-checkpoint-restore/package.json | 9 ++++++++- packages/tools/official/sprites-create/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-create/package.json | 9 ++++++++- packages/tools/official/sprites-delete/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-delete/package.json | 9 ++++++++- packages/tools/official/sprites-exec/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-exec/package.json | 9 ++++++++- packages/tools/official/sprites-get/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-get/package.json | 9 ++++++++- packages/tools/official/sprites-list/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-list/package.json | 9 ++++++++- packages/tools/official/sprites-policy-get/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-policy-get/package.json | 9 ++++++++- packages/tools/official/sprites-policy-set/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-policy-set/package.json | 9 ++++++++- packages/tools/official/sprites-sessions/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-sessions/package.json | 9 ++++++++- packages/tools/official/sprites-url-get/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-url-get/package.json | 9 ++++++++- packages/tools/official/sprites-url-set/CHANGELOG.md | 7 +++++++ packages/tools/official/sprites-url-set/package.json | 9 ++++++++- 26 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 packages/tools/official/sprites-checkpoint-create/CHANGELOG.md create mode 100644 packages/tools/official/sprites-checkpoint-list/CHANGELOG.md create mode 100644 packages/tools/official/sprites-checkpoint-restore/CHANGELOG.md create mode 100644 packages/tools/official/sprites-create/CHANGELOG.md create mode 100644 packages/tools/official/sprites-delete/CHANGELOG.md create mode 100644 packages/tools/official/sprites-exec/CHANGELOG.md create mode 100644 packages/tools/official/sprites-get/CHANGELOG.md create mode 100644 packages/tools/official/sprites-list/CHANGELOG.md create mode 100644 packages/tools/official/sprites-policy-get/CHANGELOG.md create mode 100644 packages/tools/official/sprites-policy-set/CHANGELOG.md create mode 100644 packages/tools/official/sprites-sessions/CHANGELOG.md create mode 100644 packages/tools/official/sprites-url-get/CHANGELOG.md create mode 100644 packages/tools/official/sprites-url-set/CHANGELOG.md diff --git a/packages/tools/official/sprites-checkpoint-create/CHANGELOG.md b/packages/tools/official/sprites-checkpoint-create/CHANGELOG.md new file mode 100644 index 0000000..f1695c7 --- /dev/null +++ b/packages/tools/official/sprites-checkpoint-create/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-checkpoint-create + +## 0.1.5 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-checkpoint-create/package.json b/packages/tools/official/sprites-checkpoint-create/package.json index ad78d88..622d758 100644 --- a/packages/tools/official/sprites-checkpoint-create/package.json +++ b/packages/tools/official/sprites-checkpoint-create/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-checkpoint-create", - "version": "0.1.4", + "version": "0.1.5", "description": "Create a point-in-time snapshot of a sprite's filesystem state for later restoration", "type": "module", "keywords": [ @@ -69,6 +69,13 @@ "description": "Created checkpoint with ID and metadata" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-checkpoint-list/CHANGELOG.md b/packages/tools/official/sprites-checkpoint-list/CHANGELOG.md new file mode 100644 index 0000000..b42ca65 --- /dev/null +++ b/packages/tools/official/sprites-checkpoint-list/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-checkpoint-list + +## 0.1.3 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-checkpoint-list/package.json b/packages/tools/official/sprites-checkpoint-list/package.json index 32b18c5..5c0659d 100644 --- a/packages/tools/official/sprites-checkpoint-list/package.json +++ b/packages/tools/official/sprites-checkpoint-list/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-checkpoint-list", - "version": "0.1.2", + "version": "0.1.3", "description": "List all checkpoints for a sprite ordered by creation time", "type": "module", "keywords": [ @@ -63,6 +63,13 @@ "description": "Array of checkpoints with count" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-checkpoint-restore/CHANGELOG.md b/packages/tools/official/sprites-checkpoint-restore/CHANGELOG.md new file mode 100644 index 0000000..7b9f0b2 --- /dev/null +++ b/packages/tools/official/sprites-checkpoint-restore/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-checkpoint-restore + +## 0.1.3 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-checkpoint-restore/package.json b/packages/tools/official/sprites-checkpoint-restore/package.json index 16c0b04..1159d9e 100644 --- a/packages/tools/official/sprites-checkpoint-restore/package.json +++ b/packages/tools/official/sprites-checkpoint-restore/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-checkpoint-restore", - "version": "0.1.2", + "version": "0.1.3", "description": "Restore a sprite to a previous checkpoint state", "type": "module", "keywords": [ @@ -69,6 +69,13 @@ "description": "Restoration result with sprite status" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-create/CHANGELOG.md b/packages/tools/official/sprites-create/CHANGELOG.md new file mode 100644 index 0000000..9b75899 --- /dev/null +++ b/packages/tools/official/sprites-create/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-create + +## 0.1.3 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-create/package.json b/packages/tools/official/sprites-create/package.json index 5aad799..823f5e6 100644 --- a/packages/tools/official/sprites-create/package.json +++ b/packages/tools/official/sprites-create/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-create", - "version": "0.1.2", + "version": "0.1.3", "description": "Create a new isolated Linux sandbox environment (sprite) with persistent filesystem using the Sprites API", "type": "module", "keywords": [ @@ -62,6 +62,13 @@ "description": "Created sprite with name, status, and metadata" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-delete/CHANGELOG.md b/packages/tools/official/sprites-delete/CHANGELOG.md new file mode 100644 index 0000000..6eddb1b --- /dev/null +++ b/packages/tools/official/sprites-delete/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-delete + +## 0.1.3 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-delete/package.json b/packages/tools/official/sprites-delete/package.json index 277af2c..fb1d01c 100644 --- a/packages/tools/official/sprites-delete/package.json +++ b/packages/tools/official/sprites-delete/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-delete", - "version": "0.1.2", + "version": "0.1.3", "description": "Delete a sprite and all its associated data including checkpoints", "type": "module", "keywords": [ @@ -62,6 +62,13 @@ "description": "Deletion result with success status" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-exec/CHANGELOG.md b/packages/tools/official/sprites-exec/CHANGELOG.md new file mode 100644 index 0000000..f09a4ee --- /dev/null +++ b/packages/tools/official/sprites-exec/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-exec + +## 0.1.7 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-exec/package.json b/packages/tools/official/sprites-exec/package.json index 9e21c0b..fdf2b02 100644 --- a/packages/tools/official/sprites-exec/package.json +++ b/packages/tools/official/sprites-exec/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-exec", - "version": "0.1.6", + "version": "0.1.7", "description": "Execute a command inside a sprite and return the output with exit code", "type": "module", "keywords": [ @@ -80,6 +80,13 @@ "description": "Command output with exitCode, stdout, stderr, and duration" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-get/CHANGELOG.md b/packages/tools/official/sprites-get/CHANGELOG.md new file mode 100644 index 0000000..86063ba --- /dev/null +++ b/packages/tools/official/sprites-get/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-get + +## 0.1.3 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-get/package.json b/packages/tools/official/sprites-get/package.json index 13811b6..944b44a 100644 --- a/packages/tools/official/sprites-get/package.json +++ b/packages/tools/official/sprites-get/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-get", - "version": "0.1.2", + "version": "0.1.3", "description": "Retrieve details of a specific sprite by name including status and configuration", "type": "module", "keywords": [ @@ -62,6 +62,13 @@ "description": "Sprite details including status and metadata" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-list/CHANGELOG.md b/packages/tools/official/sprites-list/CHANGELOG.md new file mode 100644 index 0000000..743a5a8 --- /dev/null +++ b/packages/tools/official/sprites-list/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-list + +## 0.1.5 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-list/package.json b/packages/tools/official/sprites-list/package.json index 17b6fdf..f90d07c 100644 --- a/packages/tools/official/sprites-list/package.json +++ b/packages/tools/official/sprites-list/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-list", - "version": "0.1.4", + "version": "0.1.5", "description": "List all sprites in the account with their current status and metadata", "type": "module", "keywords": [ @@ -55,6 +55,13 @@ "description": "Array of sprites with count" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-policy-get/CHANGELOG.md b/packages/tools/official/sprites-policy-get/CHANGELOG.md new file mode 100644 index 0000000..a5f7b55 --- /dev/null +++ b/packages/tools/official/sprites-policy-get/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-policy-get + +## 0.1.4 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-policy-get/package.json b/packages/tools/official/sprites-policy-get/package.json index 169e4d1..98554af 100644 --- a/packages/tools/official/sprites-policy-get/package.json +++ b/packages/tools/official/sprites-policy-get/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-policy-get", - "version": "0.1.3", + "version": "0.1.4", "description": "Retrieve the current network policy for a sprite including allowed domains", "type": "module", "keywords": [ @@ -63,6 +63,13 @@ "description": "Current network policy with allowed domains and rules" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-policy-set/CHANGELOG.md b/packages/tools/official/sprites-policy-set/CHANGELOG.md new file mode 100644 index 0000000..401d49c --- /dev/null +++ b/packages/tools/official/sprites-policy-set/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-policy-set + +## 0.1.4 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-policy-set/package.json b/packages/tools/official/sprites-policy-set/package.json index 35fc074..d3faa76 100644 --- a/packages/tools/official/sprites-policy-set/package.json +++ b/packages/tools/official/sprites-policy-set/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-policy-set", - "version": "0.1.3", + "version": "0.1.4", "description": "Update the network policy for a sprite to control outbound network access", "type": "module", "keywords": [ @@ -75,6 +75,13 @@ "description": "Updated policy and application status" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-sessions/CHANGELOG.md b/packages/tools/official/sprites-sessions/CHANGELOG.md new file mode 100644 index 0000000..215648f --- /dev/null +++ b/packages/tools/official/sprites-sessions/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-sessions + +## 0.1.4 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-sessions/package.json b/packages/tools/official/sprites-sessions/package.json index cb55adf..dd84521 100644 --- a/packages/tools/official/sprites-sessions/package.json +++ b/packages/tools/official/sprites-sessions/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-sessions", - "version": "0.1.3", + "version": "0.1.4", "description": "List active execution sessions for a sprite", "type": "module", "keywords": [ @@ -62,6 +62,13 @@ "description": "Array of active execution sessions with count" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-url-get/CHANGELOG.md b/packages/tools/official/sprites-url-get/CHANGELOG.md new file mode 100644 index 0000000..c9663b9 --- /dev/null +++ b/packages/tools/official/sprites-url-get/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-url-get + +## 0.1.1 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-url-get/package.json b/packages/tools/official/sprites-url-get/package.json index 605c6bf..6a77f10 100644 --- a/packages/tools/official/sprites-url-get/package.json +++ b/packages/tools/official/sprites-url-get/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-url-get", - "version": "0.1.0", + "version": "0.1.1", "description": "Get the public URL and access settings for a sprite", "type": "module", "keywords": [ @@ -62,6 +62,13 @@ "description": "Sprite URL and auth settings" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { diff --git a/packages/tools/official/sprites-url-set/CHANGELOG.md b/packages/tools/official/sprites-url-set/CHANGELOG.md new file mode 100644 index 0000000..a6e58aa --- /dev/null +++ b/packages/tools/official/sprites-url-set/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tpmjs/tools-sprites-url-set + +## 0.1.1 + +### Patch Changes + +- Add SPRITES_TOKEN environment variable requirement to package metadata so Omega can warn users about missing API keys diff --git a/packages/tools/official/sprites-url-set/package.json b/packages/tools/official/sprites-url-set/package.json index 16a5398..19e27ed 100644 --- a/packages/tools/official/sprites-url-set/package.json +++ b/packages/tools/official/sprites-url-set/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-url-set", - "version": "0.1.0", + "version": "0.1.1", "description": "Set the access settings for a sprite URL (public or private)", "type": "module", "keywords": [ @@ -68,6 +68,13 @@ "description": "Updated sprite URL and settings" } } + ], + "env": [ + { + "name": "SPRITES_TOKEN", + "description": "API token from sprites.dev", + "required": true + } ] }, "dependencies": { From 88e66d54ce5ccdfda485281a2392c1a5c4afa4b4 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Sun, 25 Jan 2026 12:38:32 +1000 Subject: [PATCH 03/54] fix(omega): return error result instead of throwing for tool failures When a tool execution fails, return an error object instead of throwing. This allows the AI model to see the error and inform the user properly, rather than silently failing with no output. --- .../conversations/[id]/messages/route.ts | 56 ++++++++++++------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/apps/web/src/app/api/omega/conversations/[id]/messages/route.ts b/apps/web/src/app/api/omega/conversations/[id]/messages/route.ts index ce70aa8..9532f6e 100644 --- a/apps/web/src/app/api/omega/conversations/[id]/messages/route.ts +++ b/apps/web/src/app/api/omega/conversations/[id]/messages/route.ts @@ -12,7 +12,7 @@ import { Prisma, prisma } from '@tpmjs/db'; import { registryExecuteTool } from '@tpmjs/registry-execute'; import { registrySearchTool } from '@tpmjs/registry-search'; -import { jsonSchema, wrapLanguageModel, type ModelMessage } from 'ai'; +import { jsonSchema, type ModelMessage, wrapLanguageModel } from 'ai'; import { type NextRequest, NextResponse } from 'next/server'; import { z } from 'zod'; import { authenticateRequest } from '~/lib/api-keys/middleware'; @@ -223,29 +223,43 @@ async function createDynamicTool( execute: async (params: any) => { console.log(`🚀 Executing ${toolMeta.packageName}/${toolMeta.name} with params:`, params); - const response = await fetch(`${EXECUTOR_URL}/execute-tool`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - packageName: toolMeta.packageName, - name: toolMeta.name, - version: toolMeta.version, - importUrl: toolMeta.importUrl, - params, - env: userEnvVars, - }), - }); + try { + const response = await fetch(`${EXECUTOR_URL}/execute-tool`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + packageName: toolMeta.packageName, + name: toolMeta.name, + version: toolMeta.version, + importUrl: toolMeta.importUrl, + params, + env: userEnvVars, + }), + }); - // biome-ignore lint/suspicious/noExplicitAny: API response types vary - const result = (await response.json()) as any; + // biome-ignore lint/suspicious/noExplicitAny: API response types vary + const result = (await response.json()) as any; - if (!result.success) { - console.error(`❌ Tool execution failed: ${result.error}`); - throw new Error(result.error || 'Tool execution failed'); + if (!result.success) { + console.error(`❌ Tool execution failed: ${result.error}`); + // Return error as result instead of throwing so AI can see it + return { + error: true, + message: result.error || 'Tool execution failed', + toolId: toolMeta.toolId, + }; + } + + console.log(`✅ Tool executed in ${result.executionTimeMs}ms`); + return result.output; + } catch (error) { + console.error(`❌ Tool execution error:`, error); + return { + error: true, + message: error instanceof Error ? error.message : 'Unknown error during tool execution', + toolId: toolMeta.toolId, + }; } - - console.log(`✅ Tool executed in ${result.executionTimeMs}ms`); - return result.output; }, }); } From 9fc928adae5ceb30a709ef79194c59888071e855 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Sun, 25 Jan 2026 15:10:52 +1000 Subject: [PATCH 04/54] fix(tools): return error objects instead of throwing in registry tools All tool executions now return error objects instead of throwing exceptions. This allows the AI model to see errors and respond appropriately instead of causing the entire stream to fail silently. --- packages/tools/registryExecute/src/index.ts | 161 ++++++++++++-------- packages/tools/registrySearch/src/index.ts | 90 ++++++----- 2 files changed, 148 insertions(+), 103 deletions(-) diff --git a/packages/tools/registryExecute/src/index.ts b/packages/tools/registryExecute/src/index.ts index 3b674f7..07df2e1 100644 --- a/packages/tools/registryExecute/src/index.ts +++ b/packages/tools/registryExecute/src/index.ts @@ -49,72 +49,101 @@ export const registryExecuteTool = tool({ additionalProperties: false, }), async execute({ toolId, params, env }) { - // Parse toolId format: "package::name" - const separatorIndex = toolId.lastIndexOf('::'); - if (separatorIndex === -1) { - throw new Error(`Invalid toolId format. Expected "package::name", got "${toolId}"`); + try { + // Parse toolId format: "package::name" + const separatorIndex = toolId.lastIndexOf('::'); + if (separatorIndex === -1) { + return { + error: true, + message: `Invalid toolId format. Expected "package::name", got "${toolId}"`, + toolId, + }; + } + + const packageName = toolId.substring(0, separatorIndex); + const name = toolId.substring(separatorIndex + 2); + + if (!packageName || !name) { + return { + error: true, + message: `Invalid toolId format. Expected "package::name", got "${toolId}"`, + toolId, + }; + } + + // Fetch tool metadata to get version and importUrl + const metaParams = new URLSearchParams({ + q: name, + limit: '10', + }); + const metaResponse = await fetch(`${TPMJS_API_URL}/api/tools/search?${metaParams}`); + + if (!metaResponse.ok) { + return { + error: true, + message: `Failed to fetch tool metadata: ${metaResponse.statusText}`, + toolId, + }; + } + + // biome-ignore lint/suspicious/noExplicitAny: API response types vary + const metaData = (await metaResponse.json()) as any; + const toolsArray = metaData.results?.tools || []; + + // Find the exact tool match + // biome-ignore lint/suspicious/noExplicitAny: API response types vary + const toolMeta = toolsArray.find( + (t: any) => t.package.npmPackageName === packageName && t.name === name + ); + + if (!toolMeta) { + return { + error: true, + message: `Tool not found: ${toolId}. Try using registrySearchTool to find available tools.`, + toolId, + }; + } + + const version = toolMeta.package.npmVersion; + const importUrl = `https://esm.sh/${packageName}@${version}`; + + // Execute via sandbox executor + const response = await fetch(`${TPMJS_EXECUTOR_URL}/execute-tool`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + packageName, + name, + version, + importUrl, + params, + env: env || {}, + }), + }); + + // biome-ignore lint/suspicious/noExplicitAny: API response types vary + const result = (await response.json()) as any; + + if (!result.success) { + return { + error: true, + message: result.error || 'Tool execution failed', + toolId, + executionTimeMs: result.executionTimeMs, + }; + } + + return { + toolId, + executionTimeMs: result.executionTimeMs, + output: result.output, + }; + } catch (error) { + return { + error: true, + message: error instanceof Error ? error.message : 'Unknown execution error', + toolId, + }; } - - const packageName = toolId.substring(0, separatorIndex); - const name = toolId.substring(separatorIndex + 2); - - if (!packageName || !name) { - throw new Error(`Invalid toolId format. Expected "package::name", got "${toolId}"`); - } - - // Fetch tool metadata to get version and importUrl - const metaParams = new URLSearchParams({ - q: name, - limit: '10', - }); - const metaResponse = await fetch(`${TPMJS_API_URL}/api/tools/search?${metaParams}`); - - if (!metaResponse.ok) { - throw new Error(`Failed to fetch tool metadata: ${metaResponse.statusText}`); - } - - // biome-ignore lint/suspicious/noExplicitAny: API response types vary - const metaData = (await metaResponse.json()) as any; - const toolsArray = metaData.results?.tools || []; - - // Find the exact tool match - // biome-ignore lint/suspicious/noExplicitAny: API response types vary - const toolMeta = toolsArray.find( - (t: any) => t.package.npmPackageName === packageName && t.name === name - ); - - if (!toolMeta) { - throw new Error(`Tool not found: ${toolId}`); - } - - const version = toolMeta.package.npmVersion; - const importUrl = `https://esm.sh/${packageName}@${version}`; - - // Execute via sandbox executor - const response = await fetch(`${TPMJS_EXECUTOR_URL}/execute-tool`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - packageName, - name, - version, - importUrl, - params, - env: env || {}, - }), - }); - - // biome-ignore lint/suspicious/noExplicitAny: API response types vary - const result = (await response.json()) as any; - - if (!result.success) { - throw new Error(result.error || 'Tool execution failed'); - } - - return { - toolId, - executionTimeMs: result.executionTimeMs, - output: result.output, - }; }, }); diff --git a/packages/tools/registrySearch/src/index.ts b/packages/tools/registrySearch/src/index.ts index 1d6247e..6bfbdcb 100644 --- a/packages/tools/registrySearch/src/index.ts +++ b/packages/tools/registrySearch/src/index.ts @@ -58,45 +58,61 @@ export const registrySearchTool = tool({ additionalProperties: false, }), async execute({ query, category, limit = 5 }) { - const params = new URLSearchParams({ - q: query, - limit: String(limit), - ...(category && { category }), - }); + try { + const params = new URLSearchParams({ + q: query, + limit: String(limit), + ...(category && { category }), + }); - const url = `${TPMJS_API_URL}/api/tools/search?${params}`; - const response = await fetch(url); + const url = `${TPMJS_API_URL}/api/tools/search?${params}`; + const response = await fetch(url); - if (!response.ok) { - throw new Error(`Search failed: ${response.statusText}`); + if (!response.ok) { + return { + error: true, + message: `Search failed: ${response.statusText}`, + query, + matchCount: 0, + tools: [], + }; + } + + // biome-ignore lint/suspicious/noExplicitAny: API response types vary + const data = (await response.json()) as any; + const toolsArray = data.results?.tools || []; + + return { + query, + matchCount: toolsArray.length, + // biome-ignore lint/suspicious/noExplicitAny: Tool types from API vary + tools: toolsArray.map((t: any) => ({ + // Unique identifier for registryExecuteTool + toolId: `${t.package.npmPackageName}::${t.name}`, + + // Human-readable info + name: t.name, + package: t.package.npmPackageName, + description: t.description, + category: t.package.category, + + // Execution requirements + requiredEnvVars: + t.package.env?.filter((e: any) => e.required).map((e: any) => e.name) || [], + + // Quality indicators + healthStatus: t.executionHealth, + qualityScore: t.qualityScore, + })), + }; + } catch (error) { + return { + error: true, + message: error instanceof Error ? error.message : 'Unknown search error', + query, + matchCount: 0, + tools: [], + }; } - - // biome-ignore lint/suspicious/noExplicitAny: API response types vary - const data = (await response.json()) as any; - const toolsArray = data.results?.tools || []; - - return { - query, - matchCount: toolsArray.length, - // biome-ignore lint/suspicious/noExplicitAny: Tool types from API vary - tools: toolsArray.map((t: any) => ({ - // Unique identifier for registryExecuteTool - toolId: `${t.package.npmPackageName}::${t.name}`, - - // Human-readable info - name: t.name, - package: t.package.npmPackageName, - description: t.description, - category: t.package.category, - - // Execution requirements - requiredEnvVars: - t.package.env?.filter((e: any) => e.required).map((e: any) => e.name) || [], - - // Quality indicators - healthStatus: t.executionHealth, - qualityScore: t.qualityScore, - })), - }; }, }); From 16c7b8155e7baf4ed4d0352ebe3cc6e8e5f73104 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Sun, 25 Jan 2026 16:59:33 +1000 Subject: [PATCH 05/54] fix(omega): fix tool discovery on custom ports and OpenAI tool name limit - Extract port from request URL instead of relying on PORT env var - Truncate sanitized tool names to 64 chars (OpenAI API limit) --- .../conversations/[id]/messages/route.ts | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/apps/web/src/app/api/omega/conversations/[id]/messages/route.ts b/apps/web/src/app/api/omega/conversations/[id]/messages/route.ts index 9532f6e..708ae9a 100644 --- a/apps/web/src/app/api/omega/conversations/[id]/messages/route.ts +++ b/apps/web/src/app/api/omega/conversations/[id]/messages/route.ts @@ -145,7 +145,8 @@ const conversationStates = new Map }> */ async function searchRelevantTools( query: string, - limit = 15 + limit = 15, + requestUrl?: string ): Promise< Array<{ toolId: string; @@ -163,11 +164,21 @@ async function searchRelevantTools( limit: String(limit), }); - // Use internal API (same server) - const baseUrl = process.env.VERCEL_URL - ? `https://${process.env.VERCEL_URL}` - : 'http://localhost:3000'; + // Determine base URL from request or environment + let baseUrl: string; + if (process.env.VERCEL_URL) { + baseUrl = `https://${process.env.VERCEL_URL}`; + } else if (requestUrl) { + // Extract origin from the incoming request URL + const url = new URL(requestUrl); + baseUrl = url.origin; + } else { + // Fallback to PORT env var or default + const port = process.env.PORT || '3000'; + baseUrl = `http://localhost:${port}`; + } + console.log(`🔍 Tool search using baseUrl: ${baseUrl}`); const response = await fetch(`${baseUrl}/api/tools/search?${params}`); if (!response.ok) { @@ -265,15 +276,29 @@ async function createDynamicTool( } /** - * Sanitize tool name to be a valid JS identifier + * Sanitize tool name to be a valid JS identifier. + * OpenAI has a 64-character limit for tool names. */ function sanitizeToolName(name: string): string { - return name + const sanitized = name .replace(/@/g, '') .replace(/\//g, '_') .replace(/-/g, '_') .replace(/::/g, '_') .replace(/[^a-zA-Z0-9_]/g, ''); + + // OpenAI API requires tool names <= 64 characters + if (sanitized.length <= 64) { + return sanitized; + } + + // Truncate but try to keep the meaningful part (tool name at the end) + // Use last 64 chars if it starts with a letter, otherwise use first 64 + const last64 = sanitized.slice(-64); + if (/^[a-zA-Z]/.test(last64)) { + return last64; + } + return sanitized.slice(0, 64); } /** @@ -415,7 +440,7 @@ export async function POST(request: NextRequest, context: RouteContext): Promise // 🔍 Auto-search for relevant tools based on user's message (BM25) console.log(`🔍 Auto-searching for tools matching: "${parsed.data.message}"`); - const relevantTools = await searchRelevantTools(parsed.data.message, 10); + const relevantTools = await searchRelevantTools(parsed.data.message, 10, request.url); console.log(`📦 Found ${relevantTools.length} relevant tools via BM25`); // Add auto-discovered tools to conversation state From 8542e3d7d5d0d2c6d6cf37fad6e8ac924857df92 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Sun, 25 Jan 2026 18:29:27 +1000 Subject: [PATCH 06/54] feat(homepage): add features section highlighting platform capabilities - Redesigned FeaturesSection with 9 key platform features - Tool Registry, Omega Agent, Collections, Custom Agents - MCP Protocol, Secure Execution, Test Scenarios - Living Skills, Developer SDK - Each feature card links to relevant section - Clean, consistent design with existing brutalist style --- apps/web/src/app/page.tsx | 6 +- .../src/components/home/FeaturesSection.tsx | 680 ++++-------------- 2 files changed, 127 insertions(+), 559 deletions(-) diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 71587ae..db0e83e 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -5,8 +5,7 @@ import { Container } from '@tpmjs/ui/Container/Container'; import { Icon } from '@tpmjs/ui/Icon/Icon'; import Link from 'next/link'; import { AppHeader } from '../components/AppHeader'; -// import { ArchitectureDiagramWrapper } from '../components/home/ArchitectureDiagramWrapper'; -// import { FeaturesSection } from '../components/home/FeaturesSection'; +import { FeaturesSection } from '../components/home/FeaturesSection'; import { HeroSection } from '../components/home/HeroSection'; export const dynamic = 'force-dynamic'; @@ -136,9 +135,8 @@ export default async function HomePage(): Promise { {/* Hero Section - Dithered Design */} - {/* Features Section - Interactive & Animated - temporarily disabled + {/* Features Section */} - */} {/* Architecture Diagram Section - temporarily disabled
diff --git a/apps/web/src/components/home/FeaturesSection.tsx b/apps/web/src/components/home/FeaturesSection.tsx index 2aeff61..84bf869 100644 --- a/apps/web/src/components/home/FeaturesSection.tsx +++ b/apps/web/src/components/home/FeaturesSection.tsx @@ -5,432 +5,55 @@ import { Button } from '@tpmjs/ui/Button/Button'; import { Container } from '@tpmjs/ui/Container/Container'; import { Icon } from '@tpmjs/ui/Icon/Icon'; import Link from 'next/link'; -import { useEffect, useRef, useState } from 'react'; -import { ToolConnectionViz } from './ToolConnectionViz'; // ============================================================================ -// Animated Terminal Component -// ============================================================================ - -function AnimatedTerminal(): React.ReactElement { - const [currentLine, setCurrentLine] = useState(0); - const [displayedText, setDisplayedText] = useState(''); - const [isTyping, setIsTyping] = useState(true); - - const lines = [ - { type: 'input', text: '$ npx @tpmjs/tools-unsandbox' }, - { type: 'output', text: '✓ Tool loaded: executeCodeAsync' }, - { type: 'input', text: '$ execute --lang python --code "print(sum(range(100)))"' }, - { type: 'output', text: '→ Spinning up secure sandbox...' }, - { type: 'output', text: '→ Executing code...' }, - { type: 'success', text: '✓ Output: 4950' }, - { type: 'input', text: '$ _' }, - ]; - - useEffect(() => { - if (currentLine >= lines.length) { - // Reset after delay - const timeout = setTimeout(() => { - setCurrentLine(0); - setDisplayedText(''); - setIsTyping(true); - }, 3000); - return () => clearTimeout(timeout); - } - - const line = lines[currentLine]; - if (!line) return; - - if (line.type === 'input') { - // Type out input lines character by character - let charIndex = 0; - setIsTyping(true); - const interval = setInterval(() => { - if (charIndex <= line.text.length) { - setDisplayedText(line.text.slice(0, charIndex)); - charIndex++; - } else { - clearInterval(interval); - setIsTyping(false); - setTimeout(() => { - setCurrentLine((prev) => prev + 1); - setDisplayedText(''); - }, 500); - } - }, 50); - return () => clearInterval(interval); - } else { - // Show output lines instantly - setDisplayedText(line.text); - setIsTyping(false); - const timeout = setTimeout(() => { - setCurrentLine((prev) => prev + 1); - setDisplayedText(''); - }, 800); - return () => clearTimeout(timeout); - } - }, [currentLine]); - - const getLineColor = (type: string) => { - switch (type) { - case 'input': - return 'text-foreground'; - case 'output': - return 'text-foreground-secondary'; - case 'success': - return 'text-success'; - case 'error': - return 'text-error'; - default: - return 'text-foreground-secondary'; - } - }; - - return ( -
- {/* Terminal Header */} -
-
-
-
-
-
- - tpmjs terminal - -
- - {/* Terminal Content */} -
- {/* Previous lines */} - {lines.slice(0, currentLine).map((line, i) => ( -
- {line.text} -
- ))} - - {/* Current line being typed */} - {currentLine < lines.length && ( -
- {displayedText} - {isTyping && } -
- )} -
-
- ); -} - -// ============================================================================ -// Feature Card with Hover Animation +// Feature Card Component // ============================================================================ interface FeatureCardProps { icon: string; title: string; description: string; - stats?: string; - delay?: number; + badge?: string; + href?: string; } -function FeatureCard({ - icon, - title, - description, - stats, - delay = 0, -}: FeatureCardProps): React.ReactElement { - const [isVisible, setIsVisible] = useState(false); - const [isHovered, setIsHovered] = useState(false); - const ref = useRef(null); - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry?.isIntersecting) { - setTimeout(() => setIsVisible(true), delay); - } - }, - { threshold: 0.2 } - ); - - if (ref.current) { - observer.observe(ref.current); - } - - return () => observer.disconnect(); - }, [delay]); - - return ( -
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - className={` - relative p-6 border-2 border-dashed border-border bg-surface - transition-all duration-300 ease-out cursor-pointer - ${isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'} - ${isHovered ? 'border-primary bg-primary/5 shadow-[4px_4px_0_0_rgba(166,89,45,0.2)]' : ''} - `} - > - {/* Animated corner accent */} -
-
- +function FeatureCard({ icon, title, description, badge, href }: FeatureCardProps): React.ReactElement { + const content = ( +
{/* Icon */} -
+
{/* Content */} -

{title}

-

- {description} -

- - {/* Stats badge */} - {stats && ( - - {stats} - - )} +
+

+ {title} +

+ {badge && ( + + {badge} + + )} +
+

{description}

); -} -// ============================================================================ -// Animated Counter -// ============================================================================ - -interface AnimatedCounterProps { - end: number; - duration?: number; - suffix?: string; - prefix?: string; -} - -function AnimatedCounter({ - end, - duration = 2000, - suffix = '', - prefix = '', -}: AnimatedCounterProps): React.ReactElement { - const [count, setCount] = useState(0); - const [hasStarted, setHasStarted] = useState(false); - const ref = useRef(null); - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry?.isIntersecting && !hasStarted) { - setHasStarted(true); - } - }, - { threshold: 0.5 } + if (href) { + return ( + + {content} + ); + } - if (ref.current) { - observer.observe(ref.current); - } - - return () => observer.disconnect(); - }, [hasStarted]); - - useEffect(() => { - if (!hasStarted) return; - - let startTime: number; - let animationFrame: number; - - const animate = (timestamp: number) => { - if (!startTime) startTime = timestamp; - const progress = Math.min((timestamp - startTime) / duration, 1); - - // Easing function for smooth animation - const easeOutQuart = 1 - (1 - progress) ** 4; - setCount(Math.floor(easeOutQuart * end)); - - if (progress < 1) { - animationFrame = requestAnimationFrame(animate); - } - }; - - animationFrame = requestAnimationFrame(animate); - return () => cancelAnimationFrame(animationFrame); - }, [hasStarted, end, duration]); - - return ( - - {prefix} - {count.toLocaleString()} - {suffix} - - ); -} - -// ============================================================================ -// Flow Diagram Component -// ============================================================================ - -function FlowDiagram(): React.ReactElement { - const [activeStep, setActiveStep] = useState(0); - - useEffect(() => { - const interval = setInterval(() => { - setActiveStep((prev) => (prev + 1) % 4); - }, 2000); - return () => clearInterval(interval); - }, []); - - const steps = [ - { label: 'npm publish', icon: 'box', desc: 'publish to npm' }, - { label: 'auto-discover', icon: 'search', desc: 'indexed in minutes' }, - { label: 'validate', icon: 'check', desc: 'health checks run' }, - { label: 'available', icon: 'globe', desc: 'ready for agents' }, - ]; - - return ( -
- {steps.map((step, i) => ( -
- {/* Step */} -
-
- -
- - {step.label} - - {step.desc} - - {/* Pulse ring when active */} - {activeStep === i && ( -
-
-
- )} -
- - {/* Arrow */} - {i < steps.length - 1 && ( -
-
i ? 'bg-primary' : 'bg-border'} - `} - /> -
i ? 'border-l-primary' : 'border-l-border'} - `} - /> -
- )} -
- ))} -
- ); -} - -// ============================================================================ -// Interactive Tool Grid -// ============================================================================ - -function InteractiveToolGrid(): React.ReactElement { - const tools = [ - { name: 'web-scraper', category: 'web', color: 'bg-info' }, - { name: 'code-executor', category: 'sandbox', color: 'bg-success' }, - { name: 'pdf-parser', category: 'data', color: 'bg-warning' }, - { name: 'image-gen', category: 'ai', color: 'bg-error' }, - { name: 'db-query', category: 'data', color: 'bg-info' }, - { name: 'api-caller', category: 'web', color: 'bg-success' }, - { name: 'file-convert', category: 'utilities', color: 'bg-warning' }, - { name: 'text-analyze', category: 'ai', color: 'bg-error' }, - { name: 'email-send', category: 'integration', color: 'bg-info' }, - ]; - - const [hoveredTool, setHoveredTool] = useState(null); - - return ( -
- {tools.map((tool, i) => ( -
setHoveredTool(tool.name)} - onMouseLeave={() => setHoveredTool(null)} - className={` - relative p-3 border border-dashed border-border bg-surface - transition-all duration-300 cursor-pointer - ${hoveredTool === tool.name ? 'border-primary scale-105 z-10 shadow-lg' : ''} - `} - style={{ - animationDelay: `${i * 100}ms`, - }} - > -
-
- {tool.name} -
- {hoveredTool === tool.name && ( -
- {tool.category} -
- )} -
- ))} -
- ); + return content; } // ============================================================================ @@ -438,175 +61,122 @@ function InteractiveToolGrid(): React.ReactElement { // ============================================================================ export function FeaturesSection(): React.ReactElement { + const features = [ + { + icon: 'search', + title: 'tool registry', + description: + 'Browse 1M+ AI tools from npm. Auto-discovered within minutes of publication with quality scoring and health monitoring.', + badge: 'auto-sync', + href: '/tool/tool-search', + }, + { + icon: 'puzzle', + title: 'omega agent', + description: + 'Chat with an AI that dynamically discovers and executes tools based on your requests. No configuration needed.', + badge: 'live', + href: '/omega', + }, + { + icon: 'folder', + title: 'collections', + description: + 'Curate tool sets for specific use cases. Add test scenarios to validate behavior and generate living documentation.', + badge: 'shareable', + href: '/collections', + }, + { + icon: 'user', + title: 'custom agents', + description: + 'Build AI agents with your choice of LLM, custom prompts, and curated tool collections. Share publicly or keep private.', + badge: 'unlimited', + href: '/agents', + }, + { + icon: 'link', + title: 'mcp protocol', + description: + 'Works with Claude Desktop, Cursor, Windsurf, and any MCP-compatible client. One URL, instant access to all tools.', + badge: 'universal', + href: '/integrations', + }, + { + icon: 'key', + title: 'secure execution', + description: + 'Every tool runs in an isolated sandbox with rate limiting and timeout handling. Your credentials are encrypted at rest.', + badge: 'sandboxed', + }, + { + icon: 'checkCircle', + title: 'test scenarios', + description: + 'AI-generated test scenarios validate tool behavior. Track pass rates, execution times, and quality scores.', + badge: 'automated', + href: '/scenarios', + }, + { + icon: 'message', + title: 'living skills', + description: + 'Documentation that evolves from real usage. Skills emerge from question patterns and proven behaviors.', + badge: 'new', + href: '/docs/skills', + }, + { + icon: 'terminal', + title: 'developer sdk', + description: + 'Publish tools with one keyword. Full TypeScript support, Vercel AI SDK integration, and automatic schema extraction.', + badge: 'npm', + href: '/publish', + }, + ]; + return ( -
- {/* Subtle grid background */} -
-
-
- - +
+ {/* Section Header */} -
+

+ platform capabilities +

+

everything you need -

-

- powerful features

-

+

From discovery to execution, TPMJS provides the complete infrastructure for AI tool - development. + development and deployment.

- {/* Stats Row */} -
- {[ - { value: 170, suffix: '+', label: 'tools indexed' }, - { value: 15, suffix: ' min', label: 'discovery time' }, - { value: 99, suffix: '%', label: 'uptime' }, - { value: 4, suffix: '', label: 'mcp clients' }, - ].map((stat, i) => ( -
-
- -
-
- {stat.label} -
-
+ {/* Feature Grid */} +
+ {features.map((feature) => ( + ))}
- {/* Main Feature Grid */} -
- {/* Left: Terminal Demo */} -
-
- - live execution - -

- Execute any tool directly from your terminal or AI agent. Secure sandboxed execution - with real-time streaming output. -

- -
-
- - {/* Right: Tool Grid */} -
-
- - tool registry - -

- Browse 170+ tools across multiple categories. Each tool is validated, documented, - and ready to use. -

- -
- - - -
-
-
-
- - {/* Flow Diagram */} -
- - how it works - - -
- - {/* Interactive Connection Visualization */} -
- - tools → tpmjs → agents - -

- TPMJS acts as the central hub connecting npm packages to AI agents. Watch data flow in - real-time as tools serve agent requests. -

- -
- - {/* Feature Cards Grid */} -
- - - - - - -
- {/* CTA */}
- - - -
From 19417964deeff05a76d11bbc827d26a35c7c130b Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Mon, 26 Jan 2026 02:41:45 +1000 Subject: [PATCH 07/54] wip --- packages/video/.gitignore | 8 + packages/video/package.json | 3 +- packages/video/src/FeaturesVideo.tsx | 55 +++ packages/video/src/Root.tsx | 27 +- .../features/CollectionsFeatureScene.tsx | 271 +++++++++++++++ .../src/scenes/features/CustomAgentsScene.tsx | 268 +++++++++++++++ .../src/scenes/features/DeveloperSDKScene.tsx | 321 ++++++++++++++++++ .../scenes/features/FeaturesClosingScene.tsx | 208 ++++++++++++ .../scenes/features/FeaturesOpeningScene.tsx | 160 +++++++++ .../src/scenes/features/LivingSkillsScene.tsx | 311 +++++++++++++++++ .../src/scenes/features/MCPProtocolScene.tsx | 298 ++++++++++++++++ .../src/scenes/features/OmegaAgentScene.tsx | 265 +++++++++++++++ .../scenes/features/SecureExecutionScene.tsx | 261 ++++++++++++++ .../scenes/features/TestScenariosScene.tsx | 284 ++++++++++++++++ .../src/scenes/features/ToolRegistryScene.tsx | 286 ++++++++++++++++ 15 files changed, 3017 insertions(+), 9 deletions(-) create mode 100644 packages/video/.gitignore create mode 100644 packages/video/src/FeaturesVideo.tsx create mode 100644 packages/video/src/scenes/features/CollectionsFeatureScene.tsx create mode 100644 packages/video/src/scenes/features/CustomAgentsScene.tsx create mode 100644 packages/video/src/scenes/features/DeveloperSDKScene.tsx create mode 100644 packages/video/src/scenes/features/FeaturesClosingScene.tsx create mode 100644 packages/video/src/scenes/features/FeaturesOpeningScene.tsx create mode 100644 packages/video/src/scenes/features/LivingSkillsScene.tsx create mode 100644 packages/video/src/scenes/features/MCPProtocolScene.tsx create mode 100644 packages/video/src/scenes/features/OmegaAgentScene.tsx create mode 100644 packages/video/src/scenes/features/SecureExecutionScene.tsx create mode 100644 packages/video/src/scenes/features/TestScenariosScene.tsx create mode 100644 packages/video/src/scenes/features/ToolRegistryScene.tsx diff --git a/packages/video/.gitignore b/packages/video/.gitignore new file mode 100644 index 0000000..a9a5b50 --- /dev/null +++ b/packages/video/.gitignore @@ -0,0 +1,8 @@ +# Rendered video outputs +out/ +*.mp4 +*.webm +*.mov + +# Remotion cache +.remotion/ diff --git a/packages/video/package.json b/packages/video/package.json index 49e111e..d414dfe 100644 --- a/packages/video/package.json +++ b/packages/video/package.json @@ -5,7 +5,8 @@ "description": "Marketing videos for TPMJS", "scripts": { "dev": "remotion studio", - "render": "remotion render MarketingVideo out/marketing-video.mp4", + "render": "remotion render TPMJSExplainer out/explainer-video.mp4", + "render:features": "remotion render TPMJSFeatures out/features-video.mp4", "upgrade": "remotion upgrade" }, "dependencies": { diff --git a/packages/video/src/FeaturesVideo.tsx b/packages/video/src/FeaturesVideo.tsx new file mode 100644 index 0000000..5d9e938 --- /dev/null +++ b/packages/video/src/FeaturesVideo.tsx @@ -0,0 +1,55 @@ +import { AbsoluteFill, Sequence, useVideoConfig } from 'remotion'; +import { colors } from './design-tokens'; + +// Scene imports +import { FeaturesOpeningScene } from './scenes/features/FeaturesOpeningScene'; +import { ToolRegistryScene } from './scenes/features/ToolRegistryScene'; +import { OmegaAgentScene } from './scenes/features/OmegaAgentScene'; +import { CollectionsFeatureScene } from './scenes/features/CollectionsFeatureScene'; +import { CustomAgentsScene } from './scenes/features/CustomAgentsScene'; +import { MCPProtocolScene } from './scenes/features/MCPProtocolScene'; +import { SecureExecutionScene } from './scenes/features/SecureExecutionScene'; +import { TestScenariosScene } from './scenes/features/TestScenariosScene'; +import { LivingSkillsScene } from './scenes/features/LivingSkillsScene'; +import { DeveloperSDKScene } from './scenes/features/DeveloperSDKScene'; +import { FeaturesClosingScene } from './scenes/features/FeaturesClosingScene'; + +/** + * TPMJS Features Showcase Video + * Total: 2700 frames @ 30fps = 90 seconds + * + * A punchy showcase of all 9 platform features + */ +export const FeaturesVideo = () => { + const { fps } = useVideoConfig(); + + // Scene timing (in seconds) + const scenes = [ + { component: FeaturesOpeningScene, start: 0, duration: 6 }, + { component: ToolRegistryScene, start: 6, duration: 9 }, + { component: OmegaAgentScene, start: 15, duration: 9 }, + { component: CollectionsFeatureScene, start: 24, duration: 8 }, + { component: CustomAgentsScene, start: 32, duration: 8 }, + { component: MCPProtocolScene, start: 40, duration: 9 }, + { component: SecureExecutionScene, start: 49, duration: 8 }, + { component: TestScenariosScene, start: 57, duration: 8 }, + { component: LivingSkillsScene, start: 65, duration: 8 }, + { component: DeveloperSDKScene, start: 73, duration: 9 }, + { component: FeaturesClosingScene, start: 82, duration: 8 }, + ]; + + return ( + + {scenes.map(({ component: Component, start, duration }, index) => ( + + + + ))} + + ); +}; diff --git a/packages/video/src/Root.tsx b/packages/video/src/Root.tsx index 2f1b3df..a7b661d 100644 --- a/packages/video/src/Root.tsx +++ b/packages/video/src/Root.tsx @@ -1,15 +1,26 @@ import { Composition } from 'remotion'; import { ExplainerVideo } from './ExplainerVideo'; +import { FeaturesVideo } from './FeaturesVideo'; export const RemotionRoot = () => { return ( - + <> + + + ); }; diff --git a/packages/video/src/scenes/features/CollectionsFeatureScene.tsx b/packages/video/src/scenes/features/CollectionsFeatureScene.tsx new file mode 100644 index 0000000..33ab429 --- /dev/null +++ b/packages/video/src/scenes/features/CollectionsFeatureScene.tsx @@ -0,0 +1,271 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Feature 3: Collections (0:24 - 0:32) + * Curate tool sets for specific use cases + */ + +const CollectionCard = ({ + name, + toolCount, + category, + delay, + isActive = false, +}: { + name: string; + toolCount: number; + category: string; + delay: number; + isActive?: boolean; +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.snappy, + }); + + return ( +
+
+
+ {name} +
+
+ {category} +
+
+
+
+ {Array.from({ length: Math.min(toolCount, 5) }).map((_, i) => ( +
+ ))} + {toolCount > 5 && ( + + +{toolCount - 5} + + )} +
+ + {toolCount} tools + +
+
+ ); +}; + +export const CollectionsFeatureScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const headerProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + const collections = [ + { name: 'data-science-kit', toolCount: 12, category: 'analytics' }, + { name: 'content-creation', toolCount: 8, category: 'marketing' }, + { name: 'devops-automation', toolCount: 15, category: 'infra' }, + ]; + + return ( + + {/* Feature badge */} +
+
+ 📁 +
+
+
+ Feature 03 +
+
+ Collections +
+
+
+ shareable +
+
+ + {/* Main content */} +
+ {/* Left - Description */} +
+
+ Curate tool sets +
+ for specific{' '} + use cases +
+
+ Add test scenarios to validate behavior. +
+ Generate living documentation automatically. +
+
+ + {/* Right - Collection cards */} +
+ {collections.map((collection, i) => ( + + ))} +
+
+
+ ); +}; diff --git a/packages/video/src/scenes/features/CustomAgentsScene.tsx b/packages/video/src/scenes/features/CustomAgentsScene.tsx new file mode 100644 index 0000000..0188e15 --- /dev/null +++ b/packages/video/src/scenes/features/CustomAgentsScene.tsx @@ -0,0 +1,268 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Feature 4: Custom Agents (0:32 - 0:40) + * Build AI agents with your choice of LLM + */ + +const AgentCard = ({ + name, + model, + tools, + status, + delay, +}: { + name: string; + model: string; + tools: number; + status: 'public' | 'private'; + delay: number; +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.snappy, + }); + + return ( +
+
+
+ {name[0].toUpperCase()} +
+
+
+ {name} +
+
+ {model} +
+
+
+ +
+ + {tools} tools configured + +
+ {status} +
+
+
+ ); +}; + +export const CustomAgentsScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const headerProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + const agents = [ + { name: 'Research Bot', model: 'claude-opus-4', tools: 8, status: 'public' as const }, + { name: 'Code Review', model: 'gpt-4o', tools: 5, status: 'private' as const }, + { name: 'Data Analyst', model: 'claude-sonnet-4', tools: 12, status: 'public' as const }, + ]; + + return ( + + {/* Feature badge */} +
+
+ 👤 +
+
+
+ Feature 04 +
+
+ Custom Agents +
+
+
+ unlimited +
+
+ + {/* Tagline */} +
+
+ Your LLM. Your prompts. +
+ Your tools. +
+
+ + {/* Agent cards */} +
+ {agents.map((agent, i) => ( + + ))} +
+ + {/* Bottom text */} +
+ + Share publicly or keep private — your choice + +
+
+ ); +}; diff --git a/packages/video/src/scenes/features/DeveloperSDKScene.tsx b/packages/video/src/scenes/features/DeveloperSDKScene.tsx new file mode 100644 index 0000000..0aa2cbd --- /dev/null +++ b/packages/video/src/scenes/features/DeveloperSDKScene.tsx @@ -0,0 +1,321 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Feature 9: Developer SDK (1:13 - 1:22) + * Publish tools with one keyword, TypeScript support + */ + +const CodeLine = ({ + content, + indent = 0, + delay, + highlight = false, +}: { + content: string; + indent?: number; + delay: number; + highlight?: boolean; +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.snappy, + }); + + return ( +
+ {content} +
+ ); +}; + +export const DeveloperSDKScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const headerProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + const codeLines = [ + { content: '// package.json', indent: 0, highlight: false }, + { content: '{', indent: 0, highlight: false }, + { content: '"name": "my-awesome-tool",', indent: 1, highlight: false }, + { content: '"keywords": ["tpmjs"],', indent: 1, highlight: true }, + { content: '"tpmjs": {', indent: 1, highlight: true }, + { content: '"tools": ["./src/tools.ts"]', indent: 2, highlight: true }, + { content: '}', indent: 1, highlight: false }, + { content: '}', indent: 0, highlight: false }, + ]; + + const features = [ + 'TypeScript support', + 'Vercel AI SDK integration', + 'Auto schema extraction', + 'Zero config publish', + ]; + + return ( + + {/* Feature badge */} +
+
+ {''} +
+
+
+ Feature 09 +
+
+ Developer SDK +
+
+
+ npm +
+
+ + {/* Main content */} +
+ {/* Left - Code block */} +
+
+
+
+
+
+
+ {codeLines.map((line, i) => ( + + ))} +
+ + {/* npm publish command */} +
+ $ + + npm publish + + + → auto-discovered in minutes + +
+
+ + {/* Right - Features */} +
+
+ One keyword. +
+ Instant discovery. +
+ +
+ {features.map((feature, i) => { + const featureProgress = spring({ + frame: frame - fps * 2 - i * 8, + fps, + config: springConfigs.snappy, + }); + + return ( +
+
+ ✓ +
+ + {feature} + +
+ ); + })} +
+
+
+ + ); +}; diff --git a/packages/video/src/scenes/features/FeaturesClosingScene.tsx b/packages/video/src/scenes/features/FeaturesClosingScene.tsx new file mode 100644 index 0000000..010c2b9 --- /dev/null +++ b/packages/video/src/scenes/features/FeaturesClosingScene.tsx @@ -0,0 +1,208 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, + Easing, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Features Closing Scene (1:22 - 1:30) + * Call to action with tpmjs.com + */ +export const FeaturesClosingScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + // All features revealed + const gridProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + // URL animation + const urlProgress = spring({ + frame: frame - fps * 1.5, + fps, + config: springConfigs.bouncy, + }); + + // CTA animation + const ctaProgress = spring({ + frame: frame - fps * 3, + fps, + config: springConfigs.snappy, + }); + + const features = [ + 'Tool Registry', + 'Omega Agent', + 'Collections', + 'Custom Agents', + 'MCP Protocol', + 'Secure Execution', + 'Test Scenarios', + 'Living Skills', + 'Developer SDK', + ]; + + // Pulsing glow + const glowIntensity = Math.sin(frame * 0.08) * 0.3 + 0.7; + + return ( + + {/* Radial gradient background */} +
+ + {/* Features grid (small) */} +
+ {features.map((feature, i) => ( +
+ {feature} +
+ ))} +
+ + {/* Main CTA */} +
+
+ Start building with AI tools +
+ + {/* URL box */} +
+
+ tpmjs.com +
+
+ + {/* Action buttons */} +
+
+ Try Omega Agent +
+
+ Read the Docs +
+
+
+ + {/* Bottom tagline */} +
+ + The Package Manager for AI Tools + +
+ + ); +}; diff --git a/packages/video/src/scenes/features/FeaturesOpeningScene.tsx b/packages/video/src/scenes/features/FeaturesOpeningScene.tsx new file mode 100644 index 0000000..233fcf1 --- /dev/null +++ b/packages/video/src/scenes/features/FeaturesOpeningScene.tsx @@ -0,0 +1,160 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, + Easing, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Features Opening Scene (0:00 - 0:06) + * Bold intro with TPMJS branding and tagline + */ +export const FeaturesOpeningScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + // Logo animation + const logoScale = spring({ + frame, + fps, + config: springConfigs.bouncy, + }); + + const logoRotate = interpolate(frame, [0, fps * 0.5], [-10, 0], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + easing: Easing.out(Easing.back(2)), + }); + + // Tagline animation + const taglineProgress = spring({ + frame: frame - fps * 1, + fps, + config: springConfigs.smooth, + }); + + // Feature count animation + const countProgress = spring({ + frame: frame - fps * 2, + fps, + config: springConfigs.snappy, + }); + + const featureCount = Math.floor( + interpolate(frame, [fps * 2, fps * 3.5], [0, 9], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + }) + ); + + // Copper line sweep + const lineWidth = interpolate(frame, [fps * 3, fps * 5], [0, 100], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + easing: Easing.out(Easing.cubic), + }); + + return ( + + {/* Gradient overlay */} +
+ + {/* Main content */} +
+ {/* Logo/Brand */} +
+ tpmjs +
+ + {/* Tagline */} +
+ The Package Manager for AI Tools +
+ + {/* Feature count */} +
+ + {featureCount} + + + Core Features + +
+
+ + {/* Bottom copper line */} +
+ + ); +}; diff --git a/packages/video/src/scenes/features/LivingSkillsScene.tsx b/packages/video/src/scenes/features/LivingSkillsScene.tsx new file mode 100644 index 0000000..bb82d3c --- /dev/null +++ b/packages/video/src/scenes/features/LivingSkillsScene.tsx @@ -0,0 +1,311 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Feature 8: Living Skills (1:05 - 1:13) + * Documentation that evolves from real usage + */ + +const SkillNode = ({ + label, + x, + y, + delay, + size = 'md', +}: { + label: string; + x: number; + y: number; + delay: number; + size?: 'sm' | 'md' | 'lg'; +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.bouncy, + }); + + const sizes = { + sm: { w: 100, h: 40, font: typography.fontSize.xs }, + md: { w: 140, h: 50, font: typography.fontSize.sm }, + lg: { w: 180, h: 60, font: typography.fontSize.base }, + }; + + const s = sizes[size]; + + return ( +
+ + {label} + +
+ ); +}; + +export const LivingSkillsScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const headerProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + // Skill nodes with positions + const skills = [ + { label: 'API Design', x: 800, y: 300, size: 'lg' as const, delay: fps * 1 }, + { label: 'Error Handling', x: 600, y: 200, size: 'md' as const, delay: fps * 1.5 }, + { label: 'Rate Limits', x: 1000, y: 200, size: 'md' as const, delay: fps * 1.8 }, + { label: 'Caching', x: 550, y: 400, size: 'sm' as const, delay: fps * 2.2 }, + { label: 'Auth', x: 700, y: 450, size: 'sm' as const, delay: fps * 2.5 }, + { label: 'Webhooks', x: 950, y: 420, size: 'sm' as const, delay: fps * 2.8 }, + { label: 'Pagination', x: 1100, y: 350, size: 'sm' as const, delay: fps * 3 }, + ]; + + // Connection lines progress + const linesProgress = interpolate(frame, [fps * 3, fps * 5], [0, 1], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + }); + + return ( + + {/* Feature badge */} +
+
+ 💬 +
+
+
+ Feature 08 +
+
+ Living Skills +
+
+
+ NEW +
+
+ + {/* Left description */} +
+
+ Documentation that{' '} + evolves +
+
+ Skills emerge from question patterns +
+ and proven behaviors in real usage. +
+
+ + {/* Skill graph */} + + {/* Connection lines */} + + + + + + + + + + + {/* Skill nodes */} + {skills.map((skill) => ( + + ))} + + {/* Bottom quote */} +
+ + "Skills, proven in the wild — not declared on paper" + +
+
+ ); +}; diff --git a/packages/video/src/scenes/features/MCPProtocolScene.tsx b/packages/video/src/scenes/features/MCPProtocolScene.tsx new file mode 100644 index 0000000..53b2b05 --- /dev/null +++ b/packages/video/src/scenes/features/MCPProtocolScene.tsx @@ -0,0 +1,298 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Feature 5: MCP Protocol (0:40 - 0:49) + * Works with Claude Desktop, Cursor, Windsurf, and any MCP client + */ + +const ClientLogo = ({ + name, + delay, + connected, +}: { + name: string; + delay: number; + connected: boolean; +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.bouncy, + }); + + const connectionProgress = spring({ + frame: frame - delay - fps * 0.5, + fps, + config: springConfigs.snappy, + }); + + return ( +
+
+ {name[0]} +
+
+ {name} +
+ {connected && ( +
+ connected +
+ )} +
+ ); +}; + +export const MCPProtocolScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const headerProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + const urlProgress = spring({ + frame: frame - fps * 2, + fps, + config: springConfigs.snappy, + }); + + const clients = [ + { name: 'Claude', connected: true }, + { name: 'Cursor', connected: true }, + { name: 'Windsurf', connected: true }, + { name: 'Any MCP', connected: false }, + ]; + + // Connection line animation + const lineProgress = interpolate(frame, [fps * 3, fps * 5], [0, 1], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + }); + + return ( + + {/* Feature badge */} +
+
+ 🔗 +
+
+
+ Feature 05 +
+
+ MCP Protocol +
+
+
+ universal +
+
+ + {/* Center URL box */} +
+
+
+ MCP Server URL +
+
+ tpmjs.com/mcp +
+
+ +
+ One URL. Instant access to all tools. +
+
+ + {/* Client logos */} +
+ {clients.map((client, i) => ( + + ))} +
+ + {/* Connection lines */} + + {clients.slice(0, 3).map((_, i) => { + const startX = 480 + i * 320; + const startY = 820; + const endX = 960; + const endY = 520; + + const path = `M ${startX} ${startY} Q ${startX} ${(startY + endY) / 2} ${endX} ${endY}`; + const pathLength = 400; // Approximate + + return ( + + ); + })} + +
+ ); +}; diff --git a/packages/video/src/scenes/features/OmegaAgentScene.tsx b/packages/video/src/scenes/features/OmegaAgentScene.tsx new file mode 100644 index 0000000..95edafc --- /dev/null +++ b/packages/video/src/scenes/features/OmegaAgentScene.tsx @@ -0,0 +1,265 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Feature 2: Omega Agent (0:15 - 0:24) + * AI that dynamically discovers and executes tools + */ + +const Message = ({ + role, + content, + delay, +}: { + role: 'user' | 'assistant'; + content: string; + delay: number; +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.snappy, + }); + + const isUser = role === 'user'; + + return ( +
+
+ {content} +
+
+ ); +}; + +const ToolPill = ({ name, delay }: { name: string; delay: number }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.bouncy, + }); + + return ( +
+
+ + {name} + +
+ ); +}; + +export const OmegaAgentScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const headerProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + const selectedTools = ['web-scraper', 'sentiment', 'chart-gen']; + + return ( + + {/* Feature badge */} +
+
+ 🧩 +
+
+
+ Feature 02 +
+
+ Omega Agent +
+
+
+ LIVE +
+
+ + {/* Chat interface */} +
+ + + {/* Tool selection */} + {frame > fps * 1.5 && ( +
+
+ Auto-selected tools: +
+
+ {selectedTools.map((tool, i) => ( + + ))} +
+
+ )} + + +
+ + {/* Bottom highlight */} +
+ + No configuration needed.{' '} + Just ask. + +
+
+ ); +}; diff --git a/packages/video/src/scenes/features/SecureExecutionScene.tsx b/packages/video/src/scenes/features/SecureExecutionScene.tsx new file mode 100644 index 0000000..dabadfe --- /dev/null +++ b/packages/video/src/scenes/features/SecureExecutionScene.tsx @@ -0,0 +1,261 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Feature 6: Secure Execution (0:49 - 0:57) + * Isolated sandboxes, rate limiting, encrypted credentials + */ + +const SecurityLayer = ({ + label, + icon, + delay, + index, +}: { + label: string; + icon: string; + delay: number; + index: number; +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.snappy, + }); + + const width = 300 - index * 40; + + return ( +
+ {icon} + + {label} + +
+ ); +}; + +export const SecureExecutionScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const headerProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + const shieldPulse = Math.sin(frame * 0.1) * 0.1 + 1; + + const layers = [ + { label: 'isolated sandbox', icon: '📦' }, + { label: 'rate limiting', icon: '⏱️' }, + { label: 'timeout handling', icon: '⚡' }, + { label: 'encrypted at rest', icon: '🔐' }, + ]; + + return ( + + {/* Feature badge */} +
+
+ 🔑 +
+
+
+ Feature 06 +
+
+ Secure Execution +
+
+
+ sandboxed +
+
+ + {/* Main content */} +
+ {/* Left - Shield visualization */} +
+
+ {/* Shield shape using CSS */} +
+
+ 🛡️ +
+
+
+
+ + {/* Right - Security layers */} +
+ {layers.map((layer, i) => ( + + ))} +
+
+ + {/* Bottom text */} +
+ + Every tool runs in{' '} + complete isolation + +
+
+ ); +}; diff --git a/packages/video/src/scenes/features/TestScenariosScene.tsx b/packages/video/src/scenes/features/TestScenariosScene.tsx new file mode 100644 index 0000000..73cf518 --- /dev/null +++ b/packages/video/src/scenes/features/TestScenariosScene.tsx @@ -0,0 +1,284 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Feature 7: Test Scenarios (0:57 - 1:05) + * AI-generated test scenarios, pass rates, execution times + */ + +const TestResult = ({ + name, + status, + time, + delay, +}: { + name: string; + status: 'pass' | 'fail' | 'running'; + time?: string; + delay: number; +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.snappy, + }); + + const statusColors = { + pass: colors.status.success, + fail: colors.status.error, + running: colors.status.warning, + }; + + const statusIcons = { + pass: '✓', + fail: '✕', + running: '○', + }; + + return ( +
+ + {statusIcons[status]} + + + {name} + + {time && ( + + {time} + + )} +
+ ); +}; + +export const TestScenariosScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const headerProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + // Animated progress bar + const passRate = Math.min( + 94, + interpolate(frame, [fps * 2, fps * 4], [0, 94], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + }) + ); + + const tests = [ + { name: 'search_basic_query', status: 'pass' as const, time: '124ms' }, + { name: 'search_with_filters', status: 'pass' as const, time: '89ms' }, + { name: 'handle_empty_results', status: 'pass' as const, time: '45ms' }, + { name: 'validate_api_key', status: 'fail' as const, time: '2.1s' }, + { name: 'rate_limit_handling', status: 'pass' as const, time: '312ms' }, + ]; + + return ( + + {/* Feature badge */} +
+
+ ✓ +
+
+
+ Feature 07 +
+
+ Test Scenarios +
+
+
+ automated +
+
+ + {/* Main content */} +
+ {/* Left - Pass rate */} +
+
+ Pass Rate +
+
+ {Math.floor(passRate)}% +
+ + {/* Progress bar */} +
+
+
+ +
+ AI-generated tests validate +
+ tool behavior automatically +
+
+ + {/* Right - Test results */} +
+
+ Recent Tests +
+ {tests.map((test, i) => ( + + ))} +
+
+ + ); +}; diff --git a/packages/video/src/scenes/features/ToolRegistryScene.tsx b/packages/video/src/scenes/features/ToolRegistryScene.tsx new file mode 100644 index 0000000..b86e769 --- /dev/null +++ b/packages/video/src/scenes/features/ToolRegistryScene.tsx @@ -0,0 +1,286 @@ +import { + AbsoluteFill, + interpolate, + spring, + useCurrentFrame, + useVideoConfig, +} from 'remotion'; +import { colors, typography, springConfigs } from '../../design-tokens'; + +/** + * Feature 1: Tool Registry (0:06 - 0:15) + * Browse 1M+ AI tools from npm + */ + +const ToolCard = ({ + name, + downloads, + score, + delay, +}: { + name: string; + downloads: string; + score: number; + delay: number; +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - delay, + fps, + config: springConfigs.snappy, + }); + + return ( +
+
+
+ {name} +
+
+ {downloads} downloads/mo +
+
+
+
+
+
+ + {score}% + +
+
+ ); +}; + +export const ToolRegistryScene = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const headerProgress = spring({ + frame, + fps, + config: springConfigs.smooth, + }); + + const counterValue = Math.floor( + interpolate(frame, [fps * 0.5, fps * 3], [0, 1847293], { + extrapolateLeft: 'clamp', + extrapolateRight: 'clamp', + }) + ); + + const tools = [ + { name: '@anthropic/claude-sdk', downloads: '2.1M', score: 98 }, + { name: 'firecrawl-aisdk', downloads: '847K', score: 95 }, + { name: 'openai-tool-kit', downloads: '1.2M', score: 92 }, + { name: 'langchain-tools', downloads: '654K', score: 89 }, + ]; + + return ( + + {/* Feature badge */} +
+
+ 🔍 +
+
+
+ Feature 01 +
+
+ Tool Registry +
+
+
+ + {/* Main content */} +
+ {/* Left - Stats */} +
+
+ {counterValue.toLocaleString()}+ +
+
+ AI tools auto-discovered from npm +
+ + {/* Badges */} +
+ {['auto-sync', 'quality scores', 'health monitoring'].map((badge) => ( +
+ {badge} +
+ ))} +
+
+ + {/* Right - Tool list */} +
+ {tools.map((tool, i) => ( + + ))} +
+
+ + {/* Bottom tagline */} +
+ + Updated within minutes of npm publication + +
+
+ ); +}; From 13e4fd954d42674a2ebb700b4123c010029d9337 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Mon, 26 Jan 2026 02:59:18 +1000 Subject: [PATCH 08/54] chore(web): remove react-grab dev tooling --- apps/web/package.json | 2 +- apps/web/src/app/layout.tsx | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index d8a5c3a..49693d9 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "postinstall": "prisma generate --schema=../../packages/db/prisma/schema.prisma", - "dev": "npx @react-grab/claude-code@latest && next dev", + "dev": "next dev", "build": "prisma generate --schema=../../packages/db/prisma/schema.prisma && next build", "start": "next start", "lint": "eslint .", diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index cbf14a1..f385cab 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,7 +1,6 @@ import { Analytics } from '@vercel/analytics/next'; import type { Metadata } from 'next'; import { Space_Grotesk, Space_Mono } from 'next/font/google'; -import Script from 'next/script'; import { Toaster } from 'sonner'; import { AppFooter } from '../components/AppFooter'; import { ThemeProvider } from '../components/providers/ThemeProvider'; @@ -130,18 +129,6 @@ export default function RootLayout({ className={`${spaceGrotesk.variable} ${spaceMono.variable}`} > - {process.env.NODE_ENV === 'development' && ( - <> - + + + diff --git a/apps/web/src/components/tech/TechDiagram.tsx b/apps/web/src/components/tech/TechDiagram.tsx index 6cc187e..c0fde99 100644 --- a/apps/web/src/components/tech/TechDiagram.tsx +++ b/apps/web/src/components/tech/TechDiagram.tsx @@ -1,63 +1,43 @@ 'use client'; +import { useState } from 'react'; import { Spinner } from '@tpmjs/ui/Spinner/Spinner'; -import dynamic from 'next/dynamic'; -import { Component, type ReactNode } from 'react'; -import { techDiagramData } from './techDiagramData'; -// Isoflow uses canvas/paper.js and must be loaded client-side only -const Isoflow = dynamic(() => import('isoflow'), { - ssr: false, - loading: () => ( -
- -
- ), -}); - -// ── Error Boundary ────────────────────────────────────────────────── -interface ErrorBoundaryState { - hasError: boolean; -} - -class DiagramErrorBoundary extends Component<{ children: ReactNode }, ErrorBoundaryState> { - constructor(props: { children: ReactNode }) { - super(props); - this.state = { hasError: false }; - } - - static getDerivedStateFromError(): ErrorBoundaryState { - return { hasError: true }; - } - - render() { - if (this.state.hasError) { - return ( -
-

The interactive diagram failed to load.

- - View the alternative architecture diagram → - -
- ); - } - return this.props.children; - } -} - -// ── Main Component ────────────────────────────────────────────────── export function TechDiagram() { + const [isLoading, setIsLoading] = useState(true); + const [hasError, setHasError] = useState(false); + + if (hasError) { + return ( +
+

+ The interactive diagram failed to load. +

+ + View the alternative architecture diagram → + +
+ ); + } + return ( -
- - - +
+ {isLoading && ( +
+ +
+ )} +