Adds CUNY AI Lab to Pi: the lab's models through the CUNY AI Lab Gateway, and sign-in with your CUNY account. You need a CUNY AI Lab account with model access.
-
Install Pi with Pi's own installer.
macOS or Linux:
curl -fsSL https://pi.dev/install.sh | shWindows PowerShell:
irm https://pi.dev/install.ps1 | iex
Press Enter to accept Pi's suggestions. It installs Node.js too when your computer needs it. When it asks "Start pi now?", type
n: the next step runs in the terminal, not inside Pi. If it says your shell is not using the new install yet, open a new terminal window. -
Add the CUNY AI Lab package:
pi install npm:@cuny-ai-lab/cail-pi -
Start Pi and sign in:
piType
/login cailand choose Sign in with CUNY AI Lab. Your browser opens the lab's sign-in page. Sign in with CUNY Login, click Connect Pi, and return to Pi.Pi then reports that it logged in "but no default model is configured for provider "cail"". That message is expected. Type your first message and Pi answers with DeepSeek V4 Flash.
Working on another computer over SSH? Pi shows a link instead of opening a browser. Open the link on any computer, click Connect Pi, and paste the code it shows into Pi.
You can also choose Sign in with an API key and paste a personal key from Model access.
-
Optional: add web search and questions from Pi. Quit Pi first (type
/quit), then run:pi install npm:pi-web-access pi install npm:pi-ask-userpi-web-access lets Pi search the web and read pages. Search uses Exa's free service and needs no key; many searches sent at the same moment from one network can be refused briefly, and Pi can simply search again. pi-ask-user lets Pi ask you to choose between options before it goes ahead. Both are community packages, separate from this one; if you already have them, skip this step.
- CUNY AI Lab models, listed live from the gateway, so new models appear without updating anything. Choose one with
/model. Sessions start on the newest DeepSeek Flash unless you have saved a default model yourself (Ctrl+Sin/model). - Sign in with CUNY AI Lab in
/login, or a personal API key. /cail, a health check for your setup.- On Windows, Pi's PowerShell tool is turned on in
%USERPROFILE%\.pi\agent\settings.json. With nodefaultToolsset, it becomes["-bash", "+powershell"], so the model uses PowerShell in place of bash and keeps Pi's other default tools. A tool list you chose yourself gets"+powershell"added, unless it already namespowershell(including"-powershell"). A backup of the previous file is kept next to it. Run/reloadto use the tool in the session where it was turned on.
Type /cail inside Pi. It reports whether the gateway is reachable, whether your key is saved and accepted, how many CUNY AI Lab models you can use, which model is active, and on Windows whether the PowerShell tool is on. It never shows your key.
"No API key found for the selected model"
Run /login cail and sign in, as in step 3.
"Your CUNY AI Lab sign-in has expired"
Keys from signing in last 180 days. Type /login cail and sign in again.
"Your CUNY AI Lab access is not active yet" Signing in works only for accounts with lab model access. Open Model access to see how to request it.
"That API key was not accepted" (in /cail)
Check for missing or extra characters and run /login again. Keys are individual; use the one issued to you.
No CUNY AI Lab models in /model
Pi lists them once a key is saved. If /cail says the key is valid but the list stays empty, restart Pi.
Windows: "running scripts is disabled on this system"
Pi's installer removes its PowerShell launcher when the execution policy would block it, so pi runs pi.cmd. If the policy changed after installing, type pi.cmd instead of pi. There is no need to change the execution policy.
Coming from the earlier setup
Versions up to 0.3.0 were set up with npx @cuny-ai-lab/cail-pi, which also ran LazyPi. That installer is gone, and the npx command no longer works. Run pi update --all to move to this version. Your saved key, your settings and the packages LazyPi installed, web search and ask_user among them, stay as they are. Remove any hand-written cail provider from ~/.pi/agent/models.json and any ~/.pi/agent/extensions/cail.ts, since the package registers the same provider. The deprecated npm:@cuny-ai-lab/pi-workshop package also registers it; remove it with pi remove npm:@cuny-ai-lab/pi-workshop.
- Signing in creates a personal key for this computer, named "Pi on" and the computer's name, that expires after 180 days. It is listed in Model access, where you can revoke it. Signing in again from the same computer replaces that key; a key you pasted yourself is never replaced.
- Pi stores your key when you run
/login, in~/.pi/agent/auth.jsonwith owner-only permissions on macOS and Linux. - The CUNY AI Lab extension sends your key only to the CUNY AI Lab gateway: with every model request, and to check it when you run
/cail. - If you add pi-web-access, its searches go to Exa and pages are fetched directly from your computer.
- Advanced users can set
AILAB_API_KEYinstead of running/login.
Update the package with pi update npm:@cuny-ai-lab/cail-pi, and update Pi itself with pi update.
Remove the package with pi remove npm:@cuny-ai-lab/cail-pi. Your key stays in Pi until you run /logout. Logging out does not revoke the key; revoke it in Model access if you no longer need it.
extensions/cail.ts Pi extension: the "cail" provider, the session-start model, the Windows
PowerShell setting and the /cail command
src/cail-catalog.mjs gateway catalog → Pi models, newest DeepSeek Flash first
src/cail-api.mjs key check (/v1/quota) and catalog fetch (/v1/models)
src/doctor.mjs /cail report formatting
src/signin.mjs "Sign in with CUNY AI Lab": loopback or copy-code sign-in, PKCE, key exchange
src/settings.mjs Windows defaultTools merge
test/ node:test suites with a mocked gateway; test/live for the real gateway;
test/smoke installs the packed package into a clean Pi and runs pi -p
The package has no runtime dependencies. Web search and ask_user stay in the separate pi-web-access and pi-ask-user packages rather than inside this one. Pi's package documentation requires another Pi package used as a dependency to be bundled in the tarball, and Pi refuses to start when two loaded extensions register the same tool. A bundled copy would therefore stop Pi for anyone who also installed those packages, as LazyPi does by default.
- The provider is a native pi-ai provider built with
createProviderfrom@earendil-works/pi-ai/compat, usingenvApiKeyAuth("CUNY AI Lab API key", ["AILAB_API_KEY"]), an OAuth sign-in, andopenAICompletionsApi(). Pi owns/login,/logout, credential storage, streaming and catalog persistence. - Sign-in follows the lab's connect flow, documented in the Model Access API README. Pi opens
/model-access/connectwith a PKCE challenge and, on its own computer, a127.0.0.1callback; over SSH (SSH_CONNECTION,SSH_CLIENTorSSH_TTYset) it asks for the code the page shows. It trades the code at/model-access/v1/connect/tokenfor an ordinary personal key and stores it as an OAuth credential whose expiry is the key's. When Pi's storedcailcredential came from an earlier sign-in, the exchange names that key so the lab replaces it. An expired credential cannot be refreshed; Pi reports that the participant should sign in again. - Models come from
GET /v1/models. The response carries names, capabilities, context length, pricing, status and sunset dates, so the package has no static model list. The extension fetches the catalog once at load (public endpoint, 5 s timeout, skipped whenPI_OFFLINEis set) and again throughfetchModelson Pi's refreshes. - Speech models, sunset models, and models whose catalog entry lacks
function-callingare dropped, because Pi sends its tools with every request and the gateway rejects tool calls to such models. Models with no capability information are offered with conservative defaults. - With no saved default, Pi starts on the first available model of a provider it has no built-in default for, so the catalog lists the newest DeepSeek Flash first. After
/login, Pi selects no model for such a provider and holds a placeholder model instead; aninputhandler selects the newest DeepSeek Flash for that session when the first message arrives. Neither writes settings, and a saved default always wins. /cailchecks the key withGET /v1/quota, which needs a valid bearer credential and runs no inference.
npm install
npm test # unit tests, mocked gateway
CAIL_LIVE_TEST_KEY=... npm run test:live # optional, real gateway
npm run test:smoke # packed package in a clean Pi profile, one pi -p call
npm pack --dry-run # review the published filesTry a build in an isolated Pi profile without touching your own. Install it from an unpacked tarball rather than from the checkout, whose node_modules also holds the development copy of @earendil-works/pi-ai:
npm pack --pack-destination /tmp
mkdir -p /tmp/cail-pi-build && tar xzf /tmp/cuny-ai-lab-cail-pi-*.tgz -C /tmp/cail-pi-build --strip-components 1
PI_CODING_AGENT_DIR=/tmp/pi-scratch pi install /tmp/cail-pi-build
PI_CODING_AGENT_DIR=/tmp/pi-scratch piReleases are published by GitHub Actions through npm trusted publishing, so no npm token exists and nobody publishes from their own machine. After testing a build in a clean Pi profile on macOS and Windows, bump the version and push the tag from main:
npm version patch # or minor; commits the bump and tags vX.Y.Z
git push --follow-tagsThe tag starts the publish job in .github/workflows/ci.yml. It waits for the unit suite and the pi -p smoke test to pass on Linux, macOS and Windows, checks that the tag matches the package.json version, and publishes with provenance. The trusted publisher on npmjs.com is tied to that workflow filename, so renaming the file breaks publishing until the npm setting is updated. files in package.json limits the published files to extensions, src, README.md and LICENSE.