| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
feat(connect): Corsair Connect — in-app OAuth flow for end users (#1209) * fix(auth): treat non-expiring tokens as fresh * feat(auth): add ReconnectRequiredError for hub reconnect responses * feat(react): add CorsairProvider and useConnect connect overlay * feat(react): detect connect completion via popup close and postMessage * fix(react): make connect completion detection correct and self-host-safe Ignore status polls from a closed or superseded connection attempt via an attempt-id guard, so a late poll can no longer settle the wrong promise. Drop the postMessage fast-path. The connect success page cannot pin a target origin to an arbitrary customer app, and the status poll plus popup-close watch already detect completion everywhere, including self-hosted and custom connect pages. * feat(connect): record connect-requests and serve them to the client When a tool call raises auth-missing, the binding writes a per-tenant row to corsair_connect_requests with the plugin and its connect link; the management handler exposes it (GET /connect/request, POST /connect/request/clear), tenant-scoped so end-user mode can't read across tenants. Registered in REQUIRED_TABLES so a missing table warns to run migrations instead of 500ing. * feat(react): Corsair Connect provider, boundary, and dialog Wrap the app once in <CorsairProvider>: an auth-missing failure surfaces a connect dialog and resumes the failed work once connected, with no per-call code at the call sites. <CorsairBoundary> covers server-rendered reads; useConnect exposes the proactive connect() and the call() mutation wrapper. Splits a browser bundle so the dialog renders inside any host app. * docs(database): add the corsair_connect_requests table Document the connect-request table across the SQLite, Postgres, Drizzle, and Prisma migrations and the core-tables reference. * fix(react): tenant-scope the connect watch and stop repeat settles The status poll and connect-request clear now use the tenant the link was minted for, so a proactive connect for a non-default tenant no longer watches the default one. A settled attempt bumps its id before running effects, so a slower poll from the same attempt can't refresh the router twice, and unmount tears down the popup, timers, and any waiting promise. PluginIcon resets its source when the domain changes so a reused mark never shows the prior brand. * fix(connect): record reconnect requests under the owning tenant, no-store responses recordConnectRequestBestEffort now keys on the error's own tenantId — the tenant the scoped connectUrl belongs to — and falls back to the binding's tenant, so the owner can read its own pending request instead of it landing under `default`. The connect-request GET is served Cache-Control: no-store so a shared cache can't hand one tenant's connect link to another browser. * build(corsair): clean dist with a portable command rm -rf isn't available under cmd.exe, so the pre-tsup clean broke Windows builds. Use node's fs.rmSync, which runs on every platform. * fix(react): end the connect attempt when the popup is blocked window.open returns null when the browser blocks the popup; the watcher then never settles because there is no window to observe, so connect() hung. Guard it — end the attempt so the promise resolves and the caller can retry. Also correct the call() contract docs: it retries whenever a connect-request is pending (the server records that across the RSC boundary where the typed error is lost), so it is documented for connect-gated or retry-safe mutations. * fix(react): confirm the connection before treating a closed popup as cancel The success page closes the popup only after the connection persists, but the confirming status poll can outrun the grace window. The grace timeout used to settle cancelled outright, so a slow final poll discarded a real success — connect() resolved false though the account connected. It now runs one authoritative status check when the grace fires and settles on the result. Extracted finishConnected/finishCancelled to share that path with the poll. * docs: document Corsair Connect and rebuild the llms.txt index Add a Corsair Connect section to the React adapter page (CorsairProvider, CorsairBoundary, useConnect, and the React-only caveat), and note the typed AuthMissingError / ReconnectRequiredError under error handling. Rebuild both llms.txt files against the current docs: the index had drifted — the Frameworks, Use cases, Workflows, Management, and LLM-gateway sections were missing and one MCP path (openai-agents) no longer exists. Regenerated from the live nav and page frontmatter, with every link verified to resolve. * feat(connect): error.tsx boundary, dialog redesign, post-connect retry - Replace the inline CorsairBoundary with CorsairErrorBoundary wired for a Next error.tsx — the only boundary that catches a Server Component read throw (a nested client boundary never sees it). - Redesign the overlay: rounded card, drop the plus-corner marks and the blueprint dot-grid, and a Clerk-style "Secured by <mark> Corsair" footer. - call(): retry the resumed action with linear backoff so the first run after connect rides out the credential-propagation window instead of surfacing a one-off auth-missing. * feat(connect): global auto-prompt for unwrapped client calls CorsairProvider listens for unhandled promise rejections and opens the dialog when the reason is a Corsair auth-missing / reconnect error, so a plain client-triggered server action prompts to connect with no `call` wrapper at the call site. Wrapped calls are caught upstream and never reach the listener (no double-prompt); server reads still gate through the error boundary. `call` stays the opt-in for auto-resume. On by default; opt out with captureUnhandled={false}. * style(connect): refine dialog type — hierarchy + readable greys Title carries presence via size + tracking rather than black weight, and secondary labels stay at quiet medium weights. Darken the muted/faint tokens so the greyed text (Not now, roster, footer) reads comfortably instead of too pale. * docs(connect): document the shipped Connect model Rewrite the React Connect guide to what ships: the dialog auto-opens for any client call (global capture, no wrapper), `call` adds resume, server reads gate through an error.tsx re-exporting CorsairErrorBoundary, and `connect` stays the proactive path. Drop the old inline <CorsairBoundary>. * refactor(connect): trim internal exports off the react barrel Drop useCorsair + CorsairContextValue from the public client/react entry — they only serve the in-package error boundary, which imports useCorsair from ./provider directly. Delete the dead connectNonce (written on connect, read nowhere) and its now-unused useState import. * feat(connect): unify connection status into useConnections Rename useConnect -> useConnections and fold this user's connection status into it: connections, isConnected(plugin), loading. The provider now owns the status (one fetch on mount) and refetches after every successful connect, so a proactive button reflects reality and never re-prompts an already-connected plugin — no second hook, no manual refetch. Drop the unused status field. Document PluginConnectionState / ConnectionStatus in core so the states are self-explaining, and export PluginConnectionState. * feat(react): mark provider-opened connect links for auto-forward The Hub connect page auto-forwards a single fresh OAuth link straight to sign-in. Tag the popup the provider opens with forward=1 so only those links skip the connect page; cold links (grid, email, pasted) omit the marker and render the page as before. * chore(corsair): bump to 0.1.125 * fix(react): confirm connection with retries before reading popup-close as cancel * fix(react): coalesce concurrent connect calls so a second doesn't cancel the first * refactor(db): rename corsair_connect_requests table to corsair_connects * fix(react): keep the connect modal centered and scroll a tall card * docs: rename corsair_connect_requests to corsair_connects * fix(react): restore the connect modal to its original centered position * fix(react): anchor the connect modal to the top so it grows downward * fix(react): scope connect coalescing to tenant and cancel a stale grace on reopen * fix(react): let the reactive connect path (null tenant) coalesce with an open flow * fix(connect): scope the reactive connect path to its resolved tenant The connect-request DTO now carries the tenant the server resolved it under, so the reactive path stops passing null and openDialog coalesces on the real tenant. Strict equality then joins a same-tenant concurrent flow and supersedes a different one — fixing both the cancel-same-tenant and join-wrong-tenant cases at the source instead of special-casing null in the coalesce check. * fix(connect): resolve proactive tenant for symmetric coalescing The proactive path passed null while the reactive path passed the server-resolved tenant, so strict equality superseded a same-tenant flow — dropping the reactive read to 'dismissed' or resolving connect() false despite connecting. ConnectLink now returns the tenant it resolved (both builders already default to 'default'), and connect() scopes the flow to it. Both paths always express the tenant the same way, so openDialog never sees null from a live flow and coalescing is exact. * refactor(connect): drop the unneeded row cast in readConnectRequest corsair_connects is registered in the Kysely schema, so selectAll() is already typed — the 'as CorsairConnect | undefined' cast (and its import) were dead. * test(connect): lock the no-double-side-effect and no-hang invariants Provider-level regression tests through a mounted CorsairProvider: call() rethrows without re-running the op when no connect-request is pending (an auth-missing failure records the request only after failing at the auth gate, so nothing to duplicate), and a pending connect() resolves false rather than hanging when the provider unmounts mid-flow. * fix(connect): gate coalescing on flow liveness to stop stranding a caller finishConnected empties the waiters and stops the watch before React commits the success state, so the state ref reads 'connecting' for a beat. A same-tenant openDialog landing in that window joined the already-settled flow and added a waiter that nothing settles again — the caller's promise hung forever. Gate coalescing on the waiter count (the synchronous liveness truth) so a settled flow is never joined. | 1 个月前 | |
feat(cloud): language client wrappers, useCorsair, cloud-proxy, and cloud CLI (#1741) * feat(clients): Swift Corsair Cloud client wrapper Thin URLSession client mirroring createCorsairCloud: dynamic tool calls (tenant(t).call(plugin, op, args)) + management (connectionStatus, connect link, disconnect, tenants). Zero deps; JSONValue for dynamic in/out. Swift Testing covers URL/bearer/body/data, connection-status, and the error envelope. WRAPPER-SPEC.md pins the canonical shape every language wrapper follows. * docs(cloud): add Corsair Cloud overview page Explains the hosted runtime, the key+URL, the server (createCorsairCloud) and client (CorsairProvider) surfaces, the connect flow, multi-tenancy, and the pull-generated types. Wired into Get Started. * feat(client): add useCorsair() react hook for tool calls Adds client.call(plugin, op, tenantId, args) to the vanilla management client, posting to /:tenant/:plugin/call/:op, and a public useCorsair() hook returning { api, db } built on the existing useAsync state machine. db is a reserved placeholder — no /db route exists yet. Renames the internal context accessor useCorsair -> useCorsairContext to free the name for the new public hook. * feat(clients): Python Corsair Cloud client wrapper Thin stdlib (urllib) client mirroring the Swift/TS shape: with_tenant(t).call( plugin, op, args) -> data, plus manage (connection_status, connect link, disconnect, tenants). CorsairError from the runtime envelope. pytest, no network. * feat(clients): Go Corsair Cloud client wrapper Thin net/http client mirroring the Swift/Python/TS shape: Tenant(id).Call(ctx, plugin, op, args) -> json.RawMessage, plus ConnectionStatus/CreateConnectLink/ Disconnect/Tenants/CreateTenant. *CorsairError from the runtime envelope. httptest-backed tests, stdlib only. * feat(cloud): add corsair/cloud-proxy backend helper createCloudProxy({ apiKey, url }) is a framework-agnostic (Request)=>Response that forwards a same-origin /api/corsair route to the runtime and injects the ck_cloud_ bearer server-side. Drops the caller's Authorization/cookies. Pairs with CorsairProvider so the key never reaches the browser. Docs snippet + wiring. * ci: add clients CI, docs pages, and PyPI publish for cloud wrappers Swift/Python/Go each run their own job on clients/** PRs. Docs get a Client libraries nav group. Python pyproject is publish-ready and gated behind a corsair-cloud-py-v* tag + PYPI_TOKEN secret. Go/Swift READMEs note their monorepo distribution constraints (subdir tags, SPM root). * feat(cli): add corsair cloud list/pull for Corsair Cloud discovery corsair cloud list prints a hosted project's op tree (GET /call, bearer project key). corsair cloud pull writes corsair-env.d.ts, merging the ops into CorsairCloudRegistry so createCorsairCloud() is typed without a plugin list. Nested under `cloud` since top-level `list` is already the local-instance operation lister. * docs(cloud): correct CLI command to 'corsair cloud pull/list' * chore(clients): stop tracking Swift .build output * fix(cli): preserve terminal+nested ops and quote generated d.ts keys insertOp overwrote a terminal op's true with an object when a longer op shared its prefix (users vs users.list), dropping the leaf. Track call state separately from children, and quote every generated key so plugin/op names with punctuation don't break corsair-env.d.ts. * fix(cli): require https for the cloud url, fix env-var test cleanup The cloud key goes out as a bearer token, so an --url pointing at plain http would leak it. Reject non-https unless the host is loopback, matching the reference client's rule. Also stop restoring 'undefined' as a string into process.env in afterEach — delete when the original value was actually unset. * fix(corsair): gate cloud proxy behind https and an authorize hook createCloudProxy forwarded any caller-chosen tenant/plugin/op with the project bearer attached, so once mounted, an unauthenticated caller could reach any op the project key is allowed to invoke. Add an optional authorize hook that runs before forwarding and rejects with 401 when it returns false, document it as required in the Next.js snippet, and reject non-https upstream urls the same way the reference client does. * fix(client): rename useCorsair's api to useApi — it calls hooks internally api() called useAsync (useReducer/useEffect/useRef) but wasn't named like a hook, so nothing stopped a caller from invoking it conditionally, in a loop, or from an event handler and breaking hook order at runtime. Rename to useApi so it reads — and lints — as the hook it already was. * fix(go-client): require https, escape path segments, add redirectUri send() joined path segments raw, so a tenant/plugin/op containing '/' changed the route instead of addressing it — escape each segment with url.PathEscape. Reject non-https base URLs (loopback excepted, so the existing httptest.NewServer-based tests keep working) since the API key rides as a bearer token. Give the default client a finite timeout instead of http.DefaultClient's none. Add the redirectUri param to CreateConnectLink so it matches the Python and Swift wrappers. * fix(python-client): require https, escape path segments, add timeout _request joined tenant/plugin/op into the path unescaped, so a value containing '/' changed the route instead of addressing it — quote each dynamic segment. Reject non-https urls (loopback excepted) since the API key rides as a bearer token, and pass a finite timeout to urlopen so a stalled server can't hang the caller forever. Also bump the setuptools floor to 77, the minimum that understands the Apache-2.0 license-expression syntax already in pyproject.toml. * fix(swift-client): require https, escape path segments; fix docs install send() built the URL path by joining segments raw through URLComponents.path, so a tenant/plugin/op containing '/' changed the route instead of addressing it — percent-encode each segment into percentEncodedPath instead. Reject non-https base URLs (loopback excepted) at call time. Also fix the docs/README install snippet: SPM can't resolve .package(url:) against a monorepo subdirectory, so show a local path dependency instead of a command that can't actually resolve. * ci(clients): pin Xcode 16 for swift test, restrict checkout token macos-14's default Xcode (15.4) has no Testing module, so 'import Testing' failed to compile — pin DEVELOPER_DIR to the Xcode 16.2 image already on the runner. Also set workflow permissions to contents:read and persist-credentials:false on every checkout so same-repo PR test code can't exfiltrate a writable token. * chore: bump corsair 0.1.137 + @corsair-dev/cli 0.1.24 (cloud wrappers + CLI) * docs(cloud): rewrite client docs and fix swift/go client bugs - Rewrite cloud/overview + python/go/swift client docs and READMEs as step-by-step first-time walkthroughs; drop AI-tell phrasing - Move client libraries under Frameworks > Clients in the nav - Swift: strip trailing slash on base URL (was producing //path) - Go: coerce nil args to {} so body is {"args":{}}, not {"args":null} - Add Go + Swift regression tests * fix(cloud): warn when cloud proxy has no authorize gate * docs(cloud): add client icons and swift mirror publish workflow * feat(cloud): resolve createCorsairCloud URL from the key createCorsairCloud({ apiKey }) now derives its base URL from the key slug (ck_cloud_<slug>_<secret> -> https://api.corsair.cloud/<slug>/api/corsair), so the developer passes only the key. `url` stays as an internal dev/test override. * feat(cloud): resolve the URL from the key across all clients Mirror the TS createCorsairCloud change in every client so the developer passes only the API key: Python/Go/Swift clients, cloud-proxy, and the cloud CLI now derive https://api.corsair.cloud/<slug>/api/corsair from a ck_cloud_<slug>_<secret> key; url stays as an optional dev/testing override. Tests: Go 6/6, Swift 8/8, Python + cloud-proxy + CLI green. * docs(cloud): key-only Corsair Cloud docs across the client surface Rewrite the cloud + per-client docs to the one-key contract: the overview, each language guide (Python/Go/Swift), the client READMEs, the cloud-proxy snippet, the WRAPPER-SPEC (source of truth), and the OpenAPI servers block no longer tell developers to copy a URL — the client derives it from the key. Also documents mapping tenants (manage.tenants.list) and marks raw-DB access as coming with BYO DB. * docs(cloud): give the curl example a URL source (derive from key slug) * fix(cloud): parse ck_cloud key slug on '.' delimiter The Hub now mints cloud keys as ck_cloud_<slug>.<secret>. Switch all client URL resolvers (TS core, cloud-proxy, CLI, Python, Go, Swift) to split the slug on the first '.' instead of '_'. The base64url secret can't contain '.', so an older slug-less key resolves to null rather than a wrong URL — locked by a legacy-underscore-key test in each suite. * fix(cloud): harden clients against review findings - cli op-tree: null-prototype maps so op names like toString/constructor key real nodes instead of inherited Object.prototype members - cloud-proxy: a thrown authorize() returns 401, not an escaping 500 - swift: percent-encode literal % in path segments (a%2Fb no longer decodes to a/b server-side), matching Go/Python - go: WithHTTPClient(nil) keeps the default; typed-nil args marshal to {} - reject a query/fragment in cloud URLs across core/proxy/cli (path segments are appended, so it would misroute) - docs: python client is 3.10+ (matches pyproject) - ci: publish-python guards tag==version; publish-swift tags then pushes branch+tag atomically * fix(cloud): ship py.typed marker for the Python client The Typing :: Typed classifier promises inline types; without the marker + package-data, type checkers ignore them for consumers. * fix(cloud): reject an empty tenant id in the language clients TS withTenant rejects an empty tenant; Go/Python/Swift silently built a path with a missing segment (…//plugin/call/op) that misroutes. Guard it: Python raises in with_tenant, Go returns an error from Call, Swift throws EmptyTenantIdError. Tests added for Go and Python. * refactor(cloud): rename to corsairCloud + corsairConnect Per the agreed surface naming: createCorsairCloud -> corsairCloud (the hosted client), createCloudProxy -> corsairConnect (the same-origin route handler), and the subpath corsair/cloud-proxy -> corsair/connect (file cloud-proxy.ts -> connect.ts). CorsairProvider keeps its name. Config type CreateCorsairCloudConfig -> CorsairCloudConfig, CloudProxyOptions -> CorsairConnectOptions. No behavior change. * docs(cloud): fix stale slug delimiter + unslop the cloud docs Overview curl example now derives the slug on the first '.' (not the old last '_'); renamed the stale CloudProxyNext import alias to ConnectExample; tightened the tenants/connection-status note to state exactly what the manage surface returns (redacted) vs what BYO-DB adds later; removed em-dashes across overview, the three client guides, and the connect snippet. * feat(cloud): expose manage.permissions.get on the cloud clients The runtime already serves GET /permissions/:id (admin, same gate as /tenants which the client uses) and POST /permissions/lookup-by-token (public approval-page lookup). Wire both through the TS manage namespace (by id or by token) and add a by-id read to the Python/Go/Swift clients. Lets a project read a tenant's permission grants — scopes and approval status — without touching credentials. * ci: publish corsair-cloud via PyPI trusted publishing (OIDC) Drop the stored PYPI_TOKEN in favor of OIDC: id-token: write + a pypi environment + pypa/gh-action-pypi-publish. Keeps the tag==pyproject version guard. * refactor(cloud): share the key-URL contract + warn on open proxy in prod - Extract cloudUrlFromKey + the https/query-fragment assertion + loopback set into core/cloud/url.ts, consumed by both the cloud client and the connect route handler, so the ck_cloud key format can't drift between them. - corsairConnect now warns about a missing authorize in every environment, production included — suppressing the signal in prod was backwards for a route that forwards any op with the project key. * docs(cloud): handle err in the Go connect/status examples so they compile * feat(cloud): corsairConnect fails closed without authorize An open proxy by omission forwards any tenant/plugin/op with the project key. Require authorize (or an explicit allowUnauthenticated: true opt-in), throwing at construction otherwise. Addresses the proxy-authorization review finding. * chore(cloud): drop internal WRAPPER-SPEC scaffolding doc It was the design spec that guided the client shape — not user-facing (the docs are docs/clients/*.mdx + per-client READMEs) and unreferenced. Doesn't belong in the shipped clients/ dir. | 1 个月前 | |
feat(cloud): language client wrappers, useCorsair, cloud-proxy, and cloud CLI (#1741) * feat(clients): Swift Corsair Cloud client wrapper Thin URLSession client mirroring createCorsairCloud: dynamic tool calls (tenant(t).call(plugin, op, args)) + management (connectionStatus, connect link, disconnect, tenants). Zero deps; JSONValue for dynamic in/out. Swift Testing covers URL/bearer/body/data, connection-status, and the error envelope. WRAPPER-SPEC.md pins the canonical shape every language wrapper follows. * docs(cloud): add Corsair Cloud overview page Explains the hosted runtime, the key+URL, the server (createCorsairCloud) and client (CorsairProvider) surfaces, the connect flow, multi-tenancy, and the pull-generated types. Wired into Get Started. * feat(client): add useCorsair() react hook for tool calls Adds client.call(plugin, op, tenantId, args) to the vanilla management client, posting to /:tenant/:plugin/call/:op, and a public useCorsair() hook returning { api, db } built on the existing useAsync state machine. db is a reserved placeholder — no /db route exists yet. Renames the internal context accessor useCorsair -> useCorsairContext to free the name for the new public hook. * feat(clients): Python Corsair Cloud client wrapper Thin stdlib (urllib) client mirroring the Swift/TS shape: with_tenant(t).call( plugin, op, args) -> data, plus manage (connection_status, connect link, disconnect, tenants). CorsairError from the runtime envelope. pytest, no network. * feat(clients): Go Corsair Cloud client wrapper Thin net/http client mirroring the Swift/Python/TS shape: Tenant(id).Call(ctx, plugin, op, args) -> json.RawMessage, plus ConnectionStatus/CreateConnectLink/ Disconnect/Tenants/CreateTenant. *CorsairError from the runtime envelope. httptest-backed tests, stdlib only. * feat(cloud): add corsair/cloud-proxy backend helper createCloudProxy({ apiKey, url }) is a framework-agnostic (Request)=>Response that forwards a same-origin /api/corsair route to the runtime and injects the ck_cloud_ bearer server-side. Drops the caller's Authorization/cookies. Pairs with CorsairProvider so the key never reaches the browser. Docs snippet + wiring. * ci: add clients CI, docs pages, and PyPI publish for cloud wrappers Swift/Python/Go each run their own job on clients/** PRs. Docs get a Client libraries nav group. Python pyproject is publish-ready and gated behind a corsair-cloud-py-v* tag + PYPI_TOKEN secret. Go/Swift READMEs note their monorepo distribution constraints (subdir tags, SPM root). * feat(cli): add corsair cloud list/pull for Corsair Cloud discovery corsair cloud list prints a hosted project's op tree (GET /call, bearer project key). corsair cloud pull writes corsair-env.d.ts, merging the ops into CorsairCloudRegistry so createCorsairCloud() is typed without a plugin list. Nested under `cloud` since top-level `list` is already the local-instance operation lister. * docs(cloud): correct CLI command to 'corsair cloud pull/list' * chore(clients): stop tracking Swift .build output * fix(cli): preserve terminal+nested ops and quote generated d.ts keys insertOp overwrote a terminal op's true with an object when a longer op shared its prefix (users vs users.list), dropping the leaf. Track call state separately from children, and quote every generated key so plugin/op names with punctuation don't break corsair-env.d.ts. * fix(cli): require https for the cloud url, fix env-var test cleanup The cloud key goes out as a bearer token, so an --url pointing at plain http would leak it. Reject non-https unless the host is loopback, matching the reference client's rule. Also stop restoring 'undefined' as a string into process.env in afterEach — delete when the original value was actually unset. * fix(corsair): gate cloud proxy behind https and an authorize hook createCloudProxy forwarded any caller-chosen tenant/plugin/op with the project bearer attached, so once mounted, an unauthenticated caller could reach any op the project key is allowed to invoke. Add an optional authorize hook that runs before forwarding and rejects with 401 when it returns false, document it as required in the Next.js snippet, and reject non-https upstream urls the same way the reference client does. * fix(client): rename useCorsair's api to useApi — it calls hooks internally api() called useAsync (useReducer/useEffect/useRef) but wasn't named like a hook, so nothing stopped a caller from invoking it conditionally, in a loop, or from an event handler and breaking hook order at runtime. Rename to useApi so it reads — and lints — as the hook it already was. * fix(go-client): require https, escape path segments, add redirectUri send() joined path segments raw, so a tenant/plugin/op containing '/' changed the route instead of addressing it — escape each segment with url.PathEscape. Reject non-https base URLs (loopback excepted, so the existing httptest.NewServer-based tests keep working) since the API key rides as a bearer token. Give the default client a finite timeout instead of http.DefaultClient's none. Add the redirectUri param to CreateConnectLink so it matches the Python and Swift wrappers. * fix(python-client): require https, escape path segments, add timeout _request joined tenant/plugin/op into the path unescaped, so a value containing '/' changed the route instead of addressing it — quote each dynamic segment. Reject non-https urls (loopback excepted) since the API key rides as a bearer token, and pass a finite timeout to urlopen so a stalled server can't hang the caller forever. Also bump the setuptools floor to 77, the minimum that understands the Apache-2.0 license-expression syntax already in pyproject.toml. * fix(swift-client): require https, escape path segments; fix docs install send() built the URL path by joining segments raw through URLComponents.path, so a tenant/plugin/op containing '/' changed the route instead of addressing it — percent-encode each segment into percentEncodedPath instead. Reject non-https base URLs (loopback excepted) at call time. Also fix the docs/README install snippet: SPM can't resolve .package(url:) against a monorepo subdirectory, so show a local path dependency instead of a command that can't actually resolve. * ci(clients): pin Xcode 16 for swift test, restrict checkout token macos-14's default Xcode (15.4) has no Testing module, so 'import Testing' failed to compile — pin DEVELOPER_DIR to the Xcode 16.2 image already on the runner. Also set workflow permissions to contents:read and persist-credentials:false on every checkout so same-repo PR test code can't exfiltrate a writable token. * chore: bump corsair 0.1.137 + @corsair-dev/cli 0.1.24 (cloud wrappers + CLI) * docs(cloud): rewrite client docs and fix swift/go client bugs - Rewrite cloud/overview + python/go/swift client docs and READMEs as step-by-step first-time walkthroughs; drop AI-tell phrasing - Move client libraries under Frameworks > Clients in the nav - Swift: strip trailing slash on base URL (was producing //path) - Go: coerce nil args to {} so body is {"args":{}}, not {"args":null} - Add Go + Swift regression tests * fix(cloud): warn when cloud proxy has no authorize gate * docs(cloud): add client icons and swift mirror publish workflow * feat(cloud): resolve createCorsairCloud URL from the key createCorsairCloud({ apiKey }) now derives its base URL from the key slug (ck_cloud_<slug>_<secret> -> https://api.corsair.cloud/<slug>/api/corsair), so the developer passes only the key. `url` stays as an internal dev/test override. * feat(cloud): resolve the URL from the key across all clients Mirror the TS createCorsairCloud change in every client so the developer passes only the API key: Python/Go/Swift clients, cloud-proxy, and the cloud CLI now derive https://api.corsair.cloud/<slug>/api/corsair from a ck_cloud_<slug>_<secret> key; url stays as an optional dev/testing override. Tests: Go 6/6, Swift 8/8, Python + cloud-proxy + CLI green. * docs(cloud): key-only Corsair Cloud docs across the client surface Rewrite the cloud + per-client docs to the one-key contract: the overview, each language guide (Python/Go/Swift), the client READMEs, the cloud-proxy snippet, the WRAPPER-SPEC (source of truth), and the OpenAPI servers block no longer tell developers to copy a URL — the client derives it from the key. Also documents mapping tenants (manage.tenants.list) and marks raw-DB access as coming with BYO DB. * docs(cloud): give the curl example a URL source (derive from key slug) * fix(cloud): parse ck_cloud key slug on '.' delimiter The Hub now mints cloud keys as ck_cloud_<slug>.<secret>. Switch all client URL resolvers (TS core, cloud-proxy, CLI, Python, Go, Swift) to split the slug on the first '.' instead of '_'. The base64url secret can't contain '.', so an older slug-less key resolves to null rather than a wrong URL — locked by a legacy-underscore-key test in each suite. * fix(cloud): harden clients against review findings - cli op-tree: null-prototype maps so op names like toString/constructor key real nodes instead of inherited Object.prototype members - cloud-proxy: a thrown authorize() returns 401, not an escaping 500 - swift: percent-encode literal % in path segments (a%2Fb no longer decodes to a/b server-side), matching Go/Python - go: WithHTTPClient(nil) keeps the default; typed-nil args marshal to {} - reject a query/fragment in cloud URLs across core/proxy/cli (path segments are appended, so it would misroute) - docs: python client is 3.10+ (matches pyproject) - ci: publish-python guards tag==version; publish-swift tags then pushes branch+tag atomically * fix(cloud): ship py.typed marker for the Python client The Typing :: Typed classifier promises inline types; without the marker + package-data, type checkers ignore them for consumers. * fix(cloud): reject an empty tenant id in the language clients TS withTenant rejects an empty tenant; Go/Python/Swift silently built a path with a missing segment (…//plugin/call/op) that misroutes. Guard it: Python raises in with_tenant, Go returns an error from Call, Swift throws EmptyTenantIdError. Tests added for Go and Python. * refactor(cloud): rename to corsairCloud + corsairConnect Per the agreed surface naming: createCorsairCloud -> corsairCloud (the hosted client), createCloudProxy -> corsairConnect (the same-origin route handler), and the subpath corsair/cloud-proxy -> corsair/connect (file cloud-proxy.ts -> connect.ts). CorsairProvider keeps its name. Config type CreateCorsairCloudConfig -> CorsairCloudConfig, CloudProxyOptions -> CorsairConnectOptions. No behavior change. * docs(cloud): fix stale slug delimiter + unslop the cloud docs Overview curl example now derives the slug on the first '.' (not the old last '_'); renamed the stale CloudProxyNext import alias to ConnectExample; tightened the tenants/connection-status note to state exactly what the manage surface returns (redacted) vs what BYO-DB adds later; removed em-dashes across overview, the three client guides, and the connect snippet. * feat(cloud): expose manage.permissions.get on the cloud clients The runtime already serves GET /permissions/:id (admin, same gate as /tenants which the client uses) and POST /permissions/lookup-by-token (public approval-page lookup). Wire both through the TS manage namespace (by id or by token) and add a by-id read to the Python/Go/Swift clients. Lets a project read a tenant's permission grants — scopes and approval status — without touching credentials. * ci: publish corsair-cloud via PyPI trusted publishing (OIDC) Drop the stored PYPI_TOKEN in favor of OIDC: id-token: write + a pypi environment + pypa/gh-action-pypi-publish. Keeps the tag==pyproject version guard. * refactor(cloud): share the key-URL contract + warn on open proxy in prod - Extract cloudUrlFromKey + the https/query-fragment assertion + loopback set into core/cloud/url.ts, consumed by both the cloud client and the connect route handler, so the ck_cloud key format can't drift between them. - corsairConnect now warns about a missing authorize in every environment, production included — suppressing the signal in prod was backwards for a route that forwards any op with the project key. * docs(cloud): handle err in the Go connect/status examples so they compile * feat(cloud): corsairConnect fails closed without authorize An open proxy by omission forwards any tenant/plugin/op with the project key. Require authorize (or an explicit allowUnauthenticated: true opt-in), throwing at construction otherwise. Addresses the proxy-authorization review finding. * chore(cloud): drop internal WRAPPER-SPEC scaffolding doc It was the design spec that guided the client shape — not user-facing (the docs are docs/clients/*.mdx + per-client READMEs) and unreferenced. Doesn't belong in the shipped clients/ dir. | 1 个月前 | |
fix: update github oauth refresh | 1 个月前 | |
fix: update docs routing for intro and quick start pages | 1 个月前 | |
fix: update docs routing for intro and quick start pages | 1 个月前 | |
fix(google): request least-privilege oauth scopes (#1767) | 24 天前 | |
chore(tunnel): default tunnel zone to corsair.run (#1730) | 1 个月前 | |
feat(cloud): language client wrappers, useCorsair, cloud-proxy, and cloud CLI (#1741) * feat(clients): Swift Corsair Cloud client wrapper Thin URLSession client mirroring createCorsairCloud: dynamic tool calls (tenant(t).call(plugin, op, args)) + management (connectionStatus, connect link, disconnect, tenants). Zero deps; JSONValue for dynamic in/out. Swift Testing covers URL/bearer/body/data, connection-status, and the error envelope. WRAPPER-SPEC.md pins the canonical shape every language wrapper follows. * docs(cloud): add Corsair Cloud overview page Explains the hosted runtime, the key+URL, the server (createCorsairCloud) and client (CorsairProvider) surfaces, the connect flow, multi-tenancy, and the pull-generated types. Wired into Get Started. * feat(client): add useCorsair() react hook for tool calls Adds client.call(plugin, op, tenantId, args) to the vanilla management client, posting to /:tenant/:plugin/call/:op, and a public useCorsair() hook returning { api, db } built on the existing useAsync state machine. db is a reserved placeholder — no /db route exists yet. Renames the internal context accessor useCorsair -> useCorsairContext to free the name for the new public hook. * feat(clients): Python Corsair Cloud client wrapper Thin stdlib (urllib) client mirroring the Swift/TS shape: with_tenant(t).call( plugin, op, args) -> data, plus manage (connection_status, connect link, disconnect, tenants). CorsairError from the runtime envelope. pytest, no network. * feat(clients): Go Corsair Cloud client wrapper Thin net/http client mirroring the Swift/Python/TS shape: Tenant(id).Call(ctx, plugin, op, args) -> json.RawMessage, plus ConnectionStatus/CreateConnectLink/ Disconnect/Tenants/CreateTenant. *CorsairError from the runtime envelope. httptest-backed tests, stdlib only. * feat(cloud): add corsair/cloud-proxy backend helper createCloudProxy({ apiKey, url }) is a framework-agnostic (Request)=>Response that forwards a same-origin /api/corsair route to the runtime and injects the ck_cloud_ bearer server-side. Drops the caller's Authorization/cookies. Pairs with CorsairProvider so the key never reaches the browser. Docs snippet + wiring. * ci: add clients CI, docs pages, and PyPI publish for cloud wrappers Swift/Python/Go each run their own job on clients/** PRs. Docs get a Client libraries nav group. Python pyproject is publish-ready and gated behind a corsair-cloud-py-v* tag + PYPI_TOKEN secret. Go/Swift READMEs note their monorepo distribution constraints (subdir tags, SPM root). * feat(cli): add corsair cloud list/pull for Corsair Cloud discovery corsair cloud list prints a hosted project's op tree (GET /call, bearer project key). corsair cloud pull writes corsair-env.d.ts, merging the ops into CorsairCloudRegistry so createCorsairCloud() is typed without a plugin list. Nested under `cloud` since top-level `list` is already the local-instance operation lister. * docs(cloud): correct CLI command to 'corsair cloud pull/list' * chore(clients): stop tracking Swift .build output * fix(cli): preserve terminal+nested ops and quote generated d.ts keys insertOp overwrote a terminal op's true with an object when a longer op shared its prefix (users vs users.list), dropping the leaf. Track call state separately from children, and quote every generated key so plugin/op names with punctuation don't break corsair-env.d.ts. * fix(cli): require https for the cloud url, fix env-var test cleanup The cloud key goes out as a bearer token, so an --url pointing at plain http would leak it. Reject non-https unless the host is loopback, matching the reference client's rule. Also stop restoring 'undefined' as a string into process.env in afterEach — delete when the original value was actually unset. * fix(corsair): gate cloud proxy behind https and an authorize hook createCloudProxy forwarded any caller-chosen tenant/plugin/op with the project bearer attached, so once mounted, an unauthenticated caller could reach any op the project key is allowed to invoke. Add an optional authorize hook that runs before forwarding and rejects with 401 when it returns false, document it as required in the Next.js snippet, and reject non-https upstream urls the same way the reference client does. * fix(client): rename useCorsair's api to useApi — it calls hooks internally api() called useAsync (useReducer/useEffect/useRef) but wasn't named like a hook, so nothing stopped a caller from invoking it conditionally, in a loop, or from an event handler and breaking hook order at runtime. Rename to useApi so it reads — and lints — as the hook it already was. * fix(go-client): require https, escape path segments, add redirectUri send() joined path segments raw, so a tenant/plugin/op containing '/' changed the route instead of addressing it — escape each segment with url.PathEscape. Reject non-https base URLs (loopback excepted, so the existing httptest.NewServer-based tests keep working) since the API key rides as a bearer token. Give the default client a finite timeout instead of http.DefaultClient's none. Add the redirectUri param to CreateConnectLink so it matches the Python and Swift wrappers. * fix(python-client): require https, escape path segments, add timeout _request joined tenant/plugin/op into the path unescaped, so a value containing '/' changed the route instead of addressing it — quote each dynamic segment. Reject non-https urls (loopback excepted) since the API key rides as a bearer token, and pass a finite timeout to urlopen so a stalled server can't hang the caller forever. Also bump the setuptools floor to 77, the minimum that understands the Apache-2.0 license-expression syntax already in pyproject.toml. * fix(swift-client): require https, escape path segments; fix docs install send() built the URL path by joining segments raw through URLComponents.path, so a tenant/plugin/op containing '/' changed the route instead of addressing it — percent-encode each segment into percentEncodedPath instead. Reject non-https base URLs (loopback excepted) at call time. Also fix the docs/README install snippet: SPM can't resolve .package(url:) against a monorepo subdirectory, so show a local path dependency instead of a command that can't actually resolve. * ci(clients): pin Xcode 16 for swift test, restrict checkout token macos-14's default Xcode (15.4) has no Testing module, so 'import Testing' failed to compile — pin DEVELOPER_DIR to the Xcode 16.2 image already on the runner. Also set workflow permissions to contents:read and persist-credentials:false on every checkout so same-repo PR test code can't exfiltrate a writable token. * chore: bump corsair 0.1.137 + @corsair-dev/cli 0.1.24 (cloud wrappers + CLI) * docs(cloud): rewrite client docs and fix swift/go client bugs - Rewrite cloud/overview + python/go/swift client docs and READMEs as step-by-step first-time walkthroughs; drop AI-tell phrasing - Move client libraries under Frameworks > Clients in the nav - Swift: strip trailing slash on base URL (was producing //path) - Go: coerce nil args to {} so body is {"args":{}}, not {"args":null} - Add Go + Swift regression tests * fix(cloud): warn when cloud proxy has no authorize gate * docs(cloud): add client icons and swift mirror publish workflow * feat(cloud): resolve createCorsairCloud URL from the key createCorsairCloud({ apiKey }) now derives its base URL from the key slug (ck_cloud_<slug>_<secret> -> https://api.corsair.cloud/<slug>/api/corsair), so the developer passes only the key. `url` stays as an internal dev/test override. * feat(cloud): resolve the URL from the key across all clients Mirror the TS createCorsairCloud change in every client so the developer passes only the API key: Python/Go/Swift clients, cloud-proxy, and the cloud CLI now derive https://api.corsair.cloud/<slug>/api/corsair from a ck_cloud_<slug>_<secret> key; url stays as an optional dev/testing override. Tests: Go 6/6, Swift 8/8, Python + cloud-proxy + CLI green. * docs(cloud): key-only Corsair Cloud docs across the client surface Rewrite the cloud + per-client docs to the one-key contract: the overview, each language guide (Python/Go/Swift), the client READMEs, the cloud-proxy snippet, the WRAPPER-SPEC (source of truth), and the OpenAPI servers block no longer tell developers to copy a URL — the client derives it from the key. Also documents mapping tenants (manage.tenants.list) and marks raw-DB access as coming with BYO DB. * docs(cloud): give the curl example a URL source (derive from key slug) * fix(cloud): parse ck_cloud key slug on '.' delimiter The Hub now mints cloud keys as ck_cloud_<slug>.<secret>. Switch all client URL resolvers (TS core, cloud-proxy, CLI, Python, Go, Swift) to split the slug on the first '.' instead of '_'. The base64url secret can't contain '.', so an older slug-less key resolves to null rather than a wrong URL — locked by a legacy-underscore-key test in each suite. * fix(cloud): harden clients against review findings - cli op-tree: null-prototype maps so op names like toString/constructor key real nodes instead of inherited Object.prototype members - cloud-proxy: a thrown authorize() returns 401, not an escaping 500 - swift: percent-encode literal % in path segments (a%2Fb no longer decodes to a/b server-side), matching Go/Python - go: WithHTTPClient(nil) keeps the default; typed-nil args marshal to {} - reject a query/fragment in cloud URLs across core/proxy/cli (path segments are appended, so it would misroute) - docs: python client is 3.10+ (matches pyproject) - ci: publish-python guards tag==version; publish-swift tags then pushes branch+tag atomically * fix(cloud): ship py.typed marker for the Python client The Typing :: Typed classifier promises inline types; without the marker + package-data, type checkers ignore them for consumers. * fix(cloud): reject an empty tenant id in the language clients TS withTenant rejects an empty tenant; Go/Python/Swift silently built a path with a missing segment (…//plugin/call/op) that misroutes. Guard it: Python raises in with_tenant, Go returns an error from Call, Swift throws EmptyTenantIdError. Tests added for Go and Python. * refactor(cloud): rename to corsairCloud + corsairConnect Per the agreed surface naming: createCorsairCloud -> corsairCloud (the hosted client), createCloudProxy -> corsairConnect (the same-origin route handler), and the subpath corsair/cloud-proxy -> corsair/connect (file cloud-proxy.ts -> connect.ts). CorsairProvider keeps its name. Config type CreateCorsairCloudConfig -> CorsairCloudConfig, CloudProxyOptions -> CorsairConnectOptions. No behavior change. * docs(cloud): fix stale slug delimiter + unslop the cloud docs Overview curl example now derives the slug on the first '.' (not the old last '_'); renamed the stale CloudProxyNext import alias to ConnectExample; tightened the tenants/connection-status note to state exactly what the manage surface returns (redacted) vs what BYO-DB adds later; removed em-dashes across overview, the three client guides, and the connect snippet. * feat(cloud): expose manage.permissions.get on the cloud clients The runtime already serves GET /permissions/:id (admin, same gate as /tenants which the client uses) and POST /permissions/lookup-by-token (public approval-page lookup). Wire both through the TS manage namespace (by id or by token) and add a by-id read to the Python/Go/Swift clients. Lets a project read a tenant's permission grants — scopes and approval status — without touching credentials. * ci: publish corsair-cloud via PyPI trusted publishing (OIDC) Drop the stored PYPI_TOKEN in favor of OIDC: id-token: write + a pypi environment + pypa/gh-action-pypi-publish. Keeps the tag==pyproject version guard. * refactor(cloud): share the key-URL contract + warn on open proxy in prod - Extract cloudUrlFromKey + the https/query-fragment assertion + loopback set into core/cloud/url.ts, consumed by both the cloud client and the connect route handler, so the ck_cloud key format can't drift between them. - corsairConnect now warns about a missing authorize in every environment, production included — suppressing the signal in prod was backwards for a route that forwards any op with the project key. * docs(cloud): handle err in the Go connect/status examples so they compile * feat(cloud): corsairConnect fails closed without authorize An open proxy by omission forwards any tenant/plugin/op with the project key. Require authorize (or an explicit allowUnauthenticated: true opt-in), throwing at construction otherwise. Addresses the proxy-authorization review finding. * chore(cloud): drop internal WRAPPER-SPEC scaffolding doc It was the design spec that guided the client shape — not user-facing (the docs are docs/clients/*.mdx + per-client READMEs) and unreferenced. Doesn't belong in the shipped clients/ dir. | 1 个月前 | |
docs: hub-first documentation revamp (#454) | 3 个月前 | |
docs(env): standardize key env vars to unprefixed names (#1459) | 1 个月前 | |
feat(mcp): subpath re-exports (mastra/langchain/llamaindex) + turnkey stdio server (#1726) * feat(mastra): add @corsair-dev/mastra ToolProvider for Mastra * feat(mastra): move to adapters/ and address review - move packages/mastra -> adapters/mastra; wire adapters/* into the workspace, publish-changed, deploy/pr-checks, and turbo filters - resolveTenant: a decodable connectionId is authoritative over the tenant function (authorize and resolve can't diverge) - add getToolSchema for the editor tool-detail view - add revokeConnection + supportsRevoke (via corsair manage.disconnect) - peer @mastra/core >=1.64.0; form-field docstrings; README tool recipes - demo/mastra: minimal Mastra playground app * fix(mastra): harden tenant isolation and close review gaps - resolveTenant: the configured tenant is authoritative. A pinned string is never overridden by a connectionId, and when the caller is known a connectionId naming a different tenant is rejected (no cross-tenant access). A fresh connection under a function resolver throws instead of silently opening OAuth under a fallback tenant (Mastra passes no context to authorize). - Document connectionId as an unsigned, server-minted handle. - Require zod ^4.4.0 so z.unknown() rejects omitted required fields. - CI: build/test the full lane over adapters/* too, not just packages/*. - demo: pin the LLM gateway host; ignore SQLite sidecar files. * fix(mastra): optional @mastra/core peer, run checks in CI, document fresh-auth limit * feat(mcp): re-export @corsair-dev/mastra via ./mastra subpath The Mastra ToolProvider now has one home: @corsair-dev/mastra. mcp re-exports it from a dedicated subpath instead of carrying a copy, so the two never drift. Subpath, not the main barrel: the provider extends @mastra/core BaseToolProvider, so importing it evaluates @mastra/core. Keeping it under ./mastra means consumers of @corsair-dev/mcp on other frameworks never load @mastra/core (optional peer). Bump the @mastra/core peer to >=1.64.0 to match what the provider needs. * fix(mcp): pin demo LLM baseURL to the gateway * feat(mcp): re-export langchain and llamaindex adapters on subpaths * docs(adapters): add LangChain and LlamaIndex adapter pages * feat(mcp): add env-configured stdio bin for MCP registries * Update package.json * fix(mcp): correct corsair_permissions schema in bin and demos The turnkey stdio bin and the sqlite demos created corsair_permissions with (account_id, endpoint, status), but the permissions runtime inserts and queries token, plugin, args, tenant_id, expires_at and error. Any permission-gated operation would fail with missing-column errors. Align all three schemas with demo/testing/migration.sql. * fix(mastra): reject tool slugs outside the exposed API operations resolveToolsVNext invoked any dotted path the caller passed as a tool slug, so a management/non-API operation (e.g. manage.disconnect) could be executed even though discovery only exposes type: 'api' operations (CWE-470). Validate each slug against the discovered API set at the single resolution chokepoint, after tenant resolution. Adds a guard assertion. * test(langchain): add runnable demo mirroring demo/mastra Real-usage harness for @corsair-dev/langchain: corsairTools -> LangGraph createReactAgent, model routed through the Corsair LLM gateway, sqlite store with the corrected corsair_permissions schema. Run with pnpm --filter corsair-langchain-demo test (needs real Hub + gateway keys). * fix(mcp): reject databases with an outdated permissions schema CREATE TABLE IF NOT EXISTS is a no-op against a database created by an older schema, so a reused CORSAIR_DB_PATH could lack the permission-runtime columns and fail with missing-column errors mid-operation. Check the corsair_permissions columns at startup and exit with a clear message if the database is incompatible. Also removes internal comment markers. * fix(mcp): run plugins with managed auth in the turnkey bin The bin supplies Hub credentials but instantiated each plugin with no options, so plugins defaulted to api_key auth and a fresh npx run had no usable credentials. Pass authType: 'managed' to match the Hub-backed flow. Also tighten the sqlite handle to a typed interface and document the remaining dynamic-import assertions. | 1 个月前 | |
feat: update docs for new packages | 1 天前 | |
add the favicon to docs page (#108) Co-authored-by: Dev Jain <devrjain32200@gmail.com> | 6 个月前 | |
feat(cloud): language client wrappers, useCorsair, cloud-proxy, and cloud CLI (#1741) * feat(clients): Swift Corsair Cloud client wrapper Thin URLSession client mirroring createCorsairCloud: dynamic tool calls (tenant(t).call(plugin, op, args)) + management (connectionStatus, connect link, disconnect, tenants). Zero deps; JSONValue for dynamic in/out. Swift Testing covers URL/bearer/body/data, connection-status, and the error envelope. WRAPPER-SPEC.md pins the canonical shape every language wrapper follows. * docs(cloud): add Corsair Cloud overview page Explains the hosted runtime, the key+URL, the server (createCorsairCloud) and client (CorsairProvider) surfaces, the connect flow, multi-tenancy, and the pull-generated types. Wired into Get Started. * feat(client): add useCorsair() react hook for tool calls Adds client.call(plugin, op, tenantId, args) to the vanilla management client, posting to /:tenant/:plugin/call/:op, and a public useCorsair() hook returning { api, db } built on the existing useAsync state machine. db is a reserved placeholder — no /db route exists yet. Renames the internal context accessor useCorsair -> useCorsairContext to free the name for the new public hook. * feat(clients): Python Corsair Cloud client wrapper Thin stdlib (urllib) client mirroring the Swift/TS shape: with_tenant(t).call( plugin, op, args) -> data, plus manage (connection_status, connect link, disconnect, tenants). CorsairError from the runtime envelope. pytest, no network. * feat(clients): Go Corsair Cloud client wrapper Thin net/http client mirroring the Swift/Python/TS shape: Tenant(id).Call(ctx, plugin, op, args) -> json.RawMessage, plus ConnectionStatus/CreateConnectLink/ Disconnect/Tenants/CreateTenant. *CorsairError from the runtime envelope. httptest-backed tests, stdlib only. * feat(cloud): add corsair/cloud-proxy backend helper createCloudProxy({ apiKey, url }) is a framework-agnostic (Request)=>Response that forwards a same-origin /api/corsair route to the runtime and injects the ck_cloud_ bearer server-side. Drops the caller's Authorization/cookies. Pairs with CorsairProvider so the key never reaches the browser. Docs snippet + wiring. * ci: add clients CI, docs pages, and PyPI publish for cloud wrappers Swift/Python/Go each run their own job on clients/** PRs. Docs get a Client libraries nav group. Python pyproject is publish-ready and gated behind a corsair-cloud-py-v* tag + PYPI_TOKEN secret. Go/Swift READMEs note their monorepo distribution constraints (subdir tags, SPM root). * feat(cli): add corsair cloud list/pull for Corsair Cloud discovery corsair cloud list prints a hosted project's op tree (GET /call, bearer project key). corsair cloud pull writes corsair-env.d.ts, merging the ops into CorsairCloudRegistry so createCorsairCloud() is typed without a plugin list. Nested under `cloud` since top-level `list` is already the local-instance operation lister. * docs(cloud): correct CLI command to 'corsair cloud pull/list' * chore(clients): stop tracking Swift .build output * fix(cli): preserve terminal+nested ops and quote generated d.ts keys insertOp overwrote a terminal op's true with an object when a longer op shared its prefix (users vs users.list), dropping the leaf. Track call state separately from children, and quote every generated key so plugin/op names with punctuation don't break corsair-env.d.ts. * fix(cli): require https for the cloud url, fix env-var test cleanup The cloud key goes out as a bearer token, so an --url pointing at plain http would leak it. Reject non-https unless the host is loopback, matching the reference client's rule. Also stop restoring 'undefined' as a string into process.env in afterEach — delete when the original value was actually unset. * fix(corsair): gate cloud proxy behind https and an authorize hook createCloudProxy forwarded any caller-chosen tenant/plugin/op with the project bearer attached, so once mounted, an unauthenticated caller could reach any op the project key is allowed to invoke. Add an optional authorize hook that runs before forwarding and rejects with 401 when it returns false, document it as required in the Next.js snippet, and reject non-https upstream urls the same way the reference client does. * fix(client): rename useCorsair's api to useApi — it calls hooks internally api() called useAsync (useReducer/useEffect/useRef) but wasn't named like a hook, so nothing stopped a caller from invoking it conditionally, in a loop, or from an event handler and breaking hook order at runtime. Rename to useApi so it reads — and lints — as the hook it already was. * fix(go-client): require https, escape path segments, add redirectUri send() joined path segments raw, so a tenant/plugin/op containing '/' changed the route instead of addressing it — escape each segment with url.PathEscape. Reject non-https base URLs (loopback excepted, so the existing httptest.NewServer-based tests keep working) since the API key rides as a bearer token. Give the default client a finite timeout instead of http.DefaultClient's none. Add the redirectUri param to CreateConnectLink so it matches the Python and Swift wrappers. * fix(python-client): require https, escape path segments, add timeout _request joined tenant/plugin/op into the path unescaped, so a value containing '/' changed the route instead of addressing it — quote each dynamic segment. Reject non-https urls (loopback excepted) since the API key rides as a bearer token, and pass a finite timeout to urlopen so a stalled server can't hang the caller forever. Also bump the setuptools floor to 77, the minimum that understands the Apache-2.0 license-expression syntax already in pyproject.toml. * fix(swift-client): require https, escape path segments; fix docs install send() built the URL path by joining segments raw through URLComponents.path, so a tenant/plugin/op containing '/' changed the route instead of addressing it — percent-encode each segment into percentEncodedPath instead. Reject non-https base URLs (loopback excepted) at call time. Also fix the docs/README install snippet: SPM can't resolve .package(url:) against a monorepo subdirectory, so show a local path dependency instead of a command that can't actually resolve. * ci(clients): pin Xcode 16 for swift test, restrict checkout token macos-14's default Xcode (15.4) has no Testing module, so 'import Testing' failed to compile — pin DEVELOPER_DIR to the Xcode 16.2 image already on the runner. Also set workflow permissions to contents:read and persist-credentials:false on every checkout so same-repo PR test code can't exfiltrate a writable token. * chore: bump corsair 0.1.137 + @corsair-dev/cli 0.1.24 (cloud wrappers + CLI) * docs(cloud): rewrite client docs and fix swift/go client bugs - Rewrite cloud/overview + python/go/swift client docs and READMEs as step-by-step first-time walkthroughs; drop AI-tell phrasing - Move client libraries under Frameworks > Clients in the nav - Swift: strip trailing slash on base URL (was producing //path) - Go: coerce nil args to {} so body is {"args":{}}, not {"args":null} - Add Go + Swift regression tests * fix(cloud): warn when cloud proxy has no authorize gate * docs(cloud): add client icons and swift mirror publish workflow * feat(cloud): resolve createCorsairCloud URL from the key createCorsairCloud({ apiKey }) now derives its base URL from the key slug (ck_cloud_<slug>_<secret> -> https://api.corsair.cloud/<slug>/api/corsair), so the developer passes only the key. `url` stays as an internal dev/test override. * feat(cloud): resolve the URL from the key across all clients Mirror the TS createCorsairCloud change in every client so the developer passes only the API key: Python/Go/Swift clients, cloud-proxy, and the cloud CLI now derive https://api.corsair.cloud/<slug>/api/corsair from a ck_cloud_<slug>_<secret> key; url stays as an optional dev/testing override. Tests: Go 6/6, Swift 8/8, Python + cloud-proxy + CLI green. * docs(cloud): key-only Corsair Cloud docs across the client surface Rewrite the cloud + per-client docs to the one-key contract: the overview, each language guide (Python/Go/Swift), the client READMEs, the cloud-proxy snippet, the WRAPPER-SPEC (source of truth), and the OpenAPI servers block no longer tell developers to copy a URL — the client derives it from the key. Also documents mapping tenants (manage.tenants.list) and marks raw-DB access as coming with BYO DB. * docs(cloud): give the curl example a URL source (derive from key slug) * fix(cloud): parse ck_cloud key slug on '.' delimiter The Hub now mints cloud keys as ck_cloud_<slug>.<secret>. Switch all client URL resolvers (TS core, cloud-proxy, CLI, Python, Go, Swift) to split the slug on the first '.' instead of '_'. The base64url secret can't contain '.', so an older slug-less key resolves to null rather than a wrong URL — locked by a legacy-underscore-key test in each suite. * fix(cloud): harden clients against review findings - cli op-tree: null-prototype maps so op names like toString/constructor key real nodes instead of inherited Object.prototype members - cloud-proxy: a thrown authorize() returns 401, not an escaping 500 - swift: percent-encode literal % in path segments (a%2Fb no longer decodes to a/b server-side), matching Go/Python - go: WithHTTPClient(nil) keeps the default; typed-nil args marshal to {} - reject a query/fragment in cloud URLs across core/proxy/cli (path segments are appended, so it would misroute) - docs: python client is 3.10+ (matches pyproject) - ci: publish-python guards tag==version; publish-swift tags then pushes branch+tag atomically * fix(cloud): ship py.typed marker for the Python client The Typing :: Typed classifier promises inline types; without the marker + package-data, type checkers ignore them for consumers. * fix(cloud): reject an empty tenant id in the language clients TS withTenant rejects an empty tenant; Go/Python/Swift silently built a path with a missing segment (…//plugin/call/op) that misroutes. Guard it: Python raises in with_tenant, Go returns an error from Call, Swift throws EmptyTenantIdError. Tests added for Go and Python. * refactor(cloud): rename to corsairCloud + corsairConnect Per the agreed surface naming: createCorsairCloud -> corsairCloud (the hosted client), createCloudProxy -> corsairConnect (the same-origin route handler), and the subpath corsair/cloud-proxy -> corsair/connect (file cloud-proxy.ts -> connect.ts). CorsairProvider keeps its name. Config type CreateCorsairCloudConfig -> CorsairCloudConfig, CloudProxyOptions -> CorsairConnectOptions. No behavior change. * docs(cloud): fix stale slug delimiter + unslop the cloud docs Overview curl example now derives the slug on the first '.' (not the old last '_'); renamed the stale CloudProxyNext import alias to ConnectExample; tightened the tenants/connection-status note to state exactly what the manage surface returns (redacted) vs what BYO-DB adds later; removed em-dashes across overview, the three client guides, and the connect snippet. * feat(cloud): expose manage.permissions.get on the cloud clients The runtime already serves GET /permissions/:id (admin, same gate as /tenants which the client uses) and POST /permissions/lookup-by-token (public approval-page lookup). Wire both through the TS manage namespace (by id or by token) and add a by-id read to the Python/Go/Swift clients. Lets a project read a tenant's permission grants — scopes and approval status — without touching credentials. * ci: publish corsair-cloud via PyPI trusted publishing (OIDC) Drop the stored PYPI_TOKEN in favor of OIDC: id-token: write + a pypi environment + pypa/gh-action-pypi-publish. Keeps the tag==pyproject version guard. * refactor(cloud): share the key-URL contract + warn on open proxy in prod - Extract cloudUrlFromKey + the https/query-fragment assertion + loopback set into core/cloud/url.ts, consumed by both the cloud client and the connect route handler, so the ck_cloud key format can't drift between them. - corsairConnect now warns about a missing authorize in every environment, production included — suppressing the signal in prod was backwards for a route that forwards any op with the project key. * docs(cloud): handle err in the Go connect/status examples so they compile * feat(cloud): corsairConnect fails closed without authorize An open proxy by omission forwards any tenant/plugin/op with the project key. Require authorize (or an explicit allowUnauthenticated: true opt-in), throwing at construction otherwise. Addresses the proxy-authorization review finding. * chore(cloud): drop internal WRAPPER-SPEC scaffolding doc It was the design spec that guided the client shape — not user-facing (the docs are docs/clients/*.mdx + per-client READMEs) and unreferenced. Doesn't belong in the shipped clients/ dir. | 1 个月前 | |
fix: update docs routing for intro and quick start pages | 1 个月前 | |
docs(env): standardize key env vars to unprefixed names (#1459) | 1 个月前 | |
Moving Docs to Mintlify | 6 个月前 | |
feat: update docs for new packages | 1 天前 | |
Moving Docs to Mintlify | 6 个月前 | |
docs(env): standardize key env vars to unprefixed names (#1459) | 1 个月前 | |
docs: framework guides, use cases, and Hub docs overhaul (#858) | 1 个月前 | |
refactor(corsair): record connect requests in corsair_events, drop corsair_connects (#1463) * refactor(corsair): record connect requests in corsair_events, drop corsair_connects Route connect-request writes into the append-only corsair_events log instead of a dedicated corsair_connects table: - record -> append a connect.request event (payload {plugin, connectUrl}) - read -> newest connect.request across the tenant's accounts, within TTL, unless a newer connect.cleared tombstone supersedes it - clear -> append a connect.cleared tombstone (no mutation/delete) Account FK resolved from (tenant, plugin); setup/ensurePluginRows guarantees the account row exists before a link is minted. Removes the CorsairConnect kysely type, orm/schema entries, the REQUIRED_TABLES registration, and the test DDL. The client HTTP contract (/connect/request, /connect/request/clear) is unchanged. * docs: drop corsair_connects, note connect prompts live in corsair_events * fix(connect): per-plugin connect requests, one-at-a-time FIFO readConnectRequest now scans each account's latest connect event with a bounded per-plugin query and returns the oldest live one (FIFO), so a later plugin failure can't shadow an earlier one. A same-timestamp connect.cleared beats a connect.request via a CASE tiebreak, keeping the prompt suppressed. clearConnectRequest takes a plugin arg and tombstones only that plugin; the handler, client, and React provider thread it through. Index corsair_events(account_id, event_type, created_at) so the per-plugin read is an index seek, not a log scan. * fix(connect): monotonic connect-event timestamps so re-request after clear wins Same-ms clear-then-request kept the prompt suppressed because the CASE tiebreak always let connect.cleared win; event_type can't encode append order. Stamp each connect event's created_at with max(now, latest+1) per account for a strict total order, then read newest by created_at alone. * chore(corsair): bump version to 0.1.127 in package.json | 1 个月前 | |
ci(pr-review): drop Codex auto-fix, make the review loop zero-LLM (#774) | 2 个月前 | |
docs(quick-start): add one-line install for the Corsair agent skill (#1467) | 1 个月前 | |
docs: framework guides, use cases, and Hub docs overhaul (#858) | 1 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 24 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 3 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 天前 | ||
| 6 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 6 个月前 | ||
| 1 天前 | ||
| 6 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 个月前 |