From 6f289a094b4821230e8fbd175a4f10459661fc22 Mon Sep 17 00:00:00 2001 From: Jacques de Villiers Date: Thu, 26 Mar 2026 12:23:44 +0200 Subject: [PATCH 1/3] Update Docusaurus configuration to use environment variables for URL and base URL --- docusaurus.config.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 90bfac0..c37de88 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -7,6 +7,9 @@ const redirects = [ {from: ['/cli/readme'], to: '/cli'}, ]; +const url = process.env.DOCUSAURUS_URL || 'http://localhost'; +const baseUrl = process.env.DOCUSAURUS_BASE_URL || '/'; + // Converts GitBook-flavoured card tables to card grid divs. // MDX parses raw HTML as mdxJsxFlowElement nodes, so we work with the MDX AST. // Input: ...
@@ -116,8 +119,8 @@ const config: Config = { v4: true, }, - url: 'https://preview.ritza.co', - baseUrl: '/cc-new/', + url, + baseUrl, trailingSlash: true, onBrokenLinks: 'warn', markdown: { From 89a568cc5080b67755d37c28b5211be546911ef0 Mon Sep 17 00:00:00 2001 From: Lewis Dwyer Date: Mon, 30 Mar 2026 08:54:33 +0200 Subject: [PATCH 2/3] Reduce cover image height on category index pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Halve the max-height of cover images (420px → 210px) on category index pages (products, platform, tutorials, etc.) by adding a doc-cover--compact modifier class. Tutorial covers remain unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/css/custom.css | 8 ++++++++ src/theme/DocItem/Content/index.tsx | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/css/custom.css b/src/css/custom.css index a2d26dd..9d09d69 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -428,6 +428,14 @@ body { display: block; } +.doc-cover--compact { + max-height: 210px; +} + +.doc-cover--compact .doc-cover__img { + max-height: 210px; +} + /* ============================================================ COPY PAGE BUTTON ============================================================ */ diff --git a/src/theme/DocItem/Content/index.tsx b/src/theme/DocItem/Content/index.tsx index 46f112b..285b612 100644 --- a/src/theme/DocItem/Content/index.tsx +++ b/src/theme/DocItem/Content/index.tsx @@ -61,7 +61,7 @@ export default function ContentWrapper(props: Props): ReactNode { return ( <> {cover && ( -
+
)} From ee7168e553fa210365c5efccbe1cac3f89cb3dcf Mon Sep 17 00:00:00 2001 From: Gareth Dwyer Date: Tue, 31 Mar 2026 15:07:02 +0200 Subject: [PATCH 3/3] scalar openapi --- .../agent-capsule/chat/agent-api-sample.md | 27 - .../agent-capsule/chat/agent-api-sample.mdx | 11 + package-lock.json | 133 ++- package.json | 1 + .../ScalarEmbeddedApiReference.module.css | 74 ++ src/components/ScalarEmbeddedApiReference.tsx | 285 ++++++ src/lib/agentApiReference.ts | 202 ++++ static/openapi/agent-capsule-swagger.json | 861 ++++++++++++++++++ 8 files changed, 1563 insertions(+), 31 deletions(-) delete mode 100644 docs/products/agent-capsule/chat/agent-api-sample.md create mode 100644 docs/products/agent-capsule/chat/agent-api-sample.mdx create mode 100644 src/components/ScalarEmbeddedApiReference.module.css create mode 100644 src/components/ScalarEmbeddedApiReference.tsx create mode 100644 src/lib/agentApiReference.ts create mode 100644 static/openapi/agent-capsule-swagger.json diff --git a/docs/products/agent-capsule/chat/agent-api-sample.md b/docs/products/agent-capsule/chat/agent-api-sample.md deleted file mode 100644 index fdf82ac..0000000 --- a/docs/products/agent-capsule/chat/agent-api-sample.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -slug: "/products/agent-capsule/chat/agent-api-sample" ---- - -# Agent API (Sample) - -The following specifications describe a standard API used by the Code Capsules chat window to communicate with the agent. - -[OpenAPI code-capsules-api](https://cc-agent-fork-setx.ovh-test.ccdns.co/swagger.json) - - -[OpenAPI code-capsules-api](https://cc-agent-fork-setx.ovh-test.ccdns.co/swagger.json) - - -[OpenAPI code-capsules-api](https://cc-agent-fork-setx.ovh-test.ccdns.co/swagger.json) - - -[OpenAPI code-capsules-api](https://cc-agent-fork-setx.ovh-test.ccdns.co/swagger.json) - - -[OpenAPI code-capsules-api](https://cc-agent-fork-setx.ovh-test.ccdns.co/swagger.json) - - -## Schemas - -[OpenAPI code-capsules-api](https://cc-agent-fork-setx.ovh-test.ccdns.co/swagger.json) - diff --git a/docs/products/agent-capsule/chat/agent-api-sample.mdx b/docs/products/agent-capsule/chat/agent-api-sample.mdx new file mode 100644 index 0000000..9a508df --- /dev/null +++ b/docs/products/agent-capsule/chat/agent-api-sample.mdx @@ -0,0 +1,11 @@ +--- +title: Agent API (Sample) +slug: /products/agent-capsule/chat/agent-api-sample +description: The following specifications describe a standard API used by the Code Capsules chat window to communicate with the agent. +hide_title: true +hide_table_of_contents: true +--- + +import ScalarEmbeddedApiReference from '@site/src/components/ScalarEmbeddedApiReference'; + + diff --git a/package-lock.json b/package-lock.json index 2afd3b3..fa3889b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@docusaurus/preset-classic": "3.9.2", "@easyops-cn/docusaurus-search-local": "^0.55.1", "@mdx-js/react": "^3.0.0", + "@scalar/docusaurus": "^0.8.5", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", "react": "^19.0.0", @@ -165,6 +166,7 @@ "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.49.2.tgz", "integrity": "sha512-y1IOpG6OSmTpGg/CT0YBb/EAhR2nsC18QWp9Jy8HO9iGySpcwaTvs5kHa17daP3BMTwWyaX9/1tDTDQshZzXdg==", "license": "MIT", + "peer": true, "dependencies": { "@algolia/client-common": "5.49.2", "@algolia/requester-browser-xhr": "5.49.2", @@ -290,6 +292,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -624,9 +627,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -2092,6 +2095,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2114,6 +2118,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -2223,6 +2228,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2644,6 +2650,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -3648,6 +3655,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz", "integrity": "sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg==", "license": "MIT", + "peer": true, "dependencies": { "@docusaurus/core": "3.9.2", "@docusaurus/logger": "3.9.2", @@ -3916,6 +3924,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.9.2.tgz", "integrity": "sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag==", "license": "MIT", + "peer": true, "dependencies": { "@docusaurus/mdx-loader": "3.9.2", "@docusaurus/module-type-aliases": "3.9.2", @@ -4031,6 +4040,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.9.2.tgz", "integrity": "sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ==", "license": "MIT", + "peer": true, "dependencies": { "@docusaurus/logger": "3.9.2", "@docusaurus/types": "3.9.2", @@ -4796,6 +4806,7 @@ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", "license": "MIT", + "peer": true, "dependencies": { "@types/mdx": "^2.0.0" }, @@ -5315,6 +5326,79 @@ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "license": "MIT" }, + "node_modules/@scalar/docusaurus": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@scalar/docusaurus/-/docusaurus-0.8.5.tgz", + "integrity": "sha512-rC06MTzJfchbtBH31D+O6J6df6Ej9UsCEHJccwBcV6bCDtDudxIzVCeUCl7Ei7rZ3MdSd5VXkroaiy15gbwZoA==", + "license": "MIT", + "dependencies": { + "@scalar/types": "0.7.5" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "@docusaurus/utils": "^3.9.2", + "react": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@scalar/helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@scalar/helpers/-/helpers-0.4.2.tgz", + "integrity": "sha512-IrgrGVSahCfYDNWITazz4Q1BOndp5eEzlimRkfxiYn++KqeWyLfALyym1omqcdKGYtiSx1KIbKaUJL9vkjaN7w==", + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@scalar/types": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@scalar/types/-/types-0.7.5.tgz", + "integrity": "sha512-LUR/+nNpHfMbGCqTnrzyRlgRTC4FMyyqwCYCFYVs57gkEWzmXnrx+r3+igrWtuZoK/Hsr+ffFMzbBvmxnTFH7g==", + "license": "MIT", + "dependencies": { + "@scalar/helpers": "0.4.2", + "nanoid": "^5.1.6", + "type-fest": "^5.3.1", + "zod": "^4.3.5" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@scalar/types/node_modules/nanoid": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", + "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@scalar/types/node_modules/type-fest": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz", + "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -5524,6 +5608,7 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -5887,6 +5972,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -6228,6 +6314,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6295,6 +6382,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -6340,6 +6428,7 @@ "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.49.2.tgz", "integrity": "sha512-1K0wtDaRONwfhL4h8bbJ9qTjmY6rhGgRvvagXkMBsAOMNr+3Q2SffHECh9DIuNVrMA1JwA0zCwhyepgBZVakng==", "license": "MIT", + "peer": true, "dependencies": { "@algolia/abtesting": "1.15.2", "@algolia/client-abtesting": "5.49.2", @@ -6819,6 +6908,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7790,6 +7880,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -9179,6 +9270,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -13642,6 +13734,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14233,6 +14326,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -15136,6 +15230,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -15952,6 +16047,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -15961,6 +16057,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -16016,6 +16113,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/react": "*" }, @@ -16044,6 +16142,7 @@ "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -17592,6 +17691,18 @@ "node": ">= 10" } }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -17804,7 +17915,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tsyringe": { "version": "4.10.0", @@ -17885,6 +17997,7 @@ "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -18235,6 +18348,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -18433,6 +18547,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -19052,6 +19167,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 0adf1fc..1daccd3 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@docusaurus/preset-classic": "3.9.2", "@easyops-cn/docusaurus-search-local": "^0.55.1", "@mdx-js/react": "^3.0.0", + "@scalar/docusaurus": "^0.8.5", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", "react": "^19.0.0", diff --git a/src/components/ScalarEmbeddedApiReference.module.css b/src/components/ScalarEmbeddedApiReference.module.css new file mode 100644 index 0000000..ce2376f --- /dev/null +++ b/src/components/ScalarEmbeddedApiReference.module.css @@ -0,0 +1,74 @@ +.shell { + width: 100%; +} + +.surface { + min-height: 60rem; +} + +.authBlock { + margin: 1.5rem 0; + border-top: 1px solid var(--ifm-color-emphasis-300); + padding-top: 1.5rem; +} + +.authHeader { + align-items: center; + display: flex; + gap: 1rem; + justify-content: space-between; + margin-bottom: 0.75rem; +} + +.authTitle { + color: var(--ifm-heading-color); + font-size: 1.1rem; + font-weight: 600; +} + +.authSelect { + background: var(--ifm-background-color); + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: 0.5rem; + color: var(--ifm-font-color-base); + font: inherit; + padding: 0.35rem 2rem 0.35rem 0.75rem; +} + +.authRow { + border-top: 1px solid var(--ifm-color-emphasis-300); + padding-top: 0.75rem; +} + +.authName { + color: var(--ifm-heading-color); + font-size: 1rem; + font-weight: 600; +} + +.authMeta { + color: var(--ifm-color-emphasis-700); + font-size: 0.95rem; + margin-left: 0.5rem; +} + +.authRequired { + color: #c07d1f; + font-size: 0.95rem; + margin-left: 0.5rem; +} + +.authDescription { + color: var(--ifm-color-emphasis-700); + margin-top: 0.5rem; +} + +.loading, +.error { + color: var(--ifm-font-color-base); + padding: 1rem 0; +} + +.loading { + opacity: 0.7; +} diff --git a/src/components/ScalarEmbeddedApiReference.tsx b/src/components/ScalarEmbeddedApiReference.tsx new file mode 100644 index 0000000..ffadbe1 --- /dev/null +++ b/src/components/ScalarEmbeddedApiReference.tsx @@ -0,0 +1,285 @@ +import {useEffect, useRef, useState} from 'react'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import '@scalar/docusaurus/dist/theme.css'; +import styles from './ScalarEmbeddedApiReference.module.css'; +import { + agentApiReferenceConfig, + buildAgentApiReferenceSpec, + type OpenApiSpec, +} from '../lib/agentApiReference'; + +declare global { + interface Window { + Scalar?: { + createApiReference: ( + element: HTMLElement, + configuration: Record, + ) => void; + }; + } +} + +const scalarScriptSrc = 'https://cdn.jsdelivr.net/npm/@scalar/api-reference'; +const clientLabelMap = new Map([ + ['HTTP/1.1', 'HTTP'], + ['Fetch', 'JavaScript'], + ['Requests', 'Python'], + ['Curl', 'cURL'], +]); + +function loadScalarScript(): Promise { + if (window.Scalar) { + return Promise.resolve(); + } + + const existing = document.querySelector( + `script[src="${scalarScriptSrc}"]`, + ); + + if (existing) { + return new Promise((resolve, reject) => { + existing.addEventListener('load', () => resolve(), {once: true}); + existing.addEventListener('error', () => reject(new Error('Failed to load Scalar.')), { + once: true, + }); + }); + } + + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = scalarScriptSrc; + script.async = true; + script.onload = () => resolve(); + script.onerror = () => reject(new Error('Failed to load Scalar.')); + document.body.appendChild(script); + }); +} + +function extractOperationFromSectionId(sectionId: string | null | undefined) { + if (!sectionId) { + return null; + } + + const match = /^api-\d+\/([A-Z]+)(\/.+)$/.exec(sectionId); + if (!match) { + return null; + } + + return { + method: match[1].toLowerCase(), + path: match[2], + }; +} + +function injectAuthorizationSections(root: HTMLDivElement, spec: OpenApiSpec) { + const securitySchemes = spec.components?.securitySchemes as + | Record + | undefined; + + if (!securitySchemes) { + return; + } + + root.querySelectorAll('section.section').forEach((section) => { + const operationRef = extractOperationFromSectionId(section.id); + if (!operationRef) { + return; + } + + const operation = spec.paths?.[operationRef.path]?.[ + operationRef.method as keyof NonNullable[string] + ] as Record | undefined; + + const security = Array.isArray(operation?.security) + ? (operation.security as Array>) + : []; + const availableSchemes = security.flatMap((entry) => Object.keys(entry)); + + if (availableSchemes.length === 0) { + return; + } + + const operationDetails = section.querySelector('.operation-details'); + const requestBody = operationDetails?.querySelector('.request-body'); + if (!operationDetails || !requestBody) { + return; + } + + const existing = operationDetails.querySelector(`.${styles.authBlock}`); + if (existing) { + existing.remove(); + } + + const block = document.createElement('div'); + block.className = styles.authBlock; + + const header = document.createElement('div'); + header.className = styles.authHeader; + + const title = document.createElement('div'); + title.className = styles.authTitle; + title.textContent = 'Authorizations'; + + const select = document.createElement('select'); + select.className = styles.authSelect; + + availableSchemes.forEach((schemeName) => { + const option = document.createElement('option'); + option.value = schemeName; + option.textContent = schemeName; + select.appendChild(option); + }); + + const defaultScheme = availableSchemes.includes('EmailAuth') + ? 'EmailAuth' + : availableSchemes[0]; + select.value = defaultScheme; + + const row = document.createElement('div'); + row.className = styles.authRow; + + const updateRow = (schemeName: string) => { + const scheme = securitySchemes[schemeName] ?? {}; + row.innerHTML = ''; + + const name = document.createElement('span'); + name.className = styles.authName; + name.textContent = scheme.name ?? schemeName; + + const meta = document.createElement('span'); + meta.className = styles.authMeta; + meta.textContent = scheme.type === 'apiKey' ? 'string' : scheme.type ?? 'string'; + + const required = document.createElement('span'); + required.className = styles.authRequired; + required.textContent = 'required'; + + const description = document.createElement('div'); + description.className = styles.authDescription; + description.textContent = scheme.description ?? ''; + + row.append(name, meta, required, description); + }; + + select.addEventListener('change', () => updateRow(select.value)); + updateRow(defaultScheme); + + header.append(title, select); + block.append(header, row); + operationDetails.insertBefore(block, requestBody); + }); +} + +function normalizeClientPickers(root: HTMLDivElement) { + const apply = () => { + root.querySelectorAll('[data-testid="client-picker"]').forEach((button) => { + const text = button.textContent?.trim() ?? ''; + for (const [needle, label] of clientLabelMap) { + if (text.includes(needle)) { + button.dataset.flatLabel = label; + break; + } + } + }); + + document.body + .querySelectorAll('ul[role="listbox"] [id$="-label"]') + .forEach((label) => { + label.style.display = 'none'; + }); + + document.body.querySelectorAll('ul[role="listbox"] li[role="option"]').forEach((item) => { + const text = item.textContent?.trim() ?? ''; + const value = Array.from(clientLabelMap.entries()).find(([needle]) => text.includes(needle)); + const labelNode = item.querySelector('span'); + if (value && labelNode) { + labelNode.textContent = value[1]; + } + }); + }; + + const observer = new MutationObserver(apply); + observer.observe(root, {childList: true, subtree: true}); + observer.observe(document.body, {childList: true, subtree: true}); + apply(); + + return () => observer.disconnect(); +} + +export default function ScalarEmbeddedApiReference() { + const specUrl = useBaseUrl('/openapi/agent-capsule-swagger.json'); + const ref = useRef(null); + const [status, setStatus] = useState<'loading' | 'ready' | 'error'>('loading'); + + useEffect(() => { + let cancelled = false; + let stopClientPickerObserver: (() => void) | undefined; + + async function renderReference() { + try { + const [_, response] = await Promise.all([loadScalarScript(), fetch(specUrl)]); + if (!response.ok) { + throw new Error(`Failed to fetch OpenAPI spec: ${response.status}`); + } + + const sourceSpec = (await response.json()) as OpenApiSpec; + const content = buildAgentApiReferenceSpec(sourceSpec); + + if (cancelled || !ref.current || !window.Scalar) { + return; + } + + ref.current.innerHTML = ''; + window.Scalar.createApiReference(ref.current, { + ...agentApiReferenceConfig, + content, + }); + stopClientPickerObserver = normalizeClientPickers(ref.current); + + let attempts = 0; + const intervalId = window.setInterval(() => { + if (!ref.current || cancelled) { + window.clearInterval(intervalId); + return; + } + + if (ref.current.querySelector('section.section')) { + injectAuthorizationSections(ref.current, content); + } + + attempts += 1; + if (attempts >= 20) { + window.clearInterval(intervalId); + } + }, 200); + setStatus('ready'); + } catch { + if (!cancelled) { + setStatus('error'); + } + } + } + + void renderReference(); + + return () => { + cancelled = true; + stopClientPickerObserver?.(); + if (ref.current) { + ref.current.innerHTML = ''; + } + }; + }, [specUrl]); + + return ( +
+ {status === 'loading' ? ( +

Loading API reference…

+ ) : null} + {status === 'error' ? ( +

Unable to load the API reference.

+ ) : null} +
+
+ ); +} diff --git a/src/lib/agentApiReference.ts b/src/lib/agentApiReference.ts new file mode 100644 index 0000000..610259b --- /dev/null +++ b/src/lib/agentApiReference.ts @@ -0,0 +1,202 @@ +type HttpMethod = + | 'get' + | 'post' + | 'put' + | 'patch' + | 'delete' + | 'options' + | 'head'; + +type OpenApiSchema = Record; +type OpenApiOperation = Record; +export type OpenApiSpec = { + openapi: string; + info?: Record; + security?: unknown[]; + servers?: unknown[]; + paths?: Record>>; + components?: { + schemas?: Record; + securitySchemes?: Record; + [key: string]: unknown; + }; + [key: string]: unknown; +}; + +const agentApiEndpoints: Array<{path: string; method: HttpMethod}> = [ + {path: '/api/chat/message', method: 'post'}, + {path: '/api/chat/message/stream', method: 'post'}, + {path: '/api/chat/history', method: 'get'}, + {path: '/api/context/text', method: 'post'}, + {path: '/api/context/url', method: 'post'}, +]; + +const agentApiSchemas = [ + 'ChatPrompt', + 'ChatPromptContentText', + 'ChatPromptContentImage', + 'ChatPromptContentFile', + 'SendMessageResponse', + 'AgentMessage', + 'AgentMessageContentText', + 'AgentMessageContentImage', + 'AgentMessageContentFile', + 'GetChatHistoryResponse', + 'ContextText', + 'AddContextTextResponse', + 'ContextUrl', + 'AddContextFromUrlResponse', + 'Error', +]; + +export const agentApiReferenceConfig = { + layout: 'modern', + theme: 'default', + withDefaultFonts: false, + showSidebar: false, + hideSearch: true, + hideClientButton: true, + hideTestRequestButton: true, + showDeveloperTools: 'never', + showToolbar: 'never', + documentDownloadType: 'none', + hiddenClients: { + c: true, + clojure: true, + csharp: true, + dart: true, + fsharp: true, + go: true, + java: true, + kotlin: true, + node: true, + objc: true, + ocaml: true, + php: true, + powershell: true, + r: true, + ruby: true, + rust: true, + swift: true, + shell: ['httpie', 'wget'], + js: ['axios', 'jquery', 'ofetch', 'undici', 'xhr'], + python: ['httpx_async', 'httpx_sync', 'python3'], + }, + defaultHttpClient: { + targetKey: 'http', + clientKey: 'http1.1', + }, + authentication: { + preferredSecurityScheme: 'ApiKeyAuth', + }, + customCss: ` + .scalar-mcp-layer, + a[href="https://www.scalar.com"] { + display: none !important; + } + + button[data-testid="client-picker"][data-flat-label] { + font-size: 0 !important; + } + + button[data-testid="client-picker"][data-flat-label]::before { + color: inherit; + content: attr(data-flat-label); + font-size: 1rem; + line-height: 1; + } + `, + agent: { + disabled: true, + hideAddApi: true, + }, +} as const; + +function collectSchemaRefs(input: unknown, refs = new Set()): Set { + if (!input || typeof input !== 'object') { + return refs; + } + + if (Array.isArray(input)) { + input.forEach((value) => collectSchemaRefs(value, refs)); + return refs; + } + + const record = input as Record; + const ref = record.$ref; + + if (typeof ref === 'string' && ref.startsWith('#/components/schemas/')) { + refs.add(ref.replace('#/components/schemas/', '')); + } + + Object.values(record).forEach((value) => collectSchemaRefs(value, refs)); + return refs; +} + +function orderedSchemaSubset( + allSchemas: Record | undefined, + seedNames: string[], +): Record { + if (!allSchemas) { + return {}; + } + + const queue = [...seedNames]; + const seen = new Set(); + const orderedNames: string[] = []; + + while (queue.length > 0) { + const current = queue.shift(); + if (!current || seen.has(current) || !allSchemas[current]) { + continue; + } + + seen.add(current); + orderedNames.push(current); + + for (const refName of collectSchemaRefs(allSchemas[current])) { + if (!seen.has(refName)) { + queue.push(refName); + } + } + } + + return Object.fromEntries(orderedNames.map((name) => [name, allSchemas[name]])); +} + +export function buildAgentApiReferenceSpec(source: OpenApiSpec): OpenApiSpec { + const filteredPaths = Object.fromEntries( + agentApiEndpoints.flatMap(({path, method}) => { + const operation = source.paths?.[path]?.[method]; + if (!operation) { + return []; + } + + const nextOperation: OpenApiOperation = {...operation}; + delete nextOperation.tags; + + return [[path, {[method]: nextOperation}]]; + }), + ) as OpenApiSpec['paths']; + + const referencedSchemas = new Set(agentApiSchemas); + collectSchemaRefs(filteredPaths, referencedSchemas); + + return { + openapi: source.openapi, + info: { + ...(source.info ?? {}), + title: 'Agent API (Sample)', + description: + 'The following specifications describe a standard API used by the Code Capsules chat window to communicate with the agent.', + }, + security: source.security, + servers: source.servers, + paths: filteredPaths, + components: { + ...source.components, + schemas: orderedSchemaSubset(source.components?.schemas, [...referencedSchemas]), + securitySchemes: source.components?.securitySchemes, + }, + }; +} diff --git a/static/openapi/agent-capsule-swagger.json b/static/openapi/agent-capsule-swagger.json new file mode 100644 index 0000000..eb57638 --- /dev/null +++ b/static/openapi/agent-capsule-swagger.json @@ -0,0 +1,861 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Code Capsules Agent API", + "version": "1.0.0", + "description": "API documentation for the Code Capsules Agent service" + }, + "servers": [ + { + "url": "http://localhost:3000", + "description": "Local development server" + } + ], + "tags": [ + { + "name": "Chat", + "description": "Chat endpoints for interacting with the AI agent" + }, + { + "name": "Context", + "description": "Context endpoints for managing RAG (Retrieval-Augmented Generation) context" + }, + { + "name": "Calendar", + "description": "Calendar endpoints for Google Calendar integration and authentication" + } + ], + "paths": { + "/api/chat/message": { + "post": { + "tags": ["Chat"], + "summary": "Send a message to the AI agent", + "description": "Send a message to the conversational AI agent and receive a complete response. For streaming responses, use the /message/stream endpoint instead.", + "operationId": "sendMessage", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "EmailAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatPrompt" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response from the AI agent", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SendMessageResponse" + } + } + } + }, + "400": { + "description": "Bad request - Message content is required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication headers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/api/chat/message/stream": { + "post": { + "tags": ["Chat"], + "summary": "Stream a message from the AI agent", + "description": "Send a message to the conversational AI agent and receive a streaming response. The response is sent as Server-Sent Events (SSE) with each chunk containing the type and content.", + "operationId": "streamMessage", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "EmailAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatPrompt" + } + } + } + }, + "responses": { + "200": { + "description": "Successful streaming response from the AI agent", + "content": { + "text/event-stream": { + "schema": { + "type": "string", + "description": "Server-Sent Events stream containing message chunks" + } + } + } + }, + "400": { + "description": "Bad request - Message is required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication headers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/api/chat/history": { + "get": { + "tags": ["Chat"], + "summary": "Get chat history for the authenticated user", + "description": "Retrieve the complete chat history for the currently authenticated user, including all previous conversations.", + "operationId": "getChatHistory", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "EmailAuth": [] + } + ], + "responses": { + "200": { + "description": "Chat history retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetChatHistoryResponse" + } + } + } + }, + "401": { + "description": "Unauthorized - User not authenticated or missing user information", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/api/context/text": { + "post": { + "tags": ["Context"], + "summary": "Add text context to the vector store", + "description": "Add text content to the vector store for use in RAG (Retrieval-Augmented Generation) context retrieval.", + "operationId": "addContextText", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "EmailAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContextText" + } + } + } + }, + "responses": { + "200": { + "description": "Context added successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddContextTextResponse" + } + } + } + }, + "400": { + "description": "Bad request - Context text is required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication headers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/api/context/url": { + "post": { + "tags": ["Context"], + "summary": "Add context from URL to the vector store", + "description": "Fetch content from a URL and add it to the vector store for use in RAG (Retrieval-Augmented Generation) context retrieval.", + "operationId": "addContextFromUrl", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "EmailAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContextUrl" + } + } + } + }, + "responses": { + "200": { + "description": "Context added successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddContextFromUrlResponse" + } + } + } + }, + "400": { + "description": "Bad request - URL is required", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication headers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/api/calendar/auth": { + "get": { + "tags": ["Calendar"], + "summary": "Get Google Calendar authentication URL", + "description": "Retrieve the Google OAuth authentication URL for the authenticated user. This URL should be used to initiate the OAuth flow for Google Calendar access.", + "operationId": "getCalendarAuthUrl", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "EmailAuth": [] + } + ], + "responses": { + "200": { + "description": "Authentication URL retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetAuthUrlResponse" + } + } + } + }, + "401": { + "description": "Unauthorized - User not authenticated or missing user information", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/api/calendar/auth/callback": { + "get": { + "tags": ["Calendar"], + "summary": "Handle Google Calendar OAuth callback", + "description": "Handle the OAuth callback from Google Calendar authentication. This endpoint processes the authorization code and saves the access token for the user.", + "operationId": "saveCalendarToken", + "parameters": [ + { + "name": "code", + "in": "query", + "required": true, + "description": "The authorization code returned by Google OAuth", + "schema": { + "type": "string" + } + }, + { + "name": "state", + "in": "query", + "required": true, + "description": "The state parameter containing the user ID", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Token saved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SaveTokenResponse" + } + } + } + }, + "400": { + "description": "Bad request - Missing required query parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "ApiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "X-CC-API-KEY", + "description": "API key for authentication" + }, + "EmailAuth": { + "type": "apiKey", + "in": "header", + "name": "X-CC-EMAIL", + "description": "Email for user identification (used with API key)" + } + }, + "schemas": { + "ChatPrompt": { + "type": "object", + "required": ["content", "date"], + "properties": { + "content": { + "type": "array", + "description": "Array of message content blocks (text, images, or files)", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ChatPromptContentText" + }, + { + "$ref": "#/components/schemas/ChatPromptContentImage" + }, + { + "$ref": "#/components/schemas/ChatPromptContentFile" + } + ] + } + }, + "date": { + "type": "string", + "format": "date-time", + "description": "Timestamp of the message", + "example": "2025-11-07T10:30:00Z" + } + } + }, + "AgentMessage": { + "type": "object", + "required": ["role", "content", "date"], + "properties": { + "role": { + "type": "string", + "enum": ["user", "assistant"], + "description": "The role of the message sender" + }, + "content": { + "type": "array", + "description": "Array of message content blocks", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/AgentMessageContentText" + }, + { + "$ref": "#/components/schemas/AgentMessageContentImage" + }, + { + "$ref": "#/components/schemas/AgentMessageContentFile" + } + ] + } + }, + "date": { + "type": "string", + "format": "date-time", + "description": "Timestamp of the message" + } + } + }, + "ChatPromptContentText": { + "type": "object", + "required": ["type", "text"], + "properties": { + "type": { + "type": "string", + "enum": ["text"], + "description": "The type of content block" + }, + "text": { + "type": "string", + "description": "The text content", + "example": "Hello there" + } + } + }, + "ChatPromptContentImage": { + "type": "object", + "required": ["type", "name", "base64"], + "properties": { + "type": { + "type": "string", + "enum": ["image"], + "description": "The type of content block" + }, + "name": { + "type": "string", + "description": "The filename of the image", + "example": "photo.jpg" + }, + "base64": { + "type": "string", + "description": "Base64 encoded image data", + "example": "iVBORw0KGgo...==" + } + } + }, + "ChatPromptContentFile": { + "type": "object", + "required": ["type", "name", "text"], + "properties": { + "type": { + "type": "string", + "enum": ["file"], + "description": "The type of content block" + }, + "name": { + "type": "string", + "description": "The filename of the file", + "example": "document.pdf" + }, + "text": { + "type": "string", + "description": "Text content of the file", + "example": "This is the text content of the file" + } + } + }, + "AgentMessageContentText": { + "type": "object", + "required": ["type", "text"], + "properties": { + "type": { + "type": "string", + "enum": ["text"], + "description": "The type of content block" + }, + "text": { + "type": "string", + "description": "The text content", + "example": "Hello there" + } + } + }, + "AgentMessageContentImage": { + "type": "object", + "required": ["type", "name", "base64"], + "properties": { + "type": { + "type": "string", + "enum": ["image"], + "description": "The type of content block" + }, + "name": { + "type": "string", + "description": "The filename of the image", + "example": "photo.jpg" + }, + "base64": { + "type": "string", + "description": "Base64 encoded image data", + "example": "iVBORw0KGgo...==" + } + } + }, + "AgentMessageContentFile": { + "type": "object", + "required": ["name", "text"], + "properties": { + "type": { + "type": "string", + "enum": ["file"], + "description": "The type of content block" + }, + "name": { + "type": "string", + "description": "The filename of the file", + "example": "document.pdf" + }, + "text": { + "type": "string", + "description": "Text content of the file", + "example": "This is the text content of the file" + } + } + }, + "ContextText": { + "type": "object", + "required": ["text"], + "properties": { + "text": { + "type": "string", + "description": "The text content to be added to the vector store" + } + } + }, + "ContextUrl": { + "type": "object", + "required": ["url"], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "The URL to fetch content from", + "example": "https://example.com/article" + } + } + }, + "ApiResponse": { + "type": "object", + "required": ["data", "success"], + "description": "Generic API response wrapper. Use endpoint-specific response schemas for exact data types.", + "properties": { + "data": { + "description": "The data to be returned (type varies by endpoint)" + }, + "success": { + "type": "boolean", + "description": "Whether the request was successful. Always true.", + "example": true + } + } + }, + "SendMessageResponse": { + "type": "object", + "required": ["data", "success"], + "properties": { + "data": { + "$ref": "#/components/schemas/AgentMessage" + }, + "success": { + "type": "boolean", + "description": "Whether the request was successful. Always true.", + "example": true + } + } + }, + "GetChatHistoryResponse": { + "type": "object", + "required": ["data", "success"], + "properties": { + "data": { + "type": "array", + "description": "Array of all chat messages in the user's history", + "items": { + "$ref": "#/components/schemas/AgentMessage" + } + }, + "success": { + "type": "boolean", + "description": "Whether the request was successful. Always true.", + "example": true + } + } + }, + "AddContextTextResponse": { + "type": "object", + "required": ["success"], + "properties": { + "success": { + "type": "boolean", + "description": "Whether the request was successful. Always true.", + "example": true + } + } + }, + "AddContextFromUrlResponse": { + "type": "object", + "required": ["success"], + "properties": { + "success": { + "type": "boolean", + "description": "Whether the request was successful. Always true.", + "example": true + } + } + }, + "Error": { + "type": "object", + "required": ["success", "error"], + "properties": { + "success": { + "type": "boolean", + "description": "Whether the request was successful. Always false.", + "example": false + }, + "error": { + "type": "string", + "description": "The error message", + "example": "Internal Server Error" + } + } + }, + "ChatResponseChunk": { + "type": "object", + "required": ["chunk"], + "description": "A chunk of streaming response data", + "properties": { + "chunk": { + "type": "string", + "description": "The chunk content", + "example": "Hello" + } + } + }, + "AgentMessageChunk": { + "type": "object", + "required": ["role", "chunk"], + "description": "A chunk of an agent message in a streaming response", + "properties": { + "role": { + "type": "string", + "enum": ["user", "assistant"], + "description": "The role of the message sender" + }, + "chunk": { + "type": "string", + "description": "The chunk content", + "example": "Hello" + } + } + }, + "AgentStreamChunkResponse": { + "type": "object", + "required": ["contentBlocks"], + "description": "Response structure for agent stream chunks", + "properties": { + "contentBlocks": { + "type": "array", + "description": "Array of content blocks in the stream chunk", + "items": { + "type": "object", + "required": ["type", "text"], + "properties": { + "type": { + "type": "string", + "description": "The type of content block" + }, + "text": { + "type": "string", + "description": "The text content" + } + } + } + } + } + }, + "GetAuthUrlResponse": { + "type": "object", + "required": ["data", "success"], + "properties": { + "data": { + "type": "object", + "required": ["url"], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "The Google OAuth authentication URL", + "example": "https://accounts.google.com/o/oauth2/v2/auth?..." + } + } + }, + "success": { + "type": "boolean", + "description": "Whether the request was successful. Always true.", + "example": true + } + } + }, + "SaveTokenResponse": { + "type": "object", + "required": ["data", "success"], + "properties": { + "data": { + "type": "object", + "required": ["message"], + "properties": { + "message": { + "type": "string", + "description": "Success message", + "example": "Token saved" + } + } + }, + "success": { + "type": "boolean", + "description": "Whether the request was successful. Always true.", + "example": true + } + } + } + } + } +}