/** * Solution Bundle Types * * A solution bundle is a team template (`config/templates/*.json` or * `/template.json`, same format as {@link TeamTemplate}) with an extra * `bundle` section that turns it into a complete, sellable setup: the team, * its norms/SOPs and review points, skills, connectors, Slack layout, * recurring schedules, deploy-time questions, first-week tasks and the * recommended runtime and server tier. The apply engine * (`services/bundle/bundle-apply.service.ts`) deploys it in one step. * * Templates without `bundle` are unaffected. See specs/solution-bundles.md. * * @module types/solution-bundle */ import type { TemplateRole } from './team-template.types.js'; /** Runtimes a bundle may recommend (equal to RUNTIME_TYPES values). */ export type BundleRuntimeId = 'claude-code' | 'gemini-cli' | 'codex-cli' | 'opencode-cli' | 'antigravity-cli' | 'crewly-agent'; /** Hosted server tier (entry 2 vCPU / 4 GB, standard 4 / 8, advanced 8 / 16). */ export type BundleServerTier = 'entry' | 'standard' | 'advanced'; /** `ready` bundles are offered to owners; `draft` ones need `allowDraft`. */ export type BundleStatus = 'ready' | 'draft'; /** Input control of a deploy-time question. */ export type BundleQuestionType = 'text' | 'textarea' | 'select' | 'multiselect'; /** One option of a select / multiselect question. */ export interface BundleQuestionOption { /** Stored value (what fills the placeholder) */ value: string; /** Owner-facing label (defaults to the value) */ label?: string; } /** * A question asked of the owner at deploy time. Its answer fills * `{{}}` placeholders in prompts, norms, SOPs, schedules and tasks. */ export interface BundleQuestion { /** Placeholder name: lower snake case, e.g. `business_name` */ id: string; /** Owner-facing question, e.g. 公司/品牌叫什么? */ label: string; /** One-line hint under the field */ help?: string; /** Input control */ type: BundleQuestionType; /** Required questions have no default; optional ones must declare one */ required: boolean; /** Choices for select / multiselect */ options?: BundleQuestionOption[]; /** Value used when an optional question is left empty */ default?: string | string[]; /** Example shown inside an empty field */ placeholder?: string; } /** Recommended runtime (model harness) for the bundle's agents. */ export interface BundleRuntime { /** The runtime this bundle is designed and priced for */ recommended: BundleRuntimeId; /** Other runtimes it works on */ compatible?: BundleRuntimeId[]; /** Model hint shown to the owner, e.g. `deepseek-chat` */ model?: string; /** Why this runtime (owner-facing, short) */ reason?: string; } /** Recommended hosted server. Absorbs the old `cloud-deploy.json` `deployConfig`. */ export interface BundleServer { /** Tier the bundle fits on */ tier: BundleServerTier; /** Minimum vCPUs */ minVcpu?: number; /** Minimum memory in GB */ minMemoryGb?: number; /** Needs a real browser (Chrome) on the server */ requiresBrowser?: boolean; /** Needs git on the server */ requiresGit?: boolean; } /** Usage estimate (for pricing the hosted monthly fee). */ export interface BundleUsage { /** Agent runs per day, all members together */ runsPerDay?: number; /** Average prompt tokens per run */ tokensPerRun?: number; /** Free-text note, e.g. how the estimate was measured */ note?: string; } /** A team norm (operating agreement) written to `teams//norms/.md`. */ export interface BundleNorm { /** File name without `.md` (kebab case) */ id: string; /** Title (frontmatter) */ title: string; /** Comma-separated triggers, e.g. `before_publish,content_review` */ trigger?: string; /** Markdown body (placeholders allowed) — or `file` */ content?: string; /** Path of the body, relative to the template directory */ file?: string; /** Team key (defaults to the main team) */ team?: string; } /** A team SOP written to `teams//sops//.md`. */ export interface BundleSop { /** File name without `.md` (kebab case) */ id: string; /** Title (frontmatter) */ title: string; /** Category folder (defaults to `team`) */ category?: string; /** Markdown body (placeholders allowed) — or `file` */ content?: string; /** Path of the body, relative to the template directory */ file?: string; /** Team key (defaults to the main team) */ team?: string; } /** Something the team must get the owner's (or the lead's) OK for. */ export interface BundleReviewPoint { /** Stable id */ id: string; /** What needs approval, e.g. 任何要对外发布的内容 */ what: string; /** Who approves */ approver: 'owner' | 'lead'; /** How to ask, e.g. 在 Slack 私信里发预览,老板回「可以」才发 */ how?: string; /** Team key (defaults to the main team) */ team?: string; } /** Skills the bundle needs installed. */ export interface BundleSkills { /** Installed at deploy (bundled ones are already there) */ required: string[]; /** Nice to have; failures are reported, not fatal */ optional?: string[]; } /** A service the owner connects (the /connections cards). */ export interface BundleConnector { /** Connector id, e.g. `google-workspace`, `canva`, `whatsapp` */ id: string; /** Google products for `google-workspace`: gmail, calendar, drive */ products?: string[]; /** Whether the team cannot do its core job without it */ required: boolean; /** Owner-facing reason, e.g. 用来读客户来信、起草回复 */ why: string; } /** An extra Slack channel (every team also gets its own team channel). */ export interface BundleSlackChannel { /** Stable key */ key: string; /** Channel name (placeholders allowed; made Slack-legal) */ name: string; /** Channel purpose */ purpose?: string; /** Member refs (`role`, `team/role`) or `*` for every member of every team */ members: string[]; } /** Slack layout. */ export interface BundleSlack { /** Give every team its own channel (default true) */ teamChannels?: boolean; /** Extra channels */ channels?: BundleSlackChannel[]; } /** A recurring task (created through the cron-task scheduler). */ export interface BundleSchedule { /** Stable id */ id: string; /** Owner-facing name, e.g. 每日简报 */ title: string; /** Five-field cron expression, e.g. `0 9 * * 1-5` */ cron: string; /** IANA timezone (placeholders allowed); defaults to the bundle timezone */ timezone?: string; /** Member ref that receives it (`role` or `team/role`); defaults to the main lead */ target?: string; /** What the agent is asked to do (placeholders allowed) */ task: string; } /** A task for the first week, so the owner sees output right away. */ export interface BundleFirstWeekTask { /** Stable id */ id: string; /** 0 = right after deploy, 1 = the next day, … up to 6 */ day: number; /** `HH:MM` for days ≥ 1 (bundle timezone); defaults to 09:00 */ time?: string; /** Member ref it is meant for; defaults to the main lead */ target?: string; /** Owner-facing name */ title: string; /** The task (placeholders allowed) */ task: string; } /** An extra team (the template's own `roles` form the main team). */ export interface BundleTeamSpec { /** Stable key used in refs (`key/role`) */ key: string; /** Team name (placeholders allowed) */ name: string; /** Team description (placeholders allowed) */ description?: string; /** Roles, same shape as a template's `roles` */ roles: TemplateRole[]; } /** * The `bundle` section of a team template. */ export interface SolutionBundle { /** Must equal BUNDLE_CONSTANTS.SCHEMA_VERSION */ schemaVersion: number; /** `ready` (default) or `draft` */ status?: BundleStatus; /** Short owner-facing name, e.g. 小老板营销团队 */ label: string; /** One-line pitch */ tagline: string; /** What the team does for the owner each day/week (owner-facing, a few lines) */ ownerSummary: string; /** What the owner has to do, e.g. 在手机上点头 */ ownerDoes?: string[]; /** Recommended runtime */ runtime: BundleRuntime; /** Recommended hosted server */ server: BundleServer; /** Usage estimate */ usage?: BundleUsage; /** Default timezone of schedules and first-week tasks (placeholders allowed) */ timezone?: string; /** Main team name (placeholders allowed); defaults to the template name */ teamName?: string; /** Extra teams */ teams?: BundleTeamSpec[]; /** Deploy-time questions */ questions?: BundleQuestion[]; /** Team norms */ norms?: BundleNorm[]; /** Team SOPs */ sops?: BundleSop[]; /** What needs the owner's OK */ reviewPoints?: BundleReviewPoint[]; /** Skills to install */ skills?: BundleSkills; /** Services to connect */ connectors?: BundleConnector[]; /** Slack layout */ slack?: BundleSlack; /** Recurring tasks */ schedules?: BundleSchedule[]; /** First-week tasks */ firstWeek?: BundleFirstWeekTask[]; /** Open items for the template's authors (drafts) */ todo?: string[]; } /** * The template fields the engine reads, plus `bundle`. Any template JSON * (flat file or `/template.json`) that carries a `bundle` section. */ export interface BundleTemplate { id: string; name: string; description: string; version?: string; category?: string; /** Minimum Cloud tier (free when omitted) */ requiredTier?: string; /** Crewly Pro license tier (`pro` / `enterprise`) */ tier?: string; hierarchical?: boolean; mission?: string; roles: TemplateRole[]; bundle: SolutionBundle; } /** A bundle template and the directory its relative files resolve against. */ export interface LoadedBundle { template: BundleTemplate; /** Directory of `template.json`, or the directory of a flat template file */ dir: string; /** Absolute path of the JSON file */ file: string; } /** State of one apply step. */ export type BundleStepStatus = 'queued' | 'running' | 'done' | 'failed' | 'pending' | 'skipped'; /** State of one item inside a step (a skill, a channel, a schedule…). */ export interface BundleStepItem { id: string; label: string; status: 'done' | 'failed' | 'pending' | 'skipped'; message?: string; } /** One apply step. */ export interface BundleStepState { id: string; /** Owner-facing label */ label: string; status: BundleStepStatus; /** Why the step is pending / skipped (a BUNDLE_CONSTANTS reason) */ reason?: string; /** Owner-facing summary */ message?: string; /** Error text when failed */ error?: string; startedAt?: string; finishedAt?: string; items?: BundleStepItem[]; } /** A team the deployment created. */ export interface BundleDeployedTeam { key: string; teamId: string; name: string; } /** A connector's state at the last check. */ export interface BundleConnectorState { id: string; products: string[]; required: boolean; why: string; status: 'connected' | 'not_connected' | 'unknown'; /** Relative link to connect it, e.g. `/connections?platform=canva` */ connectPath: string; } /** A first-week task's delivery state. */ export interface BundleFirstWeekState { id: string; title: string; day: number; /** When it is (or was) due */ dueAt: string; teamId: string; /** Session of the member it is meant for */ target: string; status: 'scheduled' | 'sent' | 'failed'; sentAt?: string; conversationId?: string | null; error?: string; } /** Overall deployment status. */ export type BundleDeploymentStatus = 'running' | 'done' | 'partial' | 'failed'; /** * What a deployment of one template on this machine looks like. Persisted at * `/bundles/.json` after every step, so a re-run * resumes where the last one stopped. */ export interface BundleDeployment { templateId: string; templateVersion: string; /** Last apply job */ jobId: string; status: BundleDeploymentStatus; runtime: string; /** Answers after defaults were applied */ answers: Record; startedAt: string; updatedAt: string; finishedAt?: string; teams: BundleDeployedTeam[]; steps: BundleStepState[]; connectors: BundleConnectorState[]; firstWeek: BundleFirstWeekState[]; /** Schedule id → cron task id */ schedules: Record; /** Channel key → Slack channel id */ channels: Record; } //# sourceMappingURL=solution-bundle.types.d.ts.map