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:
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { getCLIAdapter } from "./registry.js";
|
||||
export type { CLIAdapterModule } from "@paperclip/adapter-utils";
|
||||
export type { CLIAdapterModule } from "@paperclipai/adapter-utils";
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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)",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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`",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Command } from "commander";
|
||||
import type { ActivityEvent } from "@paperclip/shared";
|
||||
import type { ActivityEvent } from "@paperclipai/shared";
|
||||
import {
|
||||
addCommonClientOptions,
|
||||
formatInlineRecord,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Command } from "commander";
|
||||
import type { Agent } from "@paperclip/shared";
|
||||
import type { Agent } from "@paperclipai/shared";
|
||||
import {
|
||||
addCommonClientOptions,
|
||||
formatInlineRecord,
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
resubmitApprovalSchema,
|
||||
type Approval,
|
||||
type ApprovalComment,
|
||||
} from "@paperclip/shared";
|
||||
} from "@paperclipai/shared";
|
||||
import {
|
||||
addCommonClientOptions,
|
||||
formatInlineRecord,
|
||||
|
||||
@@ -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`.",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
CompanyPortabilityManifest,
|
||||
CompanyPortabilityPreviewResult,
|
||||
CompanyPortabilityImportResult,
|
||||
} from "@paperclip/shared";
|
||||
} from "@paperclipai/shared";
|
||||
import { ApiRequestError } from "../../client/http.js";
|
||||
import {
|
||||
addCommonClientOptions,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Command } from "commander";
|
||||
import type { DashboardSummary } from "@paperclip/shared";
|
||||
import type { DashboardSummary } from "@paperclipai/shared";
|
||||
import {
|
||||
addCommonClientOptions,
|
||||
handleCommandError,
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
updateIssueSchema,
|
||||
type Issue,
|
||||
type IssueComment,
|
||||
} from "@paperclip/shared";
|
||||
} from "@paperclipai/shared";
|
||||
import {
|
||||
addCommonClientOptions,
|
||||
formatInlineRecord,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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`)})`,
|
||||
);
|
||||
|
||||
@@ -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[] = [];
|
||||
|
||||
@@ -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}`),
|
||||
"",
|
||||
];
|
||||
|
||||
@@ -23,4 +23,4 @@ export {
|
||||
type SecretsConfig,
|
||||
type SecretsLocalEncryptedConfig,
|
||||
type ConfigMeta,
|
||||
} from "@paperclip/shared";
|
||||
} from "@paperclipai/shared";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
80
doc/CLI.md
80
doc/CLI.md
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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=...
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
10
package.json
10
package.json
@@ -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": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@paperclip/adapter-utils",
|
||||
"name": "@paperclipai/adapter-utils",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { TranscriptEntry } from "@paperclip/adapter-utils";
|
||||
import type { TranscriptEntry } from "@paperclipai/adapter-utils";
|
||||
|
||||
function safeJsonParse(text: string): unknown {
|
||||
try {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> = {};
|
||||
|
||||
@@ -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 }];
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@paperclip/shared",
|
||||
"name": "@paperclipai/shared",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -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 },
|
||||
);
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -13,5 +13,5 @@ export type {
|
||||
UsageSummary,
|
||||
AdapterAgent,
|
||||
AdapterRuntime,
|
||||
} from "@paperclip/adapter-utils";
|
||||
} from "@paperclipai/adapter-utils";
|
||||
export { runningProcesses } from "./utils.js";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -16,4 +16,4 @@ export type {
|
||||
AdapterSessionCodec,
|
||||
AdapterModel,
|
||||
ServerAdapterModule,
|
||||
} from "@paperclip/adapter-utils";
|
||||
} from "@paperclipai/adapter-utils";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
type DeploymentMode,
|
||||
type SecretProvider,
|
||||
type StorageProvider,
|
||||
} from "@paperclip/shared";
|
||||
} from "@paperclipai/shared";
|
||||
import {
|
||||
resolveDefaultEmbeddedPostgresDir,
|
||||
resolveDefaultSecretsKeyFilePath,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user