openlinear
v0.1.32
Published
OpenLinear launcher, installer, and validation utilities
Readme
OpenLinear
A kanban board that executes your tasks.
Describe what you want built. Click execute. Get a pull request.
GitHub · Documentation · Architecture · Releases
What is OpenLinear?
OpenLinear is a project management tool that turns your backlog into pull requests. You manage tasks on a Linear-style kanban board. When you're ready, an AI agent — running locally with your own credentials — clones your repo, creates a branch, writes the code, and opens a PR. No copy-pasting prompts, no context switching.
This package is the official launcher and utility library. It does two things:
- CLI —
openlinearcommand that launches the pre-built desktop app on macOS or Linux. - Library — TypeScript utilities for execution metadata validation, payload sanitization, and feature flag management used internally by the OpenLinear platform.
Installation
Global — CLI launcher (macOS / Linux x64)
npm install -g openlinear
# or
pnpm add -g openlinear
# or
yarn global add openlinear
# or
curl -fsSL https://raw.githubusercontent.com/kaizen403/openlinear/main/install.sh | bash
# or
paru -S openlinear-binThe global launcher currently supports macOS (Apple Silicon / Intel) and Linux x64. The npm installer and the curl installer place the desktop app in ~/.openlinear/. Once complete, run:
openlinearLocal GitHub auth
OpenLinear now prefers a browser-based local callback flow instead of a hosted callback on rixie.in.
If GitHub CLI is already authenticated on your machine, openlinear github login reuses that local token first.
If no local GitHub CLI token is available and OPENLINEAR_GITHUB_CLIENT_SECRET is set, openlinear github login starts a temporary callback server on http://localhost:<port>/callback, opens GitHub in your browser, and stores the resulting token locally.
If you do not want the browser flow, or you have not configured a client secret, the CLI falls back to GitHub device flow. You can also force either mode explicitly.
openlinear github login
openlinear github login --browser
openlinear github login --device
openlinear github status
openlinear github whoami
openlinear github logoutBrowser mode requires a GitHub OAuth app client secret:
export OPENLINEAR_GITHUB_CLIENT_SECRET=your-client-secretThe GitHub OAuth app tied to your client_id also needs a loopback callback configured in GitHub, such as http://localhost/callback.
Optional callback overrides:
export OPENLINEAR_GITHUB_CALLBACK_HOST=localhost
export OPENLINEAR_GITHUB_CALLBACK_PORT=0
export OPENLINEAR_GITHUB_CALLBACK_PATH=/callbackNo hosted callback route is involved. The token is stored locally in ~/.config/openlinear/github-auth.json.
Local — Library API
npm install openlinearCLI
openlinear [args...]The CLI launcher resolves the desktop binary from ~/.openlinear/OpenLinear.app/Contents/MacOS/OpenLinear, ~/.openlinear/openlinear, or ~/.openlinear/openlinear.AppImage, applies Linux-specific environment flags for X11/Wayland when needed, and passes all arguments through to the app.
If the binary is not found, it prints installation instructions and exits:
OpenLinear desktop app not found.
Install it from npm:
npm install -g openlinear
Or download the latest desktop release:
https://github.com/kaizen403/openlinear/releases/latestLibrary API
The package ships four entry points. Import only what you need.
| Entry point | Contents |
|---|---|
| openlinear | Re-exports everything below |
| openlinear/types | Zod schemas, TypeScript types, validation functions |
| openlinear/validation | Payload sanitization and forbidden-field utilities |
| openlinear/config | Feature flag parsing and execution mode helpers |
openlinear/types
Zod-validated types and validation functions for execution metadata synced between the local agent and the cloud dashboard.
Types
import type { ExecutionMetadataSync } from 'openlinear/types';
// ExecutionStatus — z.enum
type ExecutionStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
// ErrorCategory — z.enum
type ErrorCategory = 'AUTH' | 'RATE_LIMIT' | 'MERGE_CONFLICT' | 'TIMEOUT' | 'UNKNOWN';
// ExecutionMetadataSync — the full sync payload shape
interface ExecutionMetadataSync {
version?: '1.0';
taskId: string;
runId: string;
status: ExecutionStatus;
startedAt?: string; // ISO 8601 datetime
completedAt?: string; // ISO 8601 datetime
durationMs?: number; // non-negative integer
branch?: string;
commitSha?: string;
prUrl?: string; // valid URL
prNumber?: number; // positive integer
outcome?: string; // max 500 chars
errorCategory?: ErrorCategory;
}validateExecutionMetadataSync(payload)
Parses and returns the payload as ExecutionMetadataSync. Throws a ZodError if validation fails. Use this when an invalid payload should be a hard error.
import { validateExecutionMetadataSync } from 'openlinear/types';
const metadata = validateExecutionMetadataSync({
taskId: 'tsk_123',
runId: 'run_456',
status: 'completed',
durationMs: 45000,
branch: 'feature/add-login',
prUrl: 'https://github.com/org/repo/pull/42',
prNumber: 42,
});safeValidateExecutionMetadataSync(payload)
Safe variant. Returns a discriminated union — never throws.
import { safeValidateExecutionMetadataSync } from 'openlinear/types';
const result = safeValidateExecutionMetadataSync(payload);
if (result.success) {
console.log('Ready to sync:', result.data);
} else {
console.error('Validation failed:', result.error.errors);
}checkExecutionMetadataSync(payload)
Returns a plain { valid: boolean; issues?: string[] } report. Useful for surfacing human-readable errors in logs or UIs without dealing with ZodError.
import { checkExecutionMetadataSync } from 'openlinear/types';
const { valid, issues } = checkExecutionMetadataSync(payload);
if (!valid) {
issues?.forEach(issue => console.warn(issue));
// e.g. "prUrl: Invalid url"
}validateExecutionMetadataMiddleware()
Express middleware factory. Validates req.body against ExecutionMetadataSyncSchema, sets req.validatedMetadata on success, or responds 400 with a structured error on failure.
import express from 'express';
import { validateExecutionMetadataMiddleware } from 'openlinear/types';
const app = express();
app.use(express.json());
app.post('/sync', validateExecutionMetadataMiddleware(), (req, res) => {
// req.validatedMetadata is typed as ExecutionMetadataSync
res.json({ received: req.validatedMetadata.taskId });
});Error response shape (400):
{
"error": "Invalid sync payload",
"code": "FORBIDDEN_FIELDS | VALIDATION_ERROR",
"details": [{ "field": "prUrl", "message": "Invalid url" }]
}openlinear/validation
Utilities that enforce the trust boundary between local execution and the cloud dashboard. Sensitive fields are never allowed through the sync pipeline.
FORBIDDEN_SYNC_FIELDS
A readonly array of field names that are blocked from cloud sync:
prompt, logs, toolLogs, executionLogs, repoPath, accessToken, apiKey,
passwordHash, jwt, client, timeoutId, rawOutput, diff, fileContents,
env, environment, processEnvisForbiddenField(field)
Returns true if the field name is in FORBIDDEN_SYNC_FIELDS.
import { isForbiddenField } from 'openlinear/validation';
isForbiddenField('accessToken'); // true
isForbiddenField('taskId'); // falsesanitizePayload(payload)
Strips all forbidden fields from an arbitrary object and returns the sanitized result alongside a list of removed keys.
import { sanitizePayload } from 'openlinear/validation';
const { sanitized, removed } = sanitizePayload({
taskId: 'tsk_123',
status: 'completed',
accessToken: 'ghp_...', // forbidden
logs: '[tool output...]', // forbidden
});
// sanitized → { taskId: 'tsk_123', status: 'completed' }
// removed → ['accessToken', 'logs']openlinear/config
Feature flag utilities for managing the gradual rollout of local execution mode.
parseFeatureFlags(env?)
Parses feature flags from process.env (or any key/value map you pass). All flags have safe defaults so this never throws in production.
import { parseFeatureFlags } from 'openlinear/config';
const flags = parseFeatureFlags();
// or override for testing:
const flags = parseFeatureFlags({
LOCAL_EXECUTION_ENABLED: 'true',
CANARY_PERCENTAGE: '25',
});| Flag | Type | Default | Description |
|---|---|---|---|
| LOCAL_EXECUTION_ENABLED | boolean | false | Master switch for local execution |
| SERVER_EXECUTION_ENABLED | boolean | true | Master switch for server execution |
| CANARY_PERCENTAGE | number (0–100) | 0 | % of users routed to local execution |
| FORCE_LOCAL_EXECUTION | boolean | false | Force local for all users (overrides canary) |
| KILL_SWITCH_LOCAL_EXECUTION | boolean | false | Disable local for all users immediately |
getFeatureFlags()
Shorthand for parseFeatureFlags(process.env).
isLocalExecutionEnabled(userId, flags?)
Determines whether local execution is active for a specific user, respecting the kill switch, force flag, and canary percentage in that order.
import { isLocalExecutionEnabled, getFeatureFlags } from 'openlinear/config';
const flags = getFeatureFlags();
const useLocal = isLocalExecutionEnabled('user_abc123', flags);isServerExecutionEnabled(flags?)
Returns flags.SERVER_EXECUTION_ENABLED.
validateFlagConfiguration(flags)
Checks for invalid flag combinations and returns a { valid: boolean; errors: string[] } report.
import { validateFlagConfiguration, getFeatureFlags } from 'openlinear/config';
const { valid, errors } = validateFlagConfiguration(getFeatureFlags());
if (!valid) {
errors.forEach(e => console.error('[config]', e));
}Catches:
FORCE_LOCAL_EXECUTIONandKILL_SWITCH_LOCAL_EXECUTIONboth enabled simultaneously.- Both
LOCAL_EXECUTION_ENABLEDandSERVER_EXECUTION_ENABLEDdisabled (no execution mode active).
getMigrationPhase(flags?)
Returns the current rollout phase as a readable string. Useful for observability and dashboards.
import { getMigrationPhase } from 'openlinear/config';
getMigrationPhase(); // 'shadow' | 'canary' | 'cutover' | 'rollback' | 'unknown'| Phase | Condition |
|---|---|
| rollback | Kill switch is active |
| cutover | Server execution disabled, local is primary |
| canary | Local enabled with CANARY_PERCENTAGE > 0 |
| shadow | Local enabled but CANARY_PERCENTAGE === 0 |
| unknown | No recognizable state |
Security & Trust Boundaries
The sync pipeline enforces a strict boundary to ensure sensitive data never leaves your machine.
| Category | Examples | Synced to cloud? |
|---|---|---|
| Safe metadata | taskId, status, durationMs, branch, prUrl | Yes |
| Local-only paths | repoPath, env, environment | No — stripped |
| Credentials | accessToken, apiKey, passwordHash, jwt | No — stripped |
| Raw agent output | prompt, logs, toolLogs, diff, rawOutput | No — stripped |
Any payload passing through sanitizePayload or safeValidateExecutionMetadataSync has forbidden fields automatically removed or rejected before they can reach the network.
Repository status
The hosted OpenLinear product has been reduced to a static Vercel landing site plus installer surface. This npm package remains available for launcher and utility use.
Building from Source
git clone https://github.com/kaizen403/openlinear.git
cd openlinear
pnpm install
# Build the npm package
pnpm --filter openlinear build
# Run the landing site from the repo root
pnpm --filter @openlinear/landing devDistribution
| Format | Platform | Install |
|---|---|---|
| npm | macOS, Linux x64 | npm install -g openlinear |
| curl | macOS, Linux x64 | curl -fsSL https://raw.githubusercontent.com/kaizen403/openlinear/main/install.sh | bash |
| GitHub Releases | macOS, Linux x64 | https://github.com/kaizen403/openlinear/releases/latest |
Contributing
Contributions are welcome. Please open an issue first to discuss what you'd like to change.
The npm package lives at packages/openlinear in the monorepo.
cd packages/openlinear
pnpm build