@extension.dev/core
v0.2.1
Published
Typed client for the extension.dev platform: GitHub device-code login, credential store, and publish. The shared core under the extension.dev CLI and @extension.dev/mcp.
Maintainers
Readme
@extension.dev/core
Typed client for the extension.dev platform: GitHub device-code login, the local credential store, and the publish flow.
Status: 0.x. The surface is deliberately small (login, credentials, publish) and its on-disk contracts are frozen, but exports may move as the
extension.devCLI lands. See MIGRATION.md for the plan.
This is the shared core under every platform surface, so they cannot drift:
extension.devCLI (planned flagship): humans and CI- @extension.dev/mcp: agents
- Anything else that needs to authenticate against or publish through the platform
The Stripe-SDK posture applies: this client is open and auditable (it is the code that touches your GitHub token and stores credentials on disk), while the platform it talks to stays server-side.
What's inside
| Module | Exports | Purpose |
| --- | --- | --- |
| credentials | credentialsPath, readCredentials, writeCredentials, clearCredentials, readValidCredentials | Versioned 0600 credential file at ~/.config/extension-dev/auth.json (XDG-aware; %APPDATA% on Windows) |
| github-device | startDeviceCode, pollForToken | GitHub OAuth device flow, budget-bounded polling so callers that must return promptly (an MCP tool) can poll in slices |
| login-flow | resolveApiBase, fetchLoginConfig, exchangeAndPersist | Fetch the public client id from the platform, trade a GitHub token for a project-scoped extension.dev token, persist it |
| publish | resolveToken, publish | Publish the token-scoped project; token from EXTENSION_DEV_TOKEN or the credential file, never persisted or logged here |
Usage
import {
fetchLoginConfig,
startDeviceCode,
pollForToken,
exchangeAndPersist,
publish,
resolveApiBase,
} from "@extension.dev/core";
const api = resolveApiBase();
const { clientId, scope } = await fetchLoginConfig(api);
const device = await startDeviceCode({ clientId, scope });
// Show device.userCode + device.verificationUri to the user...
const poll = await pollForToken({
clientId,
deviceCode: device.deviceCode,
interval: device.interval,
budgetMs: device.expiresIn * 1000,
});
if (poll.ok) {
await exchangeAndPersist({
apiBase: api,
githubToken: poll.githubToken,
project: "my-workspace/my-project",
});
const result = await publish({ ttlHours: 24 });
if (result.ok) console.log(result.data.shareUrl);
}Environment variables:
EXTENSION_DEV_TOKEN: explicit access token; takes precedence over the credential fileEXTENSION_DEV_API_URL: platform base URL override (self-hosted/staging)EXTENSION_DEV_GITHUB_CLIENT_ID: device-flow client id override
Development
pnpm install --ignore-workspace
pnpm test
pnpm compileThe extension.dev stack
| Package | Use it to |
| --- | --- |
| @extension.dev/mcp | Give AI agents tools to build, run, debug, and publish extensions |
| @extension.dev/skill | Teach AI agents the judgment half: cross-browser rules, gotchas, playbooks |
| @extension.dev/deploy | Ship to Chrome, Firefox, and Edge stores from CI |
All of it rides on Extension.js, the open-source cross-browser extension framework.
License
MIT
