App Store Package and CLI
Purpose and Scope
The app-store tooling is the contributor-facing path for adding and maintaining Cal.diy integrations in the monorepo. The repository separates two closely related concerns: the app-store package that is consumed by the application, and the app-store CLI package that helps developers create, edit, delete, and regenerate integration scaffolding. This page explains those entrypoints at the package level, so a contributor can understand what to run, which command shape to use, and how the generation step turns app directories into usable source modules. It is especially relevant when implementing a new provider, refreshing app metadata, or checking why an integration is not appearing as expected.
Sources: packages/app-store/package.json, packages/app-store-cli/package.json, packages/app-store-cli/src/cli.tsx, packages/app-store-cli/src/build.ts
Cal.diy self-hosters usually encounter app integrations through setup documentation for providers such as Google Calendar, Stripe, Zoom, Daily, SendGrid, or Twilio. Those provider guides describe credentials, OAuth callbacks, webhooks, and environment variables, but they rely on the app-store package model underneath. When a provider exists as an app, its metadata and implementation need to be discoverable by the web application and by related app-store build outputs. The CLI therefore sits between contribution workflow and runtime availability: it helps create a consistent app directory, then the build script scans app-store directories and emits generated files that the application can import.
Sources: packages/app-store-cli/README.md, packages/app-store-cli/src/build.ts
Relevant Source Files
packages/app-store-cli/README.md— Short contributor-facing README that points developers to the app-building guide and records current CLI limitations and TODOs.packages/app-store-cli/package.json— Defines the private CLI package name, executable target, scripts, Node engine, and CLI dependencies such as Ink, meow, React, chokidar, and ts-node.packages/app-store-cli/src/cli.tsx— Implements the interactive and non-interactive command parser for creating, editing, and deleting apps or templates.packages/app-store-cli/src/build.ts— Implements app-store generation by scanning app directories, validating metadata, normalizing module paths, and formatting generated output.packages/app-store/package.json— Defines the main app-store package surface, type mapping, build and lint scripts, workspace dependencies, peer dependencies, and published file list.
Package Roles
The app-store package is the runtime-facing package surface. Its manifest identifies the package as private, declares the main entrypoint as an index TypeScript file, and exposes type version mappings that allow imports from the package and its type paths. It also lists workspace dependencies used by app integrations and the surrounding product surface, including shared Cal.diy libraries, internationalization, UI, features, and video integrations. Peer dependencies on React, React DOM, Stripe, and Zod are important because app implementations may render UI, validate data, or integrate payment behavior while still relying on the host application to provide compatible shared dependencies.
Sources: packages/app-store/package.json
The CLI package is the authoring and generation companion. Its package manifest names the package as a private workspace package, points the binary to a built JavaScript file, and provides scripts for building, launching the CLI from TypeScript, watching for changes, and generating app-store outputs. The dependencies reveal the developer experience choices: meow parses command-line input, Ink and React power terminal UI, and chokidar supports watch mode through the build script. The package is not presented as a general public SDK; it is a monorepo tool for contributors working inside the Cal.diy source tree.
Sources: packages/app-store-cli/package.json
CLI Command Reference
The CLI supports six command names: create, delete, edit, create-template, delete-template, and edit-template. The command parser requires exactly one positional command and displays help when the shape is wrong or the command is not supported. Delete and edit operations require a slug flag because they operate on an existing app or template directory. Create operations can run non-interactively when the required flags are present, which is useful for repeatable scaffolding in local workflows or scripted contribution examples. If the required non-interactive inputs are incomplete, the CLI falls back to the Ink application path rather than blindly creating partial app files.
Sources: packages/app-store-cli/src/cli.tsx
| Command | Purpose | Key inputs |
|---|---|---|
create | Create a new app integration from a template | --template, --name, --description, --category, optional publisher and email |
create-template | Create a reusable app template | Same create inputs, with template mode enabled |
edit | Edit an existing app identified by slug | --slug |
edit-template | Edit an existing template identified by slug | --slug |
delete | Delete an existing app identified by slug | --slug |
delete-template | Delete an existing template identified by slug | --slug |
The create flags are deliberately specific. Template selection is constrained to known template values, categories are constrained to valid app categories, and link-as-an-app templates require an external link URL. The code derives a slug from the supplied app name, supplies default publisher and email values when they are omitted, and passes all validated values into the app creation helper. After creation, the CLI calls the generation routine and prints a success summary that includes the slug, local app URL, name, description, and category. That output gives the contributor an immediate target for verifying the new app in a local Cal.diy web session.
Sources: packages/app-store-cli/src/cli.tsx
Example non-interactive create flow:
yarn workspace @calcom/app-store-cli cli create --template basic --name "Example App" --description "Example provider integration" --category calendar --publisher "Your Name" --email "email@example.com"Example edit and delete flows:
yarn workspace @calcom/app-store-cli cli edit --slug example-app
yarn workspace @calcom/app-store-cli cli delete --slug example-appBuild and Generation Flow
The build script is the source-to-code bridge for app-store metadata. It resolves the repository root, checks whether it is running in watch mode, and defines a generation process that scans the app-store directory. Normal app directories are inspected directly, while enterprise and template areas are traversed as nested directories. A directory is treated as an app candidate only when the helper that derives an app name accepts it, which prevents arbitrary folders from being pulled into generated outputs. This is a safeguard for a repository area that contains app implementations, templates, support files, and historical exceptions.
Sources: packages/app-store-cli/src/build.ts
For each discovered app directory, the generator prefers a config JSON file when present, parses it, and validates it through the AppMeta schema. If a config file is not present, it can load metadata from a TypeScript metadata file. When metadata parsing fails, the script raises an error that includes the affected config path and the parse or validation message. This failure mode is useful during app development because it moves invalid metadata errors to generation time, before a developer starts debugging missing integrations in the browser. The script also preserves the directory name and path alongside parsed metadata so later output phases can produce stable imports.
Sources: packages/app-store-cli/src/build.ts
The generator includes several practical compatibility details. It formats generated files by invoking the repository Biome formatter through Yarn, and it normalizes file paths to forward slashes before handing them to Biome and before constructing module imports. The module path helper removes TypeScript and TSX extensions and treats index files as directory imports, which keeps generated imports idiomatic for the Next.js and TypeScript codebase. There is also an explicit special case for Stripe, because the older stripe payment app has a directory name that differs from its app identifier. These details show that generation is not only scaffolding; it encodes conventions and migration compatibility.
Sources: packages/app-store-cli/src/build.ts
Developer Workflow
A typical app contribution starts with the provider task, not with generated code. First, decide whether the integration is a native app, a template, or an external link style app. Then run the CLI create command with a category, description, template, and name that match the intended provider. After the CLI creates the directory and regenerates app-store files, open the local app URL printed by the command and confirm that the app appears in the expected category. Provider-specific setup then continues in the relevant integration guide, where OAuth credentials, callbacks, API keys, and environment variables are configured for the self-hosted instance.
Sources: packages/app-store-cli/README.md, packages/app-store-cli/src/cli.tsx
When modifying an existing app, prefer the edit command when the change is metadata-oriented and the delete command only when intentionally removing a generated app or template. Always regenerate after changes that affect app discovery, metadata, schema exports, or importable components. The CLI package manifest exposes build, generate, and watch scripts, so contributors can run a one-shot generation or keep the generator active while editing. Watch mode is implemented by the build script rather than by the command parser, which keeps command-line app creation separate from repeated generation and formatting work.
Sources: packages/app-store-cli/package.json, packages/app-store-cli/src/build.ts
Known Constraints and Edge Cases
The CLI README records several open workflow limitations that contributors should treat as design constraints. It notes that app-store watch and app-store commands should eventually be merged into a single command with a watch option. It also calls out the desire to skip API validation for some CLI-created apps during local end-to-end testing, because a contributor may want to test app UI and flow before the matching API endpoint is available. Another TODO concerns manually created directory names that do not satisfy slug requirements, which matters because the generator depends on directory discovery and slug-like naming conventions.
Sources: packages/app-store-cli/README.md
These constraints explain why the safest workflow is to let the CLI create app directories rather than hand-writing the directory name first. The code derives the slug from the app name during non-interactive creation, validates template and category inputs, and requires a slug for edit or delete operations. That does not remove every possible edge case, especially when files are manually moved or metadata is edited outside the tool, but it gives contributors a predictable path. If generation fails, inspect the app config first, then the directory name, and finally whether the selected template requires an external link URL or other command-line input.
Sources: packages/app-store-cli/src/cli.tsx, packages/app-store-cli/src/build.ts
Next Steps
Use this page as the package-level map, then move to the provider-specific app documentation for credential setup. For example, calendar integrations need OAuth redirect URLs and calendar scopes, payment integrations need webhook and callback configuration, and communication integrations need provider API keys. If you are building a new app, start with the CLI create flow, keep generation running while you edit, and verify the result in a local Cal.diy instance before opening a contribution. If you are troubleshooting an existing integration, compare its app-store metadata and generated outputs before investigating provider credentials, because an unavailable app cannot complete its OAuth or webhook flow.
Sources: packages/app-store-cli/README.md, packages/app-store-cli/package.json, packages/app-store/package.json