@appmorph/shared

v0.1.6

Published

Shared types and constants for Appmorph

Readme

@appmorph/shared

Shared TypeScript types and constants for the Appmorph platform. This package is used by both @appmorph/core and @appmorph/sdk.

Installation

npm install @appmorph/shared
# or
pnpm add @appmorph/shared
# or
yarn add @appmorph/shared

Usage

import {
  // Types
  UserContext,
  AuthAdapter,
  Task,
  TaskStatus,
  AgentRunContext,
  AgentProgress,
  AgentResult,
  AppmorphConfig,

  // Staging & Deployment Types
  AppmorphProjectConfig,
  StageInfo,
  DeployInfo,
  BuildResult,

  // Persistence Types
  PersistedTaskEntry,

  // Constants
  BRANCH_PREFIX,
  API_ROUTES,
  SSE_EVENTS,
} from '@appmorph/shared';

Types

User & Auth

interface UserContext {
  userId: string;
  groupIds: string[];
  tenantId?: string;
  roles?: string[];
}

interface AuthAdapter {
  getUserContext(): Promise<UserContext>;
  getAuthToken(): Promise<string>;
}

Agent

interface AgentRunContext {
  prompt: string;
  repoPath: string;  // Points to staged copy during execution
  branch: string;
  instructions?: string;
  constraints: AgentConstraints;
}

interface AgentProgress {
  type: 'log' | 'file_change' | 'thinking';
  content: string;
  timestamp: number;
}

interface AgentResult {
  success: boolean;
  commitSha?: string;
  filesChanged: string[];
  summary: string;
  testResults?: TestResult[];
  stageInfo?: StageInfo;
  deployInfo?: DeployInfo;
}

interface AgentConstraints {
  allowedPaths?: string[];
  blockedPaths?: string[];
  allowedCommands?: string[];
  blockedCommands?: string[];
  maxFileSize?: number;
  maxTotalChanges?: number;
}

Staging & Deployment

interface AppmorphProjectConfig {
  source_type: 'file_system';
  source_location: string;
  build_command: string;  // Must contain <dist> placeholder
  deploy_type: 'file_system';
  deploy_root: string;
}

interface StageInfo {
  sessionId: string;
  stagePath: string;
}

interface DeployInfo {
  sessionId: string;
  deployPath: string;
  deployUrl: string;
}

interface BuildResult {
  success: boolean;
  output?: string;
  error?: string;
}

Task

type TaskStatus = 'pending' | 'running' | 'completed' | 'failed';

interface Task {
  id: string;
  prompt: string;
  status: TaskStatus;
  userId: string;
  groupId?: string;
  branch: string;
  createdAt: number;
  updatedAt: number;
  result?: AgentResult;
  error?: string;
}

API Contracts

// Create task
interface CreateTaskRequest {
  prompt: string;
  groupId?: string;
}

interface CreateTaskResponse {
  taskId: string;
  branch: string;
}

// Task status
interface TaskStatusResponse {
  task: Task;
}

// Version management
interface VersionMapping {
  groupId: string;
  commitSha: string;
  deployedAt: number;
  deployedBy: string;
  previewUrl?: string;
}

interface VersionsConfig {
  production: VersionMapping | null;
  groups: Record<string, VersionMapping>;
}

// Promote/Revert
interface PromoteRequest {
  groupId: string;
  commitSha: string;
  toProduction?: boolean;
}

interface RevertRequest {
  groupId?: string;
  toCommitSha?: string;
}

Configuration

interface AppmorphConfig {
  version: '1';
  repo: RepoConfig;
  agent: AgentConfig;
  constraints?: AgentConstraints;
  plugins?: PluginConfig[];
}

interface RepoConfig {
  type: 'git' | 'github' | 'gitlab';
  url: string;
  defaultBranch?: string;
}

interface AgentConfig {
  type: 'claude-cli';
  model?: string;
  maxTokens?: number;
  instructions?: string;
}

Plugin Hooks

interface TaskContext {
  task: Task;
  user: UserContext;
  config: AppmorphConfig;
}

interface StageContext {
  task: Task;
  user: UserContext;
  branch: string;
  commitSha: string;
}

interface PromoteContext {
  user: UserContext;
  groupId: string;
  commitSha: string;
  toProduction: boolean;
}

interface VetoResult {
  vetoed: true;
  reason: string;
}

interface AuthDecision {
  allowed: boolean;
  reason?: string;
}

SDK

interface AppmorphInitOptions {
  endpoint: string;
  auth: AuthAdapter;
  position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
  theme?: 'light' | 'dark' | 'auto';
  buttonLabel?: string;
  // Optional user ID. If not provided, a random UUID is generated and stored in cookie
  user_id?: string;
}

interface AppmorphSDK {
  init(options: AppmorphInitOptions): void;
  open(): void;
  close(): void;
  isOpen(): boolean;
  destroy(): void;
  submitPrompt(prompt: string): Promise<CreateTaskResponse>;
  getTaskStatus(taskId: string): Promise<TaskStatusResponse>;
}

Persistence

interface PersistedTaskEntry {
  source_base: string;        // Path to source code
  appmorph_user_id: string;   // User identifier from SDK cookie
  prompt: string;             // User's modification request
  session_id: string;         // Unique task/session ID
  created_at: number;         // Timestamp (ms since epoch)
  created_date: string;       // ISO 8601 formatted date string
}

Constants

Branch Prefixes

const BRANCH_PREFIX = {
  GROUP: 'appmorph/g/',
  USER: 'appmorph/u/',
} as const;

// Usage
const groupBranch = `${BRANCH_PREFIX.GROUP}team-a`;  // 'appmorph/g/team-a'
const userBranch = `${BRANCH_PREFIX.USER}user-123`; // 'appmorph/u/user-123'

API Routes

const API_ROUTES = {
  HEALTH: '/health',
  TASK: '/api/task',
  TASK_STATUS: '/api/task/:taskId',
  TASK_STREAM: '/api/task/:taskId/stream',
  VERSION: '/api/version',
  PROMOTE: '/api/promote',
  REVERT: '/api/revert',
} as const;

SSE Events

const SSE_EVENTS = {
  PROGRESS: 'progress',
  COMPLETE: 'complete',
  ERROR: 'error',
} as const;

Config Files

const DEFAULT_CONFIG_FILE = 'appmorph.json';
const VERSIONS_FILE = 'appmorph.versions.json';

Development

# Build
pnpm build

# Watch mode
pnpm dev

# Type check
pnpm typecheck