refactor: rename packages to @paperclipai and CLI binary to paperclipai

Rename all workspace packages from @paperclip/* to @paperclipai/* and
the CLI binary from `paperclip` to `paperclipai` in preparation for
npm publishing. Bump CLI version to 0.1.0 and add package metadata
(description, keywords, license, repository, files). Update all
imports, documentation, user-facing messages, and tests accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-03-03 08:45:26 -06:00
parent 5a5549fc54
commit f60c1001ec
196 changed files with 501 additions and 490 deletions

View File

@@ -172,7 +172,7 @@ Paperclip handles the hard orchestration details correctly.
Open source. Self-hosted. No Paperclip account required.
```bash
npx paperclip onboard
npx paperclipai onboard
```
Or manually:

View File

@@ -1,11 +1,22 @@
{
"name": "@paperclip/cli",
"version": "0.0.1",
"private": true,
"name": "paperclipai",
"version": "0.1.0",
"description": "Paperclip CLI — orchestrate AI agent teams to run a business",
"type": "module",
"bin": {
"paperclip": "./dist/index.js"
"paperclipai": "./dist/index.js"
},
"keywords": ["paperclip", "ai", "agents", "orchestration", "cli"],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/paperclipai/paperclip.git",
"directory": "cli"
},
"homepage": "https://github.com/paperclipai/paperclip",
"files": [
"dist"
],
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc",
@@ -13,13 +24,13 @@
},
"dependencies": {
"@clack/prompts": "^0.10.0",
"@paperclip/adapter-claude-local": "workspace:*",
"@paperclip/adapter-codex-local": "workspace:*",
"@paperclip/adapter-openclaw": "workspace:*",
"@paperclip/adapter-utils": "workspace:*",
"@paperclip/db": "workspace:*",
"@paperclip/server": "workspace:*",
"@paperclip/shared": "workspace:*",
"@paperclipai/adapter-claude-local": "workspace:*",
"@paperclipai/adapter-codex-local": "workspace:*",
"@paperclipai/adapter-openclaw": "workspace:*",
"@paperclipai/adapter-utils": "workspace:*",
"@paperclipai/db": "workspace:*",
"@paperclipai/server": "workspace:*",
"@paperclipai/shared": "workspace:*",
"drizzle-orm": "0.38.4",
"dotenv": "^17.0.1",
"commander": "^13.1.0",

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import type { Company } from "@paperclip/shared";
import type { Company } from "@paperclipai/shared";
import { assertDeleteConfirmation, resolveCompanyForDeletion } from "../commands/client/company.js";
function makeCompany(overrides: Partial<Company>): Company {

View File

@@ -1,4 +1,4 @@
import type { CLIAdapterModule } from "@paperclip/adapter-utils";
import type { CLIAdapterModule } from "@paperclipai/adapter-utils";
import { printHttpStdoutEvent } from "./format-event.js";
export const httpCLIAdapter: CLIAdapterModule = {

View File

@@ -1,2 +1,2 @@
export { getCLIAdapter } from "./registry.js";
export type { CLIAdapterModule } from "@paperclip/adapter-utils";
export type { CLIAdapterModule } from "@paperclipai/adapter-utils";

View File

@@ -1,4 +1,4 @@
import type { CLIAdapterModule } from "@paperclip/adapter-utils";
import type { CLIAdapterModule } from "@paperclipai/adapter-utils";
import { printProcessStdoutEvent } from "./format-event.js";
export const processCLIAdapter: CLIAdapterModule = {

View File

@@ -1,7 +1,7 @@
import type { CLIAdapterModule } from "@paperclip/adapter-utils";
import { printClaudeStreamEvent } from "@paperclip/adapter-claude-local/cli";
import { printCodexStreamEvent } from "@paperclip/adapter-codex-local/cli";
import { printOpenClawStreamEvent } from "@paperclip/adapter-openclaw/cli";
import type { CLIAdapterModule } from "@paperclipai/adapter-utils";
import { printClaudeStreamEvent } from "@paperclipai/adapter-claude-local/cli";
import { printCodexStreamEvent } from "@paperclipai/adapter-codex-local/cli";
import { printOpenClawStreamEvent } from "@paperclipai/adapter-openclaw/cli";
import { processCLIAdapter } from "./process/index.js";
import { httpCLIAdapter } from "./http/index.js";

View File

@@ -10,7 +10,7 @@ export function configCheck(configPath?: string): CheckResult {
status: "fail",
message: `Config file not found at ${filePath}`,
canRepair: false,
repairHint: "Run `paperclip onboard` to create one",
repairHint: "Run `paperclipai onboard` to create one",
};
}
@@ -27,7 +27,7 @@ export function configCheck(configPath?: string): CheckResult {
status: "fail",
message: `Invalid config: ${err instanceof Error ? err.message : String(err)}`,
canRepair: false,
repairHint: "Run `paperclip configure --section database` (or `paperclip onboard` to recreate)",
repairHint: "Run `paperclipai configure --section database` (or `paperclipai onboard` to recreate)",
};
}
}

View File

@@ -11,12 +11,12 @@ export async function databaseCheck(config: PaperclipConfig, configPath?: string
status: "fail",
message: "PostgreSQL mode selected but no connection string configured",
canRepair: false,
repairHint: "Run `paperclip configure --section database`",
repairHint: "Run `paperclipai configure --section database`",
};
}
try {
const { createDb } = await import("@paperclip/db");
const { createDb } = await import("@paperclipai/db");
const db = createDb(config.database.connectionString);
await db.execute("SELECT 1");
return {
@@ -62,6 +62,6 @@ export async function databaseCheck(config: PaperclipConfig, configPath?: string
status: "fail",
message: `Unknown database mode: ${String(config.database.mode)}`,
canRepair: false,
repairHint: "Run `paperclip configure --section database`",
repairHint: "Run `paperclipai configure --section database`",
};
}

View File

@@ -18,7 +18,7 @@ export function deploymentAuthCheck(config: PaperclipConfig): CheckResult {
status: "fail",
message: `local_trusted requires loopback host binding (found ${config.server.host})`,
canRepair: false,
repairHint: "Run `paperclip configure --section server` and set host to 127.0.0.1",
repairHint: "Run `paperclipai configure --section server` and set host to 127.0.0.1",
};
}
return {
@@ -47,7 +47,7 @@ export function deploymentAuthCheck(config: PaperclipConfig): CheckResult {
status: "fail",
message: "auth.baseUrlMode=explicit requires auth.publicBaseUrl",
canRepair: false,
repairHint: "Run `paperclip configure --section server` and provide a base URL",
repairHint: "Run `paperclipai configure --section server` and provide a base URL",
};
}
@@ -58,7 +58,7 @@ export function deploymentAuthCheck(config: PaperclipConfig): CheckResult {
status: "fail",
message: "authenticated/public requires explicit auth.publicBaseUrl",
canRepair: false,
repairHint: "Run `paperclip configure --section server` and select public exposure",
repairHint: "Run `paperclipai configure --section server` and select public exposure",
};
}
try {
@@ -78,7 +78,7 @@ export function deploymentAuthCheck(config: PaperclipConfig): CheckResult {
status: "fail",
message: "auth.publicBaseUrl is not a valid URL",
canRepair: false,
repairHint: "Run `paperclip configure --section server` and provide a valid URL",
repairHint: "Run `paperclipai configure --section server` and provide a valid URL",
};
}
}

View File

@@ -8,7 +8,7 @@ export async function llmCheck(config: PaperclipConfig): Promise<CheckResult> {
status: "warn",
message: "No LLM provider configured",
canRepair: false,
repairHint: "Run `paperclip configure --section llm` to set one up",
repairHint: "Run `paperclipai configure --section llm` to set one up",
};
}
@@ -18,7 +18,7 @@ export async function llmCheck(config: PaperclipConfig): Promise<CheckResult> {
status: "warn",
message: `${config.llm.provider} configured but no API key set`,
canRepair: false,
repairHint: "Run `paperclip configure --section llm`",
repairHint: "Run `paperclipai configure --section llm`",
};
}
@@ -46,7 +46,7 @@ export async function llmCheck(config: PaperclipConfig): Promise<CheckResult> {
status: "fail",
message: "Claude API key is invalid (401)",
canRepair: false,
repairHint: "Run `paperclip configure --section llm`",
repairHint: "Run `paperclipai configure --section llm`",
};
}
return {
@@ -67,7 +67,7 @@ export async function llmCheck(config: PaperclipConfig): Promise<CheckResult> {
status: "fail",
message: "OpenAI API key is invalid (401)",
canRepair: false,
repairHint: "Run `paperclip configure --section llm`",
repairHint: "Run `paperclipai configure --section llm`",
};
}
return {

View File

@@ -53,7 +53,7 @@ export function secretsCheck(config: PaperclipConfig, configPath?: string): Chec
status: "fail",
message: `${provider} is configured, but this build only supports local_encrypted`,
canRepair: false,
repairHint: "Run `paperclip configure --section secrets` and set provider to local_encrypted",
repairHint: "Run `paperclipai configure --section secrets` and set provider to local_encrypted",
};
}

View File

@@ -45,7 +45,7 @@ export function storageCheck(config: PaperclipConfig, configPath?: string): Chec
status: "fail",
message: "S3 storage requires non-empty bucket and region",
canRepair: false,
repairHint: "Run `paperclip configure --section storage`",
repairHint: "Run `paperclipai configure --section storage`",
};
}

View File

@@ -2,7 +2,7 @@ import { createHash, randomBytes } from "node:crypto";
import * as p from "@clack/prompts";
import pc from "picocolors";
import { and, eq, gt, isNull } from "drizzle-orm";
import { createDb, instanceUserRoles, invites } from "@paperclip/db";
import { createDb, instanceUserRoles, invites } from "@paperclipai/db";
import { readConfig, resolveConfigPath } from "../config/store.js";
function hashToken(token: string) {

View File

@@ -1,5 +1,5 @@
import { Command } from "commander";
import type { ActivityEvent } from "@paperclip/shared";
import type { ActivityEvent } from "@paperclipai/shared";
import {
addCommonClientOptions,
formatInlineRecord,

View File

@@ -1,5 +1,5 @@
import { Command } from "commander";
import type { Agent } from "@paperclip/shared";
import type { Agent } from "@paperclipai/shared";
import {
addCommonClientOptions,
formatInlineRecord,

View File

@@ -6,7 +6,7 @@ import {
resubmitApprovalSchema,
type Approval,
type ApprovalComment,
} from "@paperclip/shared";
} from "@paperclipai/shared";
import {
addCommonClientOptions,
formatInlineRecord,

View File

@@ -65,7 +65,7 @@ export function resolveCommandContext(
if (opts?.requireCompany && !companyId) {
throw new Error(
"Company ID is required. Pass --company-id, set PAPERCLIP_COMPANY_ID, or set context profile companyId via `paperclip context set`.",
"Company ID is required. Pass --company-id, set PAPERCLIP_COMPANY_ID, or set context profile companyId via `paperclipai context set`.",
);
}

View File

@@ -8,7 +8,7 @@ import type {
CompanyPortabilityManifest,
CompanyPortabilityPreviewResult,
CompanyPortabilityImportResult,
} from "@paperclip/shared";
} from "@paperclipai/shared";
import { ApiRequestError } from "../../client/http.js";
import {
addCommonClientOptions,

View File

@@ -1,5 +1,5 @@
import { Command } from "commander";
import type { DashboardSummary } from "@paperclip/shared";
import type { DashboardSummary } from "@paperclipai/shared";
import {
addCommonClientOptions,
handleCommandError,

View File

@@ -6,7 +6,7 @@ import {
updateIssueSchema,
type Issue,
type IssueComment,
} from "@paperclip/shared";
} from "@paperclipai/shared";
import {
addCommonClientOptions,
formatInlineRecord,

View File

@@ -67,7 +67,7 @@ export async function configure(opts: {
const configPath = resolveConfigPath(opts.config);
if (!configExists(opts.config)) {
p.log.error("No config file found. Run `paperclip onboard` first.");
p.log.error("No config file found. Run `paperclipai onboard` first.");
p.outro("");
return;
}

View File

@@ -49,7 +49,7 @@ export async function doctor(opts: {
status: "fail",
message: `Could not read config: ${err instanceof Error ? err.message : String(err)}`,
canRepair: false,
repairHint: "Run `paperclip configure --section database` or `paperclip onboard`",
repairHint: "Run `paperclipai configure --section database` or `paperclipai onboard`",
};
results.push(readResult);
printResult(readResult);

View File

@@ -1,6 +1,6 @@
import { setTimeout as delay } from "node:timers/promises";
import pc from "picocolors";
import type { Agent, HeartbeatRun, HeartbeatRunEvent, HeartbeatRunStatus } from "@paperclip/shared";
import type { Agent, HeartbeatRun, HeartbeatRunEvent, HeartbeatRunStatus } from "@paperclipai/shared";
import { getCLIAdapter } from "../adapters/index.js";
import { resolveCommandContext } from "./client/common.js";

View File

@@ -14,7 +14,7 @@ import { describeLocalInstancePaths, resolvePaperclipInstanceId } from "../confi
import { bootstrapCeoInvite } from "./auth-bootstrap-ceo.js";
export async function onboard(opts: { config?: string }): Promise<void> {
p.intro(pc.bgCyan(pc.black(" paperclip onboard ")));
p.intro(pc.bgCyan(pc.black(" paperclipai onboard ")));
const instance = describeLocalInstancePaths(resolvePaperclipInstanceId());
p.log.message(
pc.dim(
@@ -46,12 +46,12 @@ export async function onboard(opts: { config?: string }): Promise<void> {
const s = p.spinner();
s.start("Testing database connection...");
try {
const { createDb } = await import("@paperclip/db");
const { createDb } = await import("@paperclipai/db");
const db = createDb(database.connectionString);
await db.execute("SELECT 1");
s.stop("Database connection successful");
} catch (err) {
s.stop(pc.yellow("Could not connect to database — you can fix this later with `paperclip doctor`"));
s.stop(pc.yellow("Could not connect to database — you can fix this later with `paperclipai doctor`"));
}
}
@@ -172,7 +172,7 @@ export async function onboard(opts: { config?: string }): Promise<void> {
"Configuration saved",
);
p.log.info(`Run ${pc.cyan("pnpm paperclip doctor")} to verify your setup.`);
p.log.info(`Run ${pc.cyan("pnpm paperclipai doctor")} to verify your setup.`);
p.log.message(
`Before starting Paperclip, export ${pc.cyan("PAPERCLIP_AGENT_JWT_SECRET")} from ${pc.dim(envFilePath)} (for example: ${pc.dim(`set -a; source ${envFilePath}; set +a`)})`,
);

View File

@@ -32,7 +32,7 @@ export async function runCommand(opts: RunOptions): Promise<void> {
const configPath = resolveConfigPath(opts.config);
process.env.PAPERCLIP_CONFIG = configPath;
p.intro(pc.bgCyan(pc.black(" paperclip run ")));
p.intro(pc.bgCyan(pc.black(" paperclipai run ")));
p.log.message(pc.dim(`Home: ${paths.homeDir}`));
p.log.message(pc.dim(`Instance: ${paths.instanceId}`));
p.log.message(pc.dim(`Config: ${configPath}`));
@@ -40,7 +40,7 @@ export async function runCommand(opts: RunOptions): Promise<void> {
if (!configExists(configPath)) {
if (!process.stdin.isTTY || !process.stdout.isTTY) {
p.log.error("No config found and terminal is non-interactive.");
p.log.message(`Run ${pc.cyan("paperclip onboard")} once, then retry ${pc.cyan("paperclip run")}.`);
p.log.message(`Run ${pc.cyan("paperclipai onboard")} once, then retry ${pc.cyan("paperclipai run")}.`);
process.exit(1);
}
@@ -72,8 +72,8 @@ async function importServerEntry(): Promise<void> {
];
const specifierCandidates: string[] = [
"@paperclip/server/dist/index.js",
"@paperclip/server/src/index.ts",
"@paperclipai/server/dist/index.js",
"@paperclipai/server/src/index.ts",
];
const importErrors: string[] = [];

View File

@@ -25,7 +25,7 @@ function parseEnvFile(contents: string) {
function renderEnvFile(entries: Record<string, string>) {
const lines = [
"# Paperclip environment variables",
"# Generated by `paperclip onboard`",
"# Generated by `paperclipai onboard`",
...Object.entries(entries).map(([key, value]) => `${key}=${value}`),
"",
];

View File

@@ -23,4 +23,4 @@ export {
type SecretsConfig,
type SecretsLocalEncryptedConfig,
type ConfigMeta,
} from "@paperclip/shared";
} from "@paperclipai/shared";

View File

@@ -22,9 +22,9 @@ const DATA_DIR_OPTION_HELP =
"Paperclip data directory root (isolates state from ~/.paperclip)";
program
.name("paperclip")
.name("paperclipai")
.description("Paperclip CLI — setup, diagnose, and configure your instance")
.version("0.0.1");
.version("0.1.0");
program.hook("preAction", (_thisCommand, actionCommand) => {
const options = actionCommand.optsWithGlobals() as DataDirOptionLike;

View File

@@ -1,5 +1,5 @@
import * as p from "@clack/prompts";
import type { SecretProvider } from "@paperclip/shared";
import type { SecretProvider } from "@paperclipai/shared";
import type { SecretsConfig } from "../config/schema.js";
import { resolveDefaultSecretsKeyFilePath, resolvePaperclipInstanceId } from "../config/home.js";

View File

@@ -10,19 +10,19 @@ Paperclip CLI now supports both:
Use repo script in development:
```sh
pnpm paperclip --help
pnpm paperclipai --help
```
First-time local bootstrap + run:
```sh
pnpm paperclip run
pnpm paperclipai run
```
Choose local instance:
```sh
pnpm paperclip run --instance dev
pnpm paperclipai run --instance dev
```
## Deployment Modes
@@ -31,16 +31,16 @@ Mode taxonomy and design intent are documented in `doc/DEPLOYMENT-MODES.md`.
Current CLI behavior:
- `paperclip onboard` and `paperclip configure --section server` set deployment mode in config
- `paperclipai onboard` and `paperclipai configure --section server` set deployment mode in config
- runtime can override mode with `PAPERCLIP_DEPLOYMENT_MODE`
- `paperclip run` and `paperclip doctor` do not yet expose a direct `--mode` flag
- `paperclipai run` and `paperclipai doctor` do not yet expose a direct `--mode` flag
Target behavior (planned) is documented in `doc/DEPLOYMENT-MODES.md` section 5.
Allow an authenticated/private hostname (for example custom Tailscale DNS):
```sh
pnpm paperclip allowed-hostname dotta-macbook-pro
pnpm paperclipai allowed-hostname dotta-macbook-pro
```
All client commands support:
@@ -57,8 +57,8 @@ Company-scoped commands also support `--company-id <id>`.
Use `--data-dir` on any CLI command to isolate all default local state (config/context/db/logs/storage/secrets) away from `~/.paperclip`:
```sh
pnpm paperclip run --data-dir ./tmp/paperclip-dev
pnpm paperclip issue list --data-dir ./tmp/paperclip-dev
pnpm paperclipai run --data-dir ./tmp/paperclip-dev
pnpm paperclipai issue list --data-dir ./tmp/paperclip-dev
```
## Context Profiles
@@ -66,32 +66,32 @@ pnpm paperclip issue list --data-dir ./tmp/paperclip-dev
Store local defaults in `~/.paperclip/context.json`:
```sh
pnpm paperclip context set --api-base http://localhost:3100 --company-id <company-id>
pnpm paperclip context show
pnpm paperclip context list
pnpm paperclip context use default
pnpm paperclipai context set --api-base http://localhost:3100 --company-id <company-id>
pnpm paperclipai context show
pnpm paperclipai context list
pnpm paperclipai context use default
```
To avoid storing secrets in context, set `apiKeyEnvVarName` and keep the key in env:
```sh
pnpm paperclip context set --api-key-env-var-name PAPERCLIP_API_KEY
pnpm paperclipai context set --api-key-env-var-name PAPERCLIP_API_KEY
export PAPERCLIP_API_KEY=...
```
## Company Commands
```sh
pnpm paperclip company list
pnpm paperclip company get <company-id>
pnpm paperclip company delete <company-id-or-prefix> --yes --confirm <same-id-or-prefix>
pnpm paperclipai company list
pnpm paperclipai company get <company-id>
pnpm paperclipai company delete <company-id-or-prefix> --yes --confirm <same-id-or-prefix>
```
Examples:
```sh
pnpm paperclip company delete PAP --yes --confirm PAP
pnpm paperclip company delete 5cbe79ee-acb3-4597-896e-7662742593cd --yes --confirm 5cbe79ee-acb3-4597-896e-7662742593cd
pnpm paperclipai company delete PAP --yes --confirm PAP
pnpm paperclipai company delete 5cbe79ee-acb3-4597-896e-7662742593cd --yes --confirm 5cbe79ee-acb3-4597-896e-7662742593cd
```
Notes:
@@ -102,45 +102,45 @@ Notes:
## Issue Commands
```sh
pnpm paperclip issue list --company-id <company-id> [--status todo,in_progress] [--assignee-agent-id <agent-id>] [--match text]
pnpm paperclip issue get <issue-id-or-identifier>
pnpm paperclip issue create --company-id <company-id> --title "..." [--description "..."] [--status todo] [--priority high]
pnpm paperclip issue update <issue-id> [--status in_progress] [--comment "..."]
pnpm paperclip issue comment <issue-id> --body "..." [--reopen]
pnpm paperclip issue checkout <issue-id> --agent-id <agent-id> [--expected-statuses todo,backlog,blocked]
pnpm paperclip issue release <issue-id>
pnpm paperclipai issue list --company-id <company-id> [--status todo,in_progress] [--assignee-agent-id <agent-id>] [--match text]
pnpm paperclipai issue get <issue-id-or-identifier>
pnpm paperclipai issue create --company-id <company-id> --title "..." [--description "..."] [--status todo] [--priority high]
pnpm paperclipai issue update <issue-id> [--status in_progress] [--comment "..."]
pnpm paperclipai issue comment <issue-id> --body "..." [--reopen]
pnpm paperclipai issue checkout <issue-id> --agent-id <agent-id> [--expected-statuses todo,backlog,blocked]
pnpm paperclipai issue release <issue-id>
```
## Agent Commands
```sh
pnpm paperclip agent list --company-id <company-id>
pnpm paperclip agent get <agent-id>
pnpm paperclipai agent list --company-id <company-id>
pnpm paperclipai agent get <agent-id>
```
## Approval Commands
```sh
pnpm paperclip approval list --company-id <company-id> [--status pending]
pnpm paperclip approval get <approval-id>
pnpm paperclip approval create --company-id <company-id> --type hire_agent --payload '{"name":"..."}' [--issue-ids <id1,id2>]
pnpm paperclip approval approve <approval-id> [--decision-note "..."]
pnpm paperclip approval reject <approval-id> [--decision-note "..."]
pnpm paperclip approval request-revision <approval-id> [--decision-note "..."]
pnpm paperclip approval resubmit <approval-id> [--payload '{"...":"..."}']
pnpm paperclip approval comment <approval-id> --body "..."
pnpm paperclipai approval list --company-id <company-id> [--status pending]
pnpm paperclipai approval get <approval-id>
pnpm paperclipai approval create --company-id <company-id> --type hire_agent --payload '{"name":"..."}' [--issue-ids <id1,id2>]
pnpm paperclipai approval approve <approval-id> [--decision-note "..."]
pnpm paperclipai approval reject <approval-id> [--decision-note "..."]
pnpm paperclipai approval request-revision <approval-id> [--decision-note "..."]
pnpm paperclipai approval resubmit <approval-id> [--payload '{"...":"..."}']
pnpm paperclipai approval comment <approval-id> --body "..."
```
## Activity Commands
```sh
pnpm paperclip activity list --company-id <company-id> [--agent-id <agent-id>] [--entity-type issue] [--entity-id <id>]
pnpm paperclipai activity list --company-id <company-id> [--agent-id <agent-id>] [--entity-type issue] [--entity-id <id>]
```
## Dashboard Commands
```sh
pnpm paperclip dashboard get --company-id <company-id>
pnpm paperclipai dashboard get --company-id <company-id>
```
## Heartbeat Command
@@ -148,7 +148,7 @@ pnpm paperclip dashboard get --company-id <company-id>
`heartbeat run` now also supports context/api-key options and uses the shared client stack:
```sh
pnpm paperclip heartbeat run --agent-id <agent-id> [--api-base http://localhost:3100] [--api-key <token>]
pnpm paperclipai heartbeat run --agent-id <agent-id> [--api-base http://localhost:3100] [--api-key <token>]
```
## Local Storage Defaults
@@ -164,7 +164,7 @@ Default local instance root is `~/.paperclip/instances/default`:
Override base home or instance with env vars:
```sh
PAPERCLIP_HOME=/custom/home PAPERCLIP_INSTANCE_ID=dev pnpm paperclip run
PAPERCLIP_HOME=/custom/home PAPERCLIP_INSTANCE_ID=dev pnpm paperclipai run
```
## Storage Configuration
@@ -172,7 +172,7 @@ PAPERCLIP_HOME=/custom/home PAPERCLIP_INSTANCE_ID=dev pnpm paperclip run
Configure storage provider and settings:
```sh
pnpm paperclip configure --section storage
pnpm paperclipai configure --section storage
```
Supported providers:

View File

@@ -209,7 +209,7 @@ ClipHub is a **separate service** from Paperclip itself. Paperclip is self-hoste
|---|---|
| **ClipHub Web** | Browse, search, discover, comment, star — the website |
| **ClipHub API** | Registry API for publishing, downloading, searching programmatically |
| **Paperclip CLI** | `paperclip install`, `paperclip publish`, `paperclip cliphub sync` — built into Paperclip |
| **Paperclip CLI** | `paperclipai install`, `paperclipai publish`, `paperclipai cliphub sync` — built into Paperclip |
| **Paperclip UI** | "Browse ClipHub" panel in the Paperclip web UI for discovering templates without leaving the app |
### Tech Stack
@@ -260,7 +260,7 @@ Report
1. Open ClipHub, browse by category or search "dev shop for building SaaS"
2. Find a template that fits — "Lean SaaS Dev Shop (CEO + CTO + 3 Engineers)"
3. Read the description, inspect the org chart, check the comments
4. Run `paperclip install cliphub:acme/lean-saas-shop`
4. Run `paperclipai install cliphub:acme/lean-saas-shop`
5. Paperclip creates the company locally with all agents pre-configured
6. Set your API keys, adjust budgets, add your initial tasks
7. Hit go
@@ -268,8 +268,8 @@ Report
### "I built something great and want to share it"
1. Build and iterate on a company in Paperclip until it works well
2. Export: `paperclip export --template my-agency`
3. Publish: `paperclip publish cliphub my-agency`
2. Export: `paperclipai export --template my-agency`
3. Publish: `paperclipai publish cliphub my-agency`
4. Fill in description, category, tags on the web UI
5. Template is live — others can find and install it
@@ -285,7 +285,7 @@ Report
1. Search ClipHub for agent templates: "senior python engineer"
2. Find a well-starred agent config
3. Install just that agent: `paperclip install cliphub:acme/senior-python-eng --agent`
3. Install just that agent: `paperclipai install cliphub:acme/senior-python-eng --agent`
4. Assign it to a manager in your existing company
5. Done
@@ -311,7 +311,7 @@ ClipHub is to Paperclip what a package registry is to a language runtime: option
- [ ] Template browsing (list, filter by category)
- [ ] Template detail page (description, org chart, agent list, install command)
- [ ] Semantic search (vector embeddings)
- [ ] `paperclip install cliphub:<publisher>/<slug>` CLI command
- [ ] `paperclipai install cliphub:<publisher>/<slug>` CLI command
- [ ] GitHub OAuth authentication
- [ ] Stars
- [ ] Download counts
@@ -326,7 +326,7 @@ ClipHub is to Paperclip what a package registry is to a language runtime: option
- [ ] Verified publisher badges
- [ ] Automated security scanning of adapter configs
- [ ] "Browse ClipHub" panel in Paperclip web UI
- [ ] `paperclip cliphub sync` for bulk publishing
- [ ] `paperclipai cliphub sync` for bulk publishing
- [ ] Publisher profiles and portfolios
### Not in Scope

View File

@@ -150,7 +150,7 @@ PAPERCLIP_SECRETS_STRICT_MODE=true
You can set strict mode and provider defaults via:
```sh
pnpm paperclip configure --section secrets
pnpm paperclipai configure --section secrets
```
Inline secret migration command:

View File

@@ -50,7 +50,7 @@ This keeps one authenticated auth stack while still separating low-friction priv
Default onboarding remains interactive and flagless:
```sh
pnpm paperclip onboard
pnpm paperclipai onboard
```
Server prompt behavior:
@@ -71,7 +71,7 @@ Server prompt behavior:
Default doctor remains flagless:
```sh
pnpm paperclip doctor
pnpm paperclipai doctor
```
Doctor reads configured mode/exposure and applies mode-aware checks. Optional override flags are secondary.

View File

@@ -40,7 +40,7 @@ This runs dev as `authenticated/private` and binds the server to `0.0.0.0` for p
Allow additional private hostnames (for example custom Tailscale hostnames):
```sh
pnpm paperclip allowed-hostname dotta-macbook-pro
pnpm paperclipai allowed-hostname dotta-macbook-pro
```
## One-Command Local Run
@@ -48,13 +48,13 @@ pnpm paperclip allowed-hostname dotta-macbook-pro
For a first-time local install, you can bootstrap and run in one command:
```sh
pnpm paperclip run
pnpm paperclipai run
```
`paperclip run` does:
`paperclipai run` does:
1. auto-onboard if config is missing
2. `paperclip doctor` with repair enabled
2. `paperclipai doctor` with repair enabled
3. starts the server when checks pass
## Docker Quickstart (No local Node install)
@@ -89,7 +89,7 @@ The server will automatically use embedded PostgreSQL and persist data at:
Override home and instance:
```sh
PAPERCLIP_HOME=/custom/path PAPERCLIP_INSTANCE_ID=dev pnpm paperclip run
PAPERCLIP_HOME=/custom/path PAPERCLIP_INSTANCE_ID=dev pnpm paperclipai run
```
No Docker or external database is required for this mode.
@@ -103,7 +103,7 @@ For local development, the default storage provider is `local_disk`, which persi
Configure storage provider/settings:
```sh
pnpm paperclip configure --section storage
pnpm paperclipai configure --section storage
```
## Default Agent Workspaces
@@ -159,9 +159,9 @@ When strict mode is enabled, sensitive env keys (for example `*_API_KEY`, `*_TOK
CLI configuration support:
- `pnpm paperclip onboard` writes a default `secrets` config section (`local_encrypted`, strict mode off, key file path set) and creates a local key file when needed.
- `pnpm paperclip configure --section secrets` lets you update provider/strict mode/key path and creates the local key file when needed.
- `pnpm paperclip doctor` validates secrets adapter configuration and can create a missing local key file with `--repair`.
- `pnpm paperclipai onboard` writes a default `secrets` config section (`local_encrypted`, strict mode off, key file path set) and creates a local key file when needed.
- `pnpm paperclipai configure --section secrets` lets you update provider/strict mode/key path and creates the local key file when needed.
- `pnpm paperclipai doctor` validates secrets adapter configuration and can create a missing local key file with `--repair`.
Migration helper for existing inline env secrets:
@@ -190,22 +190,22 @@ Paperclip CLI now includes client-side control-plane commands in addition to set
Quick examples:
```sh
pnpm paperclip issue list --company-id <company-id>
pnpm paperclip issue create --company-id <company-id> --title "Investigate checkout conflict"
pnpm paperclip issue update <issue-id> --status in_progress --comment "Started triage"
pnpm paperclipai issue list --company-id <company-id>
pnpm paperclipai issue create --company-id <company-id> --title "Investigate checkout conflict"
pnpm paperclipai issue update <issue-id> --status in_progress --comment "Started triage"
```
Set defaults once with context profiles:
```sh
pnpm paperclip context set --api-base http://localhost:3100 --company-id <company-id>
pnpm paperclipai context set --api-base http://localhost:3100 --company-id <company-id>
```
Then run commands without repeating flags:
```sh
pnpm paperclip issue list
pnpm paperclip dashboard get
pnpm paperclipai issue list
pnpm paperclipai dashboard get
```
See full command reference in `doc/CLI.md`.

View File

@@ -455,11 +455,11 @@ Files:
Commands:
1. `paperclip auth bootstrap-ceo`
1. `paperclipai auth bootstrap-ceo`
- create bootstrap invite
- print one-time URL
2. `paperclip onboard`
2. `paperclipai onboard`
- in cloud mode with `bootstrap_pending`, print bootstrap URL and next steps
- in local mode, skip bootstrap requirement

View File

@@ -17,7 +17,7 @@ Current V1 assumptions are centered on one board operator. We now need:
- multi-human collaboration with per-user permissions
- safe cloud deployment defaults (no accidental loginless production)
- local mode that still feels instant (`npx paperclip run` and go)
- local mode that still feels instant (`npx paperclipai run` and go)
- agent-to-human task delegation, including a human inbox
- one user account with access to multiple companies in one deployment
- instance admins who can manage company access across the instance
@@ -106,9 +106,9 @@ Problem:
Bootstrap flow:
1. If no `instance_admin` user exists for the deployment, instance is in `bootstrap_pending` state.
2. CLI command `pnpm paperclip auth bootstrap-ceo` creates a one-time CEO onboarding invite URL for that instance.
3. `pnpm paperclip onboard` runs this bootstrap check and prints the invite URL automatically when `bootstrap_pending`.
4. Visiting the app while `bootstrap_pending` shows a blocking setup page with the exact CLI command to run (`pnpm paperclip onboard`).
2. CLI command `pnpm paperclipai auth bootstrap-ceo` creates a one-time CEO onboarding invite URL for that instance.
3. `pnpm paperclipai onboard` runs this bootstrap check and prints the invite URL automatically when `bootstrap_pending`.
4. Visiting the app while `bootstrap_pending` shows a blocking setup page with the exact CLI command to run (`pnpm paperclipai onboard`).
5. Accepting that CEO invite creates the first admin user and exits bootstrap mode.
Security rules:
@@ -323,7 +323,7 @@ This plan introduces instance-level concerns (for example bootstrap state, insta
V1 approach:
- add a minimal `Instance Settings` page for instance admins
- expose key instance settings in API + CLI (`paperclip configure` / `paperclip onboard`)
- expose key instance settings in API + CLI (`paperclipai configure` / `paperclipai onboard`)
- show read-only instance status indicators in the main UI until full settings UX exists
## Implementation phases
@@ -385,7 +385,7 @@ V1 approach:
4. `cloud_hosted` cannot start without auth configured.
5. No request in `cloud_hosted` can mutate data without authenticated actor.
6. If no initial admin exists, app shows bootstrap instructions with CLI command.
7. `pnpm paperclip onboard` outputs a CEO onboarding invite URL when bootstrap is pending.
7. `pnpm paperclipai onboard` outputs a CEO onboarding invite URL when bootstrap is pending.
8. One `company_join` link supports both human and agent onboarding via join-type selection on the invite landing page.
9. Invite delivery in V1 is copy-link only (no built-in email delivery).
10. Share-link acceptance creates a pending join request; it does not grant immediate access.

View File

@@ -203,7 +203,7 @@ On approval, the approver sets:
| **P1** | Invite link + onboarding endpoint | `POST /api/companies/:id/invites`, `GET /api/invite/:token`, `POST /api/invite/:token/register`. |
| **P1** | Approval flow | UI + API for reviewing and approving pending agent registrations. |
| **P2** | OpenClaw integration | First real external agent onboarding via invite link. |
| **P3** | CLI auth flow | `paperclip auth login` for developer-managed remote agents. |
| **P3** | CLI auth flow | `paperclipai auth login` for developer-managed remote agents. |
## P0 Implementation Plan

View File

@@ -76,7 +76,7 @@ This is one authenticated mode with two safety policies, not two different auth
Default command remains:
```sh
pnpm paperclip onboard
pnpm paperclipai onboard
```
Interactive server step:
@@ -97,7 +97,7 @@ Flags are optional power-user overrides, not required for normal setup.
Default command remains interactive:
```sh
pnpm paperclip configure --section server
pnpm paperclipai configure --section server
```
Same mode/exposure questions and defaults as onboarding.
@@ -107,7 +107,7 @@ Same mode/exposure questions and defaults as onboarding.
Default command remains flagless:
```sh
pnpm paperclip doctor
pnpm paperclipai doctor
```
Doctor reads configured mode/exposure and applies relevant checks.
@@ -209,9 +209,9 @@ This change is a clean cut:
## Acceptance Criteria
1. `pnpm paperclip onboard` is interactive-first and defaults to `local_trusted`.
1. `pnpm paperclipai onboard` is interactive-first and defaults to `local_trusted`.
2. authenticated mode is one runtime mode with `private/public` exposure guidance.
3. `pnpm paperclip doctor` works flagless with mode-aware checks.
3. `pnpm paperclipai doctor` works flagless with mode-aware checks.
4. no extra compatibility aliases for dropped naming variants.
5. Board identity is represented by real DB user/role/membership integration points, enabling consistent task assignment and permission behavior.

View File

@@ -104,7 +104,7 @@ Modules live in a top-level `modules/` directory. Each module is a pnpm workspac
Key fields:
- **`id`**: Unique identifier, used as the npm package name suffix (`@paperclip/mod-observability`)
- **`id`**: Unique identifier, used as the npm package name suffix (`@paperclipai/mod-observability`)
- **`slot`**: Optional exclusive category. If set, only one module with this slot can be active. Omit for modules that can coexist freely.
- **`hooks`**: Which core events this module subscribes to. Declared upfront so the core knows what to emit.
- **`routes.prefix`**: Mounted under `/api/modules/<prefix>`. The module owns this namespace.
@@ -118,7 +118,7 @@ Key fields:
The module's `src/index.ts` exports a `register` function that receives the module API:
```typescript
import type { ModuleAPI } from "@paperclip/core";
import type { ModuleAPI } from "@paperclipai/core";
import { createRouter } from "./routes.js";
import { onHeartbeat, onBudgetThreshold } from "./hooks.js";
@@ -236,7 +236,7 @@ This keeps the core fast and resilient. If you need pre-commit validation (e.g.,
```typescript
// modules/observability/src/hooks.ts
import type { Db } from "@paperclip/db";
import type { Db } from "@paperclipai/db";
import { tokenMetrics } from "./schema.js";
export function createHeartbeatHandler(db: Db) {
@@ -382,7 +382,7 @@ export const modulePages = [
{
path: "/observability",
label: "Observability",
component: lazy(() => import("@paperclip/mod-observability/ui")),
component: lazy(() => import("@paperclipai/mod-observability/ui")),
},
];
@@ -391,7 +391,7 @@ export const dashboardWidgets = [
id: "token-burn-rate",
label: "Token Burn Rate",
placement: "dashboard",
component: lazy(() => import("@paperclip/mod-observability/ui").then(m => ({ default: m.TokenBurnRateWidget }))),
component: lazy(() => import("@paperclipai/mod-observability/ui").then(m => ({ default: m.TokenBurnRateWidget }))),
},
];
```
@@ -587,10 +587,10 @@ The Company Store is a registry for discovering and installing modules and templ
### CLI Commands
```bash
pnpm paperclip store list # browse available modules and templates
pnpm paperclip store install <module-id> # install a module
pnpm paperclip store import <template-id> # import a company template
pnpm paperclip store export # export current company as template
pnpm paperclipai store list # browse available modules and templates
pnpm paperclipai store install <module-id> # install a module
pnpm paperclipai store import <template-id> # import a company template
pnpm paperclipai store export # export current company as template
```
---
@@ -627,7 +627,7 @@ pnpm paperclip store export # export current company as templat
### Phase 1: Core infrastructure
Add to `@paperclip/server`:
Add to `@paperclipai/server`:
1. **HookBus** — Event emitter with `register()` and `emit()`, using `Promise.allSettled`
2. **Module loader** — Scans `modules/`, validates manifests, calls `register(api)`
@@ -636,7 +636,7 @@ Add to `@paperclip/server`:
5. **Module migration runner** — Extends `db:migrate` to discover and run module migrations
6. **Emit hooks from core services** — Add `hookBus.emit()` calls to existing CRUD operations
Add to `@paperclip/ui`:
Add to `@paperclipai/ui`:
7. **Module page loader** — Reads module manifests, generates lazy routes
8. **Dashboard widget slots** — Render module-contributed widgets on the Dashboard page
@@ -644,7 +644,7 @@ Add to `@paperclip/ui`:
Add new package:
10. **`@paperclip/module-sdk`** — TypeScript types for `ModuleAPI`, `HookEvent`, `HookHandler`, manifest schema
10. **`@paperclipai/module-sdk`** — TypeScript types for `ModuleAPI`, `HookEvent`, `HookHandler`, manifest schema
### Phase 2: First module (observability)

View File

@@ -56,8 +56,8 @@ Add a single storage subsystem for Paperclip that supports:
### Acceptance Criteria
- `paperclip onboard` writes a valid `storage` config block by default.
- `paperclip configure --section storage` can switch between local and s3 modes.
- `paperclipai onboard` writes a valid `storage` config block by default.
- `paperclipai configure --section storage` can switch between local and s3 modes.
- Server startup reads storage config without env-only hacks.
## Phase 2: Server Storage Subsystem + Providers
@@ -161,7 +161,7 @@ Add a single storage subsystem for Paperclip that supports:
### Acceptance Criteria
- `paperclip doctor` reports actionable storage status.
- `paperclipai doctor` reports actionable storage status.
- Local single-user install works without extra cloud credentials.
- Cloud config supports S3-compatible endpoint without code changes.

View File

@@ -76,7 +76,7 @@ Return structured diagnostics:
## Step 5: CLI Module
`format-event.ts` — pretty-prints stdout for `paperclip run --watch` using `picocolors`.
`format-event.ts` — pretty-prints stdout for `paperclipai run --watch` using `picocolors`.
## Step 6: Register

View File

@@ -39,7 +39,7 @@ packages/adapters/<name>/
parse-stdout.ts # Stdout -> transcript entries for run viewer
build-config.ts # Form values -> adapterConfig JSON
cli/
format-event.ts # Terminal output for `paperclip run --watch`
format-event.ts # Terminal output for `paperclipai run --watch`
```
Three registries consume these modules:

View File

@@ -9,38 +9,38 @@ Client-side commands for managing issues, agents, approvals, and more.
```sh
# List issues
pnpm paperclip issue list [--status todo,in_progress] [--assignee-agent-id <id>] [--match text]
pnpm paperclipai issue list [--status todo,in_progress] [--assignee-agent-id <id>] [--match text]
# Get issue details
pnpm paperclip issue get <issue-id-or-identifier>
pnpm paperclipai issue get <issue-id-or-identifier>
# Create issue
pnpm paperclip issue create --title "..." [--description "..."] [--status todo] [--priority high]
pnpm paperclipai issue create --title "..." [--description "..."] [--status todo] [--priority high]
# Update issue
pnpm paperclip issue update <issue-id> [--status in_progress] [--comment "..."]
pnpm paperclipai issue update <issue-id> [--status in_progress] [--comment "..."]
# Add comment
pnpm paperclip issue comment <issue-id> --body "..." [--reopen]
pnpm paperclipai issue comment <issue-id> --body "..." [--reopen]
# Checkout task
pnpm paperclip issue checkout <issue-id> --agent-id <agent-id>
pnpm paperclipai issue checkout <issue-id> --agent-id <agent-id>
# Release task
pnpm paperclip issue release <issue-id>
pnpm paperclipai issue release <issue-id>
```
## Company Commands
```sh
pnpm paperclip company list
pnpm paperclip company get <company-id>
pnpm paperclipai company list
pnpm paperclipai company get <company-id>
# Export to portable folder package (writes manifest + markdown files)
pnpm paperclip company export <company-id> --out ./exports/acme --include company,agents
pnpm paperclipai company export <company-id> --out ./exports/acme --include company,agents
# Preview import (no writes)
pnpm paperclip company import \
pnpm paperclipai company import \
--from https://github.com/<owner>/<repo>/tree/main/<path> \
--target existing \
--company-id <company-id> \
@@ -48,7 +48,7 @@ pnpm paperclip company import \
--dry-run
# Apply import
pnpm paperclip company import \
pnpm paperclipai company import \
--from ./exports/acme \
--target new \
--new-company-name "Acme Imported" \
@@ -58,52 +58,52 @@ pnpm paperclip company import \
## Agent Commands
```sh
pnpm paperclip agent list
pnpm paperclip agent get <agent-id>
pnpm paperclipai agent list
pnpm paperclipai agent get <agent-id>
```
## Approval Commands
```sh
# List approvals
pnpm paperclip approval list [--status pending]
pnpm paperclipai approval list [--status pending]
# Get approval
pnpm paperclip approval get <approval-id>
pnpm paperclipai approval get <approval-id>
# Create approval
pnpm paperclip approval create --type hire_agent --payload '{"name":"..."}' [--issue-ids <id1,id2>]
pnpm paperclipai approval create --type hire_agent --payload '{"name":"..."}' [--issue-ids <id1,id2>]
# Approve
pnpm paperclip approval approve <approval-id> [--decision-note "..."]
pnpm paperclipai approval approve <approval-id> [--decision-note "..."]
# Reject
pnpm paperclip approval reject <approval-id> [--decision-note "..."]
pnpm paperclipai approval reject <approval-id> [--decision-note "..."]
# Request revision
pnpm paperclip approval request-revision <approval-id> [--decision-note "..."]
pnpm paperclipai approval request-revision <approval-id> [--decision-note "..."]
# Resubmit
pnpm paperclip approval resubmit <approval-id> [--payload '{"..."}']
pnpm paperclipai approval resubmit <approval-id> [--payload '{"..."}']
# Comment
pnpm paperclip approval comment <approval-id> --body "..."
pnpm paperclipai approval comment <approval-id> --body "..."
```
## Activity Commands
```sh
pnpm paperclip activity list [--agent-id <id>] [--entity-type issue] [--entity-id <id>]
pnpm paperclipai activity list [--agent-id <id>] [--entity-type issue] [--entity-id <id>]
```
## Dashboard
```sh
pnpm paperclip dashboard get
pnpm paperclipai dashboard get
```
## Heartbeat
```sh
pnpm paperclip heartbeat run --agent-id <agent-id> [--api-base http://localhost:3100]
pnpm paperclipai heartbeat run --agent-id <agent-id> [--api-base http://localhost:3100]
```

View File

@@ -8,7 +8,7 @@ The Paperclip CLI handles instance setup, diagnostics, and control-plane operati
## Usage
```sh
pnpm paperclip --help
pnpm paperclipai --help
```
## Global Options
@@ -29,7 +29,7 @@ Company-scoped commands also accept `--company-id <id>`.
For clean local instances, pass `--data-dir` on the command you run:
```sh
pnpm paperclip run --data-dir ./tmp/paperclip-dev
pnpm paperclipai run --data-dir ./tmp/paperclip-dev
```
## Context Profiles
@@ -38,22 +38,22 @@ Store defaults to avoid repeating flags:
```sh
# Set defaults
pnpm paperclip context set --api-base http://localhost:3100 --company-id <id>
pnpm paperclipai context set --api-base http://localhost:3100 --company-id <id>
# View current context
pnpm paperclip context show
pnpm paperclipai context show
# List profiles
pnpm paperclip context list
pnpm paperclipai context list
# Switch profile
pnpm paperclip context use default
pnpm paperclipai context use default
```
To avoid storing secrets in context, use an env var:
```sh
pnpm paperclip context set --api-key-env-var-name PAPERCLIP_API_KEY
pnpm paperclipai context set --api-key-env-var-name PAPERCLIP_API_KEY
export PAPERCLIP_API_KEY=...
```

View File

@@ -5,32 +5,32 @@ summary: Onboard, run, doctor, and configure
Instance setup and diagnostics commands.
## `paperclip run`
## `paperclipai run`
One-command bootstrap and start:
```sh
pnpm paperclip run
pnpm paperclipai run
```
Does:
1. Auto-onboards if config is missing
2. Runs `paperclip doctor` with repair enabled
2. Runs `paperclipai doctor` with repair enabled
3. Starts the server when checks pass
Choose a specific instance:
```sh
pnpm paperclip run --instance dev
pnpm paperclipai run --instance dev
```
## `paperclip onboard`
## `paperclipai onboard`
Interactive first-time setup:
```sh
pnpm paperclip onboard
pnpm paperclipai onboard
```
Prompts for:
@@ -40,13 +40,13 @@ Prompts for:
3. Public URL (if authenticated + public)
4. Database and secrets configuration
## `paperclip doctor`
## `paperclipai doctor`
Health checks with optional auto-repair:
```sh
pnpm paperclip doctor
pnpm paperclip doctor --repair
pnpm paperclipai doctor
pnpm paperclipai doctor --repair
```
Validates:
@@ -57,30 +57,30 @@ Validates:
- Storage configuration
- Missing key files
## `paperclip configure`
## `paperclipai configure`
Update configuration sections:
```sh
pnpm paperclip configure --section server
pnpm paperclip configure --section secrets
pnpm paperclip configure --section storage
pnpm paperclipai configure --section server
pnpm paperclipai configure --section secrets
pnpm paperclipai configure --section storage
```
## `paperclip env`
## `paperclipai env`
Show resolved environment configuration:
```sh
pnpm paperclip env
pnpm paperclipai env
```
## `paperclip allowed-hostname`
## `paperclipai allowed-hostname`
Allow a private hostname for authenticated/private mode:
```sh
pnpm paperclip allowed-hostname my-tailscale-host
pnpm paperclipai allowed-hostname my-tailscale-host
```
## Local Storage Paths
@@ -96,12 +96,12 @@ pnpm paperclip allowed-hostname my-tailscale-host
Override with:
```sh
PAPERCLIP_HOME=/custom/home PAPERCLIP_INSTANCE_ID=dev pnpm paperclip run
PAPERCLIP_HOME=/custom/home PAPERCLIP_INSTANCE_ID=dev pnpm paperclipai run
```
Or pass `--data-dir` directly on any command:
```sh
pnpm paperclip run --data-dir ./tmp/paperclip-dev
pnpm paperclip doctor --data-dir ./tmp/paperclip-dev
pnpm paperclipai run --data-dir ./tmp/paperclip-dev
pnpm paperclipai doctor --data-dir ./tmp/paperclip-dev
```

View File

@@ -16,7 +16,7 @@ The default mode. Optimized for single-operator local use.
```sh
# Set during onboard
pnpm paperclip onboard
pnpm paperclipai onboard
# Choose "local_trusted"
```
@@ -33,14 +33,14 @@ For private network access (Tailscale, VPN, LAN).
- **Host trust**: private-host trust policy required
```sh
pnpm paperclip onboard
pnpm paperclipai onboard
# Choose "authenticated" -> "private"
```
Allow custom Tailscale hostnames:
```sh
pnpm paperclip allowed-hostname my-machine
pnpm paperclipai allowed-hostname my-machine
```
### `authenticated` + `public`
@@ -52,7 +52,7 @@ For internet-facing deployment.
- **Security**: stricter deployment checks in doctor
```sh
pnpm paperclip onboard
pnpm paperclipai onboard
# Choose "authenticated" -> "public"
```
@@ -75,11 +75,11 @@ A signed-in user visits this URL to claim board ownership. This:
Update the deployment mode:
```sh
pnpm paperclip configure --section server
pnpm paperclipai configure --section server
```
Runtime override via environment variable:
```sh
PAPERCLIP_DEPLOYMENT_MODE=authenticated pnpm paperclip run
PAPERCLIP_DEPLOYMENT_MODE=authenticated pnpm paperclipai run
```

View File

@@ -29,13 +29,13 @@ No Docker or external database required. Paperclip uses embedded PostgreSQL auto
For a first-time install:
```sh
pnpm paperclip run
pnpm paperclipai run
```
This does:
1. Auto-onboards if config is missing
2. Runs `paperclip doctor` with repair enabled
2. Runs `paperclipai doctor` with repair enabled
3. Starts the server when checks pass
## Tailscale/Private Auth Dev Mode
@@ -51,7 +51,7 @@ This binds the server to `0.0.0.0` for private-network access.
Allow additional private hostnames:
```sh
pnpm paperclip allowed-hostname dotta-macbook-pro
pnpm paperclipai allowed-hostname dotta-macbook-pro
```
## Health Checks
@@ -86,5 +86,5 @@ pnpm dev
Override with environment variables:
```sh
PAPERCLIP_HOME=/custom/path PAPERCLIP_INSTANCE_ID=dev pnpm paperclip run
PAPERCLIP_HOME=/custom/path PAPERCLIP_INSTANCE_ID=dev pnpm paperclipai run
```

View File

@@ -45,11 +45,11 @@ Paperclip supports three deployment configurations, from zero-friction local to
Set the mode during onboarding:
```sh
pnpm paperclip onboard
pnpm paperclipai onboard
```
Or update it later:
```sh
pnpm paperclip configure --section server
pnpm paperclipai configure --section server
```

View File

@@ -22,19 +22,19 @@ This key is auto-created during onboarding. The key never leaves your machine.
Onboarding writes default secrets config:
```sh
pnpm paperclip onboard
pnpm paperclipai onboard
```
Update secrets settings:
```sh
pnpm paperclip configure --section secrets
pnpm paperclipai configure --section secrets
```
Validate secrets config:
```sh
pnpm paperclip doctor
pnpm paperclipai doctor
```
### Environment Overrides

View File

@@ -22,7 +22,7 @@ For production or multi-node deployments, use S3-compatible object storage (AWS
Configure via CLI:
```sh
pnpm paperclip configure --section storage
pnpm paperclipai configure --section storage
```
## Configuration

View File

@@ -85,7 +85,7 @@ Adapters are the bridge between Paperclip and agent runtimes. Each adapter is a
- **Server module** — `execute()` function that spawns/calls the agent, plus environment diagnostics
- **UI module** — stdout parser for the run viewer, config form fields for agent creation
- **CLI module** — terminal formatter for `paperclip run --watch`
- **CLI module** — terminal formatter for `paperclipai run --watch`
Built-in adapters: `claude_local`, `codex_local`, `process`, `http`. You can create custom adapters for any runtime.

View File

@@ -38,7 +38,7 @@ No Docker or external database required — Paperclip uses an embedded PostgreSQ
## Option 3: One-Command Bootstrap
```sh
pnpm paperclip run
pnpm paperclipai run
```
This auto-onboards if config is missing, runs health checks with auto-repair, and starts the server.

View File

@@ -5,17 +5,17 @@
"scripts": {
"dev": "node scripts/dev-runner.mjs dev",
"dev:watch": "PAPERCLIP_MIGRATION_PROMPT=never node scripts/dev-runner.mjs watch",
"dev:server": "pnpm --filter @paperclip/server dev",
"dev:ui": "pnpm --filter @paperclip/ui dev",
"dev:server": "pnpm --filter @paperclipai/server dev",
"dev:ui": "pnpm --filter @paperclipai/ui dev",
"build": "pnpm -r build",
"typecheck": "pnpm -r typecheck",
"test": "vitest",
"test:run": "vitest run",
"db:generate": "pnpm --filter @paperclip/db generate",
"db:migrate": "pnpm --filter @paperclip/db migrate",
"db:generate": "pnpm --filter @paperclipai/db generate",
"db:migrate": "pnpm --filter @paperclipai/db migrate",
"secrets:migrate-inline-env": "tsx scripts/migrate-inline-env-secrets.ts",
"db:backup": "./scripts/backup-db.sh",
"paperclip": "node cli/node_modules/tsx/dist/cli.mjs cli/src/index.ts",
"paperclipai": "node cli/node_modules/tsx/dist/cli.mjs cli/src/index.ts",
"docs:dev": "cd docs && npx mintlify dev"
},
"devDependencies": {

View File

@@ -1,5 +1,5 @@
{
"name": "@paperclip/adapter-utils",
"name": "@paperclipai/adapter-utils",
"version": "0.0.1",
"private": true,
"type": "module",

View File

@@ -1,5 +1,5 @@
{
"name": "@paperclip/adapter-claude-local",
"name": "@paperclipai/adapter-claude-local",
"version": "0.0.1",
"private": true,
"type": "module",
@@ -13,7 +13,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@paperclip/adapter-utils": "workspace:*",
"@paperclipai/adapter-utils": "workspace:*",
"picocolors": "^1.1.1"
},
"devDependencies": {

View File

@@ -2,8 +2,8 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import type { AdapterExecutionContext, AdapterExecutionResult } from "@paperclip/adapter-utils";
import type { RunProcessResult } from "@paperclip/adapter-utils/server-utils";
import type { AdapterExecutionContext, AdapterExecutionResult } from "@paperclipai/adapter-utils";
import type { RunProcessResult } from "@paperclipai/adapter-utils/server-utils";
import {
asString,
asNumber,
@@ -18,7 +18,7 @@ import {
ensurePathInEnv,
renderTemplate,
runChildProcess,
} from "@paperclip/adapter-utils/server-utils";
} from "@paperclipai/adapter-utils/server-utils";
import {
parseClaudeStreamJson,
describeClaudeFailure,

View File

@@ -6,7 +6,7 @@ export {
isClaudeMaxTurnsResult,
isClaudeUnknownSessionError,
} from "./parse.js";
import type { AdapterSessionCodec } from "@paperclip/adapter-utils";
import type { AdapterSessionCodec } from "@paperclipai/adapter-utils";
function readNonEmptyString(value: unknown): string | null {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;

View File

@@ -1,5 +1,5 @@
import type { UsageSummary } from "@paperclip/adapter-utils";
import { asString, asNumber, parseObject, parseJson } from "@paperclip/adapter-utils/server-utils";
import type { UsageSummary } from "@paperclipai/adapter-utils";
import { asString, asNumber, parseObject, parseJson } from "@paperclipai/adapter-utils/server-utils";
const CLAUDE_AUTH_REQUIRED_RE = /(?:not\s+logged\s+in|please\s+log\s+in|please\s+run\s+`?claude\s+login`?|login\s+required|requires\s+login|unauthorized|authentication\s+required)/i;
const URL_RE = /(https?:\/\/[^\s'"`<>()[\]{};,!?]+[^\s'"`<>()[\]{};,!.?:]+)/gi;

View File

@@ -2,14 +2,14 @@ import type {
AdapterEnvironmentCheck,
AdapterEnvironmentTestContext,
AdapterEnvironmentTestResult,
} from "@paperclip/adapter-utils";
} from "@paperclipai/adapter-utils";
import {
asString,
parseObject,
ensureAbsoluteDirectory,
ensureCommandResolvable,
ensurePathInEnv,
} from "@paperclip/adapter-utils/server-utils";
} from "@paperclipai/adapter-utils/server-utils";
function summarizeStatus(checks: AdapterEnvironmentCheck[]): AdapterEnvironmentTestResult["status"] {
if (checks.some((check) => check.level === "error")) return "fail";

View File

@@ -1,4 +1,4 @@
import type { CreateConfigValues } from "@paperclip/adapter-utils";
import type { CreateConfigValues } from "@paperclipai/adapter-utils";
function parseCommaArgs(value: string): string[] {
return value

View File

@@ -1,4 +1,4 @@
import type { TranscriptEntry } from "@paperclip/adapter-utils";
import type { TranscriptEntry } from "@paperclipai/adapter-utils";
function asRecord(value: unknown): Record<string, unknown> | null {
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;

View File

@@ -1,5 +1,5 @@
{
"name": "@paperclip/adapter-codex-local",
"name": "@paperclipai/adapter-codex-local",
"version": "0.0.1",
"private": true,
"type": "module",
@@ -13,7 +13,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@paperclip/adapter-utils": "workspace:*",
"@paperclipai/adapter-utils": "workspace:*",
"picocolors": "^1.1.1"
},
"devDependencies": {

View File

@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import type { AdapterExecutionContext, AdapterExecutionResult } from "@paperclip/adapter-utils";
import type { AdapterExecutionContext, AdapterExecutionResult } from "@paperclipai/adapter-utils";
import {
asString,
asNumber,
@@ -16,7 +16,7 @@ import {
ensurePathInEnv,
renderTemplate,
runChildProcess,
} from "@paperclip/adapter-utils/server-utils";
} from "@paperclipai/adapter-utils/server-utils";
import { parseCodexJsonl, isCodexUnknownSessionError } from "./parse.js";
const PAPERCLIP_SKILLS_DIR = path.resolve(

View File

@@ -1,7 +1,7 @@
export { execute } from "./execute.js";
export { testEnvironment } from "./test.js";
export { parseCodexJsonl, isCodexUnknownSessionError } from "./parse.js";
import type { AdapterSessionCodec } from "@paperclip/adapter-utils";
import type { AdapterSessionCodec } from "@paperclipai/adapter-utils";
function readNonEmptyString(value: unknown): string | null {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;

View File

@@ -1,4 +1,4 @@
import { asString, asNumber, parseObject, parseJson } from "@paperclip/adapter-utils/server-utils";
import { asString, asNumber, parseObject, parseJson } from "@paperclipai/adapter-utils/server-utils";
export function parseCodexJsonl(stdout: string) {
let sessionId: string | null = null;

View File

@@ -2,14 +2,14 @@ import type {
AdapterEnvironmentCheck,
AdapterEnvironmentTestContext,
AdapterEnvironmentTestResult,
} from "@paperclip/adapter-utils";
} from "@paperclipai/adapter-utils";
import {
asString,
parseObject,
ensureAbsoluteDirectory,
ensureCommandResolvable,
ensurePathInEnv,
} from "@paperclip/adapter-utils/server-utils";
} from "@paperclipai/adapter-utils/server-utils";
function summarizeStatus(checks: AdapterEnvironmentCheck[]): AdapterEnvironmentTestResult["status"] {
if (checks.some((check) => check.level === "error")) return "fail";

View File

@@ -1,4 +1,4 @@
import type { CreateConfigValues } from "@paperclip/adapter-utils";
import type { CreateConfigValues } from "@paperclipai/adapter-utils";
function parseCommaArgs(value: string): string[] {
return value

View File

@@ -1,4 +1,4 @@
import type { TranscriptEntry } from "@paperclip/adapter-utils";
import type { TranscriptEntry } from "@paperclipai/adapter-utils";
function safeJsonParse(text: string): unknown {
try {

View File

@@ -1,5 +1,5 @@
{
"name": "@paperclip/adapter-openclaw",
"name": "@paperclipai/adapter-openclaw",
"version": "0.0.1",
"private": true,
"type": "module",
@@ -13,7 +13,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@paperclip/adapter-utils": "workspace:*",
"@paperclipai/adapter-utils": "workspace:*",
"picocolors": "^1.1.1"
},
"devDependencies": {

View File

@@ -1,5 +1,5 @@
import type { AdapterExecutionContext, AdapterExecutionResult } from "@paperclip/adapter-utils";
import { asNumber, asString, parseObject } from "@paperclip/adapter-utils/server-utils";
import type { AdapterExecutionContext, AdapterExecutionResult } from "@paperclipai/adapter-utils";
import { asNumber, asString, parseObject } from "@paperclipai/adapter-utils/server-utils";
import { parseOpenClawResponse } from "./parse.js";
function nonEmpty(value: unknown): string | null {

View File

@@ -2,8 +2,8 @@ import type {
AdapterEnvironmentCheck,
AdapterEnvironmentTestContext,
AdapterEnvironmentTestResult,
} from "@paperclip/adapter-utils";
import { asString, parseObject } from "@paperclip/adapter-utils/server-utils";
} from "@paperclipai/adapter-utils";
import { asString, parseObject } from "@paperclipai/adapter-utils/server-utils";
function summarizeStatus(checks: AdapterEnvironmentCheck[]): AdapterEnvironmentTestResult["status"] {
if (checks.some((check) => check.level === "error")) return "fail";
@@ -58,7 +58,7 @@ function pushDeploymentDiagnostics(
code: "openclaw_private_bind_hostname_not_allowed",
level: "warn",
message: `Paperclip bind host "${bindHost}" is not in allowed hostnames.`,
hint: `Run pnpm paperclip allowed-hostname ${bindHost} so remote OpenClaw callbacks can pass host checks.`,
hint: `Run pnpm paperclipai allowed-hostname ${bindHost} so remote OpenClaw callbacks can pass host checks.`,
});
}
@@ -76,7 +76,7 @@ function pushDeploymentDiagnostics(
code: "openclaw_private_no_allowed_hostnames",
level: "warn",
message: "No explicit allowed hostnames are configured for authenticated/private mode.",
hint: "Set one with pnpm paperclip allowed-hostname <host> when OpenClaw runs on another machine.",
hint: "Set one with pnpm paperclipai allowed-hostname <host> when OpenClaw runs on another machine.",
});
}
}

View File

@@ -1,4 +1,4 @@
import type { CreateConfigValues } from "@paperclip/adapter-utils";
import type { CreateConfigValues } from "@paperclipai/adapter-utils";
export function buildOpenClawConfig(v: CreateConfigValues): Record<string, unknown> {
const ac: Record<string, unknown> = {};

View File

@@ -1,4 +1,4 @@
import type { TranscriptEntry } from "@paperclip/adapter-utils";
import type { TranscriptEntry } from "@paperclipai/adapter-utils";
export function parseOpenClawStdoutLine(line: string, ts: string): TranscriptEntry[] {
return [{ kind: "stdout", ts, text: line }];

View File

@@ -1,5 +1,5 @@
{
"name": "@paperclip/db",
"name": "@paperclipai/db",
"version": "0.0.1",
"private": true,
"type": "module",
@@ -14,7 +14,7 @@
"seed": "tsx src/seed.ts"
},
"dependencies": {
"@paperclip/shared": "workspace:*",
"@paperclipai/shared": "workspace:*",
"drizzle-orm": "^0.38.4",
"postgres": "^3.4.5"
},

View File

@@ -1,5 +1,5 @@
{
"name": "@paperclip/shared",
"name": "@paperclipai/shared",
"version": "0.0.1",
"private": true,
"type": "module",

View File

@@ -46,7 +46,7 @@ const pnpmBin = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
const serverScript = mode === "watch" ? "dev:watch" : "dev";
const child = spawn(
pnpmBin,
["--filter", "@paperclip/server", serverScript, ...forwardedArgs],
["--filter", "@paperclipai/server", serverScript, ...forwardedArgs],
{ stdio: "inherit", env },
);

View File

@@ -1,5 +1,5 @@
import { eq } from "drizzle-orm";
import { agents, createDb } from "@paperclip/db";
import { agents, createDb } from "@paperclipai/db";
import { secretService } from "../server/src/services/secrets.js";
const SENSITIVE_ENV_KEY_RE =

View File

@@ -1,5 +1,5 @@
{
"name": "@paperclip/server",
"name": "@paperclipai/server",
"version": "0.0.1",
"private": true,
"type": "module",
@@ -11,12 +11,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@paperclip/adapter-claude-local": "workspace:*",
"@paperclip/adapter-codex-local": "workspace:*",
"@paperclip/adapter-openclaw": "workspace:*",
"@paperclip/adapter-utils": "workspace:*",
"@paperclip/db": "workspace:*",
"@paperclip/shared": "workspace:*",
"@paperclipai/adapter-claude-local": "workspace:*",
"@paperclipai/adapter-codex-local": "workspace:*",
"@paperclipai/adapter-openclaw": "workspace:*",
"@paperclipai/adapter-utils": "workspace:*",
"@paperclipai/db": "workspace:*",
"@paperclipai/shared": "workspace:*",
"@aws-sdk/client-s3": "^3.888.0",
"better-auth": "^1.3.8",
"detect-port": "^2.1.0",

View File

@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { models as codexFallbackModels } from "@paperclip/adapter-codex-local";
import { models as codexFallbackModels } from "@paperclipai/adapter-codex-local";
import { listAdapterModels } from "../adapters/index.js";
import { resetCodexModelsCacheForTests } from "../adapters/codex-models.js";

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { sessionCodec as claudeSessionCodec } from "@paperclip/adapter-claude-local/server";
import { sessionCodec as codexSessionCodec, isCodexUnknownSessionError } from "@paperclip/adapter-codex-local/server";
import { sessionCodec as claudeSessionCodec } from "@paperclipai/adapter-claude-local/server";
import { sessionCodec as codexSessionCodec, isCodexUnknownSessionError } from "@paperclipai/adapter-codex-local/server";
describe("adapter session codecs", () => {
it("normalizes claude session params with cwd", () => {

View File

@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it } from "vitest";
import { testEnvironment } from "@paperclip/adapter-claude-local/server";
import { testEnvironment } from "@paperclipai/adapter-claude-local/server";
const ORIGINAL_ANTHROPIC = process.env.ANTHROPIC_API_KEY;

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { isClaudeMaxTurnsResult } from "@paperclip/adapter-claude-local/server";
import { isClaudeMaxTurnsResult } from "@paperclipai/adapter-claude-local/server";
describe("claude_local max-turn detection", () => {
it("detects max-turn exhaustion by subtype", () => {

View File

@@ -1,7 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import { isCodexUnknownSessionError, parseCodexJsonl } from "@paperclip/adapter-codex-local/server";
import { parseCodexStdoutLine } from "@paperclip/adapter-codex-local/ui";
import { printCodexStreamEvent } from "@paperclip/adapter-codex-local/cli";
import { isCodexUnknownSessionError, parseCodexJsonl } from "@paperclipai/adapter-codex-local/server";
import { parseCodexStdoutLine } from "@paperclipai/adapter-codex-local/ui";
import { printCodexStreamEvent } from "@paperclipai/adapter-codex-local/cli";
describe("codex_local parser", () => {
it("extracts session, summary, usage, and terminal error message", () => {

View File

@@ -44,13 +44,13 @@ describe("privateHostnameGuard", () => {
const app = createApp({ enabled: true, allowedHostnames: ["some-other-host"] });
const res = await request(app).get("/api/health").set("Host", "dotta-macbook-pro:3100");
expect(res.status).toBe(403);
expect(res.body?.error).toContain("please run pnpm paperclip allowed-hostname dotta-macbook-pro");
expect(res.body?.error).toContain("please run pnpm paperclipai allowed-hostname dotta-macbook-pro");
});
it("blocks unknown hostnames on page routes with plain-text remediation command", async () => {
const app = createApp({ enabled: true, allowedHostnames: ["some-other-host"] });
const res = await request(app).get("/dashboard").set("Host", "dotta-macbook-pro:3100");
expect(res.status).toBe(403);
expect(res.text).toContain("please run pnpm paperclip allowed-hostname dotta-macbook-pro");
expect(res.text).toContain("please run pnpm paperclipai allowed-hostname dotta-macbook-pro");
});
});

View File

@@ -1,5 +1,5 @@
import type { AdapterModel } from "./types.js";
import { models as codexFallbackModels } from "@paperclip/adapter-codex-local";
import { models as codexFallbackModels } from "@paperclipai/adapter-codex-local";
import { readConfigFile } from "../config-file.js";
const OPENAI_MODELS_ENDPOINT = "https://api.openai.com/v1/models";

View File

@@ -13,5 +13,5 @@ export type {
UsageSummary,
AdapterAgent,
AdapterRuntime,
} from "@paperclip/adapter-utils";
} from "@paperclipai/adapter-utils";
export { runningProcesses } from "./utils.js";

View File

@@ -3,22 +3,22 @@ import {
execute as claudeExecute,
testEnvironment as claudeTestEnvironment,
sessionCodec as claudeSessionCodec,
} from "@paperclip/adapter-claude-local/server";
import { agentConfigurationDoc as claudeAgentConfigurationDoc, models as claudeModels } from "@paperclip/adapter-claude-local";
} from "@paperclipai/adapter-claude-local/server";
import { agentConfigurationDoc as claudeAgentConfigurationDoc, models as claudeModels } from "@paperclipai/adapter-claude-local";
import {
execute as codexExecute,
testEnvironment as codexTestEnvironment,
sessionCodec as codexSessionCodec,
} from "@paperclip/adapter-codex-local/server";
import { agentConfigurationDoc as codexAgentConfigurationDoc, models as codexModels } from "@paperclip/adapter-codex-local";
} from "@paperclipai/adapter-codex-local/server";
import { agentConfigurationDoc as codexAgentConfigurationDoc, models as codexModels } from "@paperclipai/adapter-codex-local";
import {
execute as openclawExecute,
testEnvironment as openclawTestEnvironment,
} from "@paperclip/adapter-openclaw/server";
} from "@paperclipai/adapter-openclaw/server";
import {
agentConfigurationDoc as openclawAgentConfigurationDoc,
models as openclawModels,
} from "@paperclip/adapter-openclaw";
} from "@paperclipai/adapter-openclaw";
import { listCodexModels } from "./codex-models.js";
import { processAdapter } from "./process/index.js";
import { httpAdapter } from "./http/index.js";

View File

@@ -16,4 +16,4 @@ export type {
AdapterSessionCodec,
AdapterModel,
ServerAdapterModule,
} from "@paperclip/adapter-utils";
} from "@paperclipai/adapter-utils";

View File

@@ -22,11 +22,11 @@ export {
ensurePathInEnv,
ensureAbsoluteDirectory,
ensureCommandResolvable,
} from "@paperclip/adapter-utils/server-utils";
} from "@paperclipai/adapter-utils/server-utils";
// Re-export runChildProcess with the server's pino logger wired in.
import { runChildProcess as _runChildProcess } from "@paperclip/adapter-utils/server-utils";
import type { RunProcessResult } from "@paperclip/adapter-utils/server-utils";
import { runChildProcess as _runChildProcess } from "@paperclipai/adapter-utils/server-utils";
import type { RunProcessResult } from "@paperclipai/adapter-utils/server-utils";
export async function runChildProcess(
runId: string,

View File

@@ -2,8 +2,8 @@ import express, { Router, type Request as ExpressRequest } from "express";
import path from "node:path";
import fs from "node:fs";
import { fileURLToPath } from "node:url";
import type { Db } from "@paperclip/db";
import type { DeploymentExposure, DeploymentMode } from "@paperclip/shared";
import type { Db } from "@paperclipai/db";
import type { DeploymentExposure, DeploymentMode } from "@paperclipai/shared";
import type { StorageService } from "./storage/types.js";
import { httpLogger, errorHandler } from "./middleware/index.js";
import { actorMiddleware } from "./middleware/auth.js";

View File

@@ -3,13 +3,13 @@ import type { IncomingHttpHeaders } from "node:http";
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { toNodeHandler } from "better-auth/node";
import type { Db } from "@paperclip/db";
import type { Db } from "@paperclipai/db";
import {
authAccounts,
authSessions,
authUsers,
authVerifications,
} from "@paperclip/db";
} from "@paperclipai/db";
import type { Config } from "../config.js";
export type BetterAuthSessionUser = {

View File

@@ -1,8 +1,8 @@
import { randomBytes } from "node:crypto";
import { and, eq } from "drizzle-orm";
import type { Db } from "@paperclip/db";
import { companies, companyMemberships, instanceUserRoles } from "@paperclip/db";
import type { DeploymentMode } from "@paperclip/shared";
import type { Db } from "@paperclipai/db";
import { companies, companyMemberships, instanceUserRoles } from "@paperclipai/db";
import type { DeploymentMode } from "@paperclipai/shared";
const LOCAL_BOARD_USER_ID = "local-board";
const CLAIM_TTL_MS = 1000 * 60 * 60 * 24;

View File

@@ -1,5 +1,5 @@
import fs from "node:fs";
import { paperclipConfigSchema, type PaperclipConfig } from "@paperclip/shared";
import { paperclipConfigSchema, type PaperclipConfig } from "@paperclipai/shared";
import { resolvePaperclipConfigPath } from "./paths.js";
export function readConfigFile(): PaperclipConfig | null {

View File

@@ -13,7 +13,7 @@ import {
type DeploymentMode,
type SecretProvider,
type StorageProvider,
} from "@paperclip/shared";
} from "@paperclipai/shared";
import {
resolveDefaultEmbeddedPostgresDir,
resolveDefaultSecretsKeyFilePath,

View File

@@ -15,7 +15,7 @@ import {
companies,
companyMemberships,
instanceUserRoles,
} from "@paperclip/db";
} from "@paperclipai/db";
import detectPort from "detect-port";
import { createApp } from "./app.js";
import { loadConfig } from "./config.js";

View File

@@ -1,10 +1,10 @@
import { createHash } from "node:crypto";
import type { Request, RequestHandler } from "express";
import { and, eq, isNull } from "drizzle-orm";
import type { Db } from "@paperclip/db";
import { agentApiKeys, agents, companyMemberships, instanceUserRoles } from "@paperclip/db";
import type { Db } from "@paperclipai/db";
import { agentApiKeys, agents, companyMemberships, instanceUserRoles } from "@paperclipai/db";
import { verifyLocalAgentJwt } from "../agent-auth-jwt.js";
import type { DeploymentMode } from "@paperclip/shared";
import type { DeploymentMode } from "@paperclipai/shared";
import type { BetterAuthSessionResult } from "../auth/better-auth.js";
import { logger } from "./logger.js";

View File

@@ -45,7 +45,7 @@ export function resolvePrivateHostnameAllowSet(opts: { allowedHostnames: string[
function blockedHostnameMessage(hostname: string): string {
return (
`Hostname '${hostname}' is not allowed for this Paperclip instance. ` +
`If you want to allow this hostname, please run pnpm paperclip allowed-hostname ${hostname}`
`If you want to allow this hostname, please run pnpm paperclipai allowed-hostname ${hostname}`
);
}
@@ -68,7 +68,7 @@ export function privateHostnameGuard(opts: {
const wantsJson = req.path.startsWith("/api") || req.accepts(["json", "html", "text"]) === "json";
if (!hostname) {
const error = "Missing Host header. If you want to allow a hostname, run pnpm paperclip allowed-hostname <host>.";
const error = "Missing Host header. If you want to allow a hostname, run pnpm paperclipai allowed-hostname <host>.";
if (wantsJson) {
res.status(403).json({ error });
} else {

View File

@@ -2,9 +2,9 @@ import { createHash } from "node:crypto";
import type { IncomingMessage, Server as HttpServer } from "node:http";
import type { Duplex } from "node:stream";
import { and, eq, isNull } from "drizzle-orm";
import type { Db } from "@paperclip/db";
import { agentApiKeys, companyMemberships, instanceUserRoles } from "@paperclip/db";
import type { DeploymentMode } from "@paperclip/shared";
import type { Db } from "@paperclipai/db";
import { agentApiKeys, companyMemberships, instanceUserRoles } from "@paperclipai/db";
import type { DeploymentMode } from "@paperclipai/shared";
import { WebSocket, WebSocketServer } from "ws";
import type { BetterAuthSessionResult } from "../auth/better-auth.js";
import { logger } from "../middleware/logger.js";

View File

@@ -5,13 +5,13 @@ import { fileURLToPath } from "node:url";
import { Router } from "express";
import type { Request } from "express";
import { and, eq, isNull, desc } from "drizzle-orm";
import type { Db } from "@paperclip/db";
import type { Db } from "@paperclipai/db";
import {
agentApiKeys,
authUsers,
invites,
joinRequests,
} from "@paperclip/db";
} from "@paperclipai/db";
import {
acceptInviteSchema,
claimJoinRequestApiKeySchema,
@@ -20,8 +20,8 @@ import {
updateMemberPermissionsSchema,
updateUserCompanyAccessSchema,
PERMISSION_KEYS,
} from "@paperclip/shared";
import type { DeploymentExposure, DeploymentMode } from "@paperclip/shared";
} from "@paperclipai/shared";
import type { DeploymentExposure, DeploymentMode } from "@paperclipai/shared";
import { forbidden, conflict, notFound, unauthorized, badRequest } from "../errors.js";
import { validate } from "../middleware/validate.js";
import { accessService, agentService, logActivity } from "../services/index.js";
@@ -155,7 +155,7 @@ function buildJoinConnectivityDiagnostics(input: {
code: "openclaw_private_bind_not_allowed",
level: "warn",
message: `Paperclip bind host \"${bindHost}\" is not in allowed hostnames.`,
hint: `Run pnpm paperclip allowed-hostname ${bindHost}`,
hint: `Run pnpm paperclipai allowed-hostname ${bindHost}`,
});
}
if (callbackHost && !isLoopbackHost(callbackHost) && allowSet.size === 0) {
@@ -163,7 +163,7 @@ function buildJoinConnectivityDiagnostics(input: {
code: "openclaw_private_allowed_hostnames_empty",
level: "warn",
message: "No explicit allowed hostnames are configured for authenticated/private mode.",
hint: "Set one with pnpm paperclip allowed-hostname <host> when OpenClaw runs off-host.",
hint: "Set one with pnpm paperclipai allowed-hostname <host> when OpenClaw runs off-host.",
});
}
}
@@ -355,7 +355,7 @@ function buildInviteOnboardingManifest(
allowedHostnames: opts.allowedHostnames,
guidance:
opts.deploymentMode === "authenticated" && opts.deploymentExposure === "private"
? "If OpenClaw runs on another machine, ensure the Paperclip hostname is reachable and allowed via `pnpm paperclip allowed-hostname <host>`."
? "If OpenClaw runs on another machine, ensure the Paperclip hostname is reachable and allowed via `pnpm paperclipai allowed-hostname <host>`."
: "Ensure OpenClaw can reach this Paperclip API base URL for callbacks and claims.",
},
skill: {

Some files were not shown because too many files have changed in this diff Show More