@invect/version-control 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/dist/backend/flow-serializer.d.ts +38 -0
  3. package/dist/backend/flow-serializer.d.ts.map +1 -0
  4. package/dist/backend/git-provider.d.ts +77 -0
  5. package/dist/backend/git-provider.d.ts.map +1 -0
  6. package/dist/backend/github-provider.d.ts +14 -0
  7. package/dist/backend/github-provider.d.ts.map +1 -0
  8. package/dist/backend/index.cjs +1045 -0
  9. package/dist/backend/index.cjs.map +1 -0
  10. package/dist/backend/index.d.cts +51 -0
  11. package/dist/backend/index.d.cts.map +1 -0
  12. package/dist/backend/index.d.mts +51 -0
  13. package/dist/backend/index.d.mts.map +1 -0
  14. package/dist/backend/index.d.ts +5 -0
  15. package/dist/backend/index.d.ts.map +1 -0
  16. package/dist/backend/index.mjs +1043 -0
  17. package/dist/backend/index.mjs.map +1 -0
  18. package/dist/backend/plugin.d.ts +24 -0
  19. package/dist/backend/plugin.d.ts.map +1 -0
  20. package/dist/backend/schema.d.ts +3 -0
  21. package/dist/backend/schema.d.ts.map +1 -0
  22. package/dist/backend/sync-service.d.ts +47 -0
  23. package/dist/backend/sync-service.d.ts.map +1 -0
  24. package/dist/backend/types.d.ts +20 -0
  25. package/dist/backend/types.d.ts.map +1 -0
  26. package/dist/frontend/index.cjs +0 -0
  27. package/dist/frontend/index.d.cts +2 -0
  28. package/dist/frontend/index.d.mts +2 -0
  29. package/dist/frontend/index.d.ts +2 -0
  30. package/dist/frontend/index.d.ts.map +1 -0
  31. package/dist/frontend/index.mjs +1 -0
  32. package/dist/git-provider-BD8MMEXB.d.mts +80 -0
  33. package/dist/git-provider-BD8MMEXB.d.mts.map +1 -0
  34. package/dist/git-provider-CjMtpb86.d.cts +80 -0
  35. package/dist/git-provider-CjMtpb86.d.cts.map +1 -0
  36. package/dist/providers/github.cjs +191 -0
  37. package/dist/providers/github.cjs.map +1 -0
  38. package/dist/providers/github.d.cts +17 -0
  39. package/dist/providers/github.d.cts.map +1 -0
  40. package/dist/providers/github.d.mts +17 -0
  41. package/dist/providers/github.d.mts.map +1 -0
  42. package/dist/providers/github.d.ts +2 -0
  43. package/dist/providers/github.d.ts.map +1 -0
  44. package/dist/providers/github.mjs +190 -0
  45. package/dist/providers/github.mjs.map +1 -0
  46. package/dist/shared/types.cjs +0 -0
  47. package/dist/shared/types.d.cts +2 -0
  48. package/dist/shared/types.d.mts +2 -0
  49. package/dist/shared/types.d.ts +77 -0
  50. package/dist/shared/types.d.ts.map +1 -0
  51. package/dist/shared/types.mjs +1 -0
  52. package/dist/types-B32wGtx7.d.cts +80 -0
  53. package/dist/types-B32wGtx7.d.cts.map +1 -0
  54. package/dist/types-B7fFBAOX.d.mts +80 -0
  55. package/dist/types-B7fFBAOX.d.mts.map +1 -0
  56. package/package.json +53 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/backend/schema.ts","../../src/backend/flow-serializer.ts","../../src/backend/sync-service.ts","../../src/backend/plugin.ts"],"sourcesContent":["// =============================================================================\n// Version Control Plugin — Database Schema (abstract, dialect-agnostic)\n// =============================================================================\n\nimport type { InvectPluginSchema } from '@invect/core';\n\nconst SYNC_MODES = ['direct-commit', 'pr-per-save', 'pr-per-publish'] as const;\nconst SYNC_DIRECTIONS = ['push', 'pull', 'bidirectional'] as const;\nconst SYNC_ACTIONS = ['push', 'pull', 'pr-created', 'pr-merged', 'conflict'] as const;\n\nexport const VC_SCHEMA: InvectPluginSchema = {\n vcSyncConfig: {\n tableName: 'vc_sync_config',\n order: 10,\n fields: {\n id: { type: 'string', primaryKey: true },\n flowId: {\n type: 'string',\n required: true,\n unique: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n index: true,\n },\n provider: { type: 'string', required: true },\n repo: { type: 'string', required: true },\n branch: { type: 'string', required: true },\n filePath: { type: 'string', required: true },\n mode: { type: [...SYNC_MODES], required: true },\n syncDirection: { type: [...SYNC_DIRECTIONS], required: true, defaultValue: 'push' },\n lastSyncedAt: { type: 'date', required: false },\n lastCommitSha: { type: 'string', required: false },\n lastSyncedVersion: { type: 'number', required: false },\n draftBranch: { type: 'string', required: false },\n activePrNumber: { type: 'number', required: false },\n activePrUrl: { type: 'string', required: false },\n enabled: { type: 'boolean', required: true, defaultValue: true },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n vcSyncHistory: {\n tableName: 'vc_sync_history',\n order: 20,\n fields: {\n id: { type: 'uuid', primaryKey: true, defaultValue: 'uuid()' },\n flowId: {\n type: 'string',\n required: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n index: true,\n },\n action: { type: [...SYNC_ACTIONS], required: true },\n commitSha: { type: 'string', required: false },\n prNumber: { type: 'number', required: false },\n version: { type: 'number', required: false },\n message: { type: 'string', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n createdBy: { type: 'string', required: false },\n },\n },\n};\n","// =============================================================================\n// Flow Serializer — converts InvectDefinition JSON ↔ .flow.ts file content\n// =============================================================================\n\n/**\n * Serializes an InvectDefinition to a readable .flow.ts file.\n *\n * The output uses the Option A (declarative) format from the SDK plan:\n * defineFlow({ name, nodes: [...], edges: [...] })\n *\n * NOTE: This is a standalone serializer — it doesn't depend on the SDK being\n * implemented yet. It generates the .flow.ts text directly from the definition JSON.\n * When the SDK ships, this will import the actual helpers instead.\n */\nexport function serializeFlowToTs(\n definition: FlowDefinitionJson,\n metadata: { name: string; description?: string; tags?: string[] },\n): string {\n const lines: string[] = [];\n\n // Collect which helper functions are needed\n const helpers = new Set<string>();\n const providerImports = new Map<string, Set<string>>();\n\n for (const node of definition.nodes) {\n const { helperName, providerNs } = resolveHelper(node.type);\n if (providerNs) {\n if (!providerImports.has(providerNs)) {\n providerImports.set(providerNs, new Set());\n }\n providerImports.get(providerNs)?.add(helperName);\n } else {\n helpers.add(helperName);\n }\n }\n\n // Always need defineFlow\n helpers.add('defineFlow');\n\n // Build import line\n const coreHelpers = [...helpers].sort();\n lines.push(`import { ${coreHelpers.join(', ')} } from '@invect/core/sdk';`);\n\n for (const [ns, _methods] of providerImports) {\n lines.push(`import { ${ns} } from '@invect/core/sdk/providers';`);\n }\n\n lines.push('');\n lines.push('export default defineFlow({');\n\n // Metadata\n lines.push(` name: ${JSON.stringify(metadata.name)},`);\n if (metadata.description) {\n lines.push(` description: ${JSON.stringify(metadata.description)},`);\n }\n if (metadata.tags && metadata.tags.length > 0) {\n lines.push(` tags: ${JSON.stringify(metadata.tags)},`);\n }\n\n // Nodes\n lines.push('');\n lines.push(' nodes: [');\n for (const node of definition.nodes) {\n const ref = node.referenceId || node.id;\n const { helperCall } = resolveHelper(node.type);\n const params = serializeParams(node.params);\n lines.push(` ${helperCall}(${JSON.stringify(ref)}, ${params}),`);\n lines.push('');\n }\n lines.push(' ],');\n\n // Edges (tuple shorthand)\n lines.push('');\n lines.push(' edges: [');\n for (const edge of definition.edges) {\n const source = resolveNodeRef(edge.source, definition.nodes);\n const target = resolveNodeRef(edge.target, definition.nodes);\n if (edge.sourceHandle) {\n lines.push(\n ` [${JSON.stringify(source)}, ${JSON.stringify(target)}, ${JSON.stringify(edge.sourceHandle)}],`,\n );\n } else {\n lines.push(` [${JSON.stringify(source)}, ${JSON.stringify(target)}],`);\n }\n }\n lines.push(' ],');\n\n lines.push('});');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\ninterface FlowDefinitionJson {\n nodes: Array<{\n id: string;\n type: string;\n label?: string;\n referenceId?: string;\n position?: { x: number; y: number };\n params: Record<string, unknown>;\n mapper?: Record<string, unknown>;\n }>;\n edges: Array<{\n id: string;\n source: string;\n target: string;\n sourceHandle?: string;\n targetHandle?: string;\n }>;\n}\n\n/** Map action IDs to SDK helper function names */\nconst ACTION_TO_HELPER: Record<string, { helperName: string; providerNs?: string }> = {\n 'core.input': { helperName: 'input' },\n 'core.output': { helperName: 'output' },\n 'core.model': { helperName: 'model' },\n 'core.jq': { helperName: 'jq' },\n 'core.if_else': { helperName: 'ifElse' },\n 'core.template_string': { helperName: 'template' },\n 'core.javascript': { helperName: 'javascript' },\n 'core.loop': { helperName: 'loop' },\n 'http.request': { helperName: 'httpRequest' },\n AGENT: { helperName: 'agent' },\n};\n\nfunction resolveHelper(nodeType: string): {\n helperName: string;\n helperCall: string;\n providerNs?: string;\n} {\n const known = ACTION_TO_HELPER[nodeType];\n if (known) {\n return {\n helperName: known.helperName,\n helperCall: known.providerNs ? `${known.providerNs}.${known.helperName}` : known.helperName,\n providerNs: known.providerNs,\n };\n }\n\n // For provider actions like \"gmail.send_message\" → gmail.sendMessage\n const dotIdx = nodeType.indexOf('.');\n if (dotIdx > 0) {\n const ns = nodeType.substring(0, dotIdx);\n const action = nodeType.substring(dotIdx + 1);\n const camel = snakeToCamel(action);\n return {\n helperName: camel,\n helperCall: `${ns}.${camel}`,\n providerNs: ns,\n };\n }\n\n // Unknown type — use generic node() helper\n return { helperName: 'node', helperCall: 'node' };\n}\n\nfunction snakeToCamel(s: string): string {\n return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());\n}\n\n/** Resolve a node ID (e.g. \"node-classify\") back to its referenceId (\"classify\") */\nfunction resolveNodeRef(nodeId: string, nodes: FlowDefinitionJson['nodes']): string {\n const node = nodes.find((n) => n.id === nodeId);\n if (node?.referenceId) {\n return node.referenceId;\n }\n // Strip \"node-\" prefix if present\n if (nodeId.startsWith('node-')) {\n return nodeId.substring(5);\n }\n return nodeId;\n}\n\n/** Serialize params object to formatted string, filtering out credentials by ID */\nfunction serializeParams(params: Record<string, unknown>): string {\n const cleaned = { ...params };\n\n // Replace credential IDs with symbolic env references\n if (typeof cleaned.credentialId === 'string' && !cleaned.credentialId.startsWith('{{')) {\n cleaned.credentialId = `{{env.${toEnvName(cleaned.credentialId)}}}`;\n }\n\n return formatObject(cleaned, 4);\n}\n\nfunction toEnvName(credentialId: string): string {\n // \"cred_openai_123\" → \"OPENAI_CREDENTIAL\"\n // Strip common prefixes, uppercase, append CREDENTIAL\n const name = credentialId\n .replace(/^cred[_-]?/i, '')\n .replace(/[_-]?\\d+$/g, '')\n .toUpperCase();\n return name ? `${name}_CREDENTIAL` : 'CREDENTIAL';\n}\n\n/** Format a JS value as readable code (not JSON — no quoting keys where unnecessary) */\nfunction formatObject(value: unknown, indent: number): string {\n if (value === null || value === undefined) {\n return 'null';\n }\n if (typeof value === 'string') {\n return JSON.stringify(value);\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return '[]';\n }\n if (value.every((v) => typeof v === 'string' || typeof v === 'number')) {\n return `[${value.map((v) => JSON.stringify(v)).join(', ')}]`;\n }\n const items = value.map((v) => `${' '.repeat(indent + 2)}${formatObject(v, indent + 2)}`);\n return `[\\n${items.join(',\\n')}\\n${' '.repeat(indent)}]`;\n }\n\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const entries = Object.entries(obj).filter(([, v]) => v !== undefined);\n if (entries.length === 0) {\n return '{}';\n }\n\n const lines = entries.map(([key, val]) => {\n const k = isValidIdentifier(key) ? key : JSON.stringify(key);\n return `${' '.repeat(indent + 2)}${k}: ${formatObject(val, indent + 2)}`;\n });\n return `{\\n${lines.join(',\\n')}\\n${' '.repeat(indent)}}`;\n }\n\n return JSON.stringify(value);\n}\n\nfunction isValidIdentifier(s: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(s);\n}\n","// =============================================================================\n// Version Control Sync Service — orchestrates push/pull/publish operations\n// =============================================================================\n\nimport { randomUUID } from 'node:crypto';\n\nimport type { GitProvider } from './git-provider';\nimport type { PluginDatabaseApi } from '@invect/core';\nimport type { VersionControlPluginOptions } from './types';\nimport type {\n VcSyncConfig,\n VcSyncHistoryRecord,\n VcSyncResult,\n VcSyncStatus,\n ConfigureSyncInput,\n} from '../shared/types';\nimport { serializeFlowToTs } from './flow-serializer';\n\ninterface FlowRow {\n id: string;\n name: string;\n description: string | null;\n tags: string | null;\n}\n\ninterface FlowVersionRow {\n flowId: string;\n version: number;\n invectDefinition: string;\n}\n\ntype Logger = {\n debug(msg: string, meta?: Record<string, unknown>): void;\n info(msg: string, meta?: Record<string, unknown>): void;\n warn(msg: string, meta?: Record<string, unknown>): void;\n error(msg: string, meta?: Record<string, unknown>): void;\n};\n\nexport class VcSyncService {\n constructor(\n private provider: GitProvider,\n private options: VersionControlPluginOptions,\n private logger: Logger,\n ) {}\n\n // =========================================================================\n // Configuration\n // =========================================================================\n\n async configureSyncForFlow(\n db: PluginDatabaseApi,\n flowId: string,\n input: ConfigureSyncInput,\n ): Promise<VcSyncConfig> {\n // Check if flow exists\n const flows = await db.query<FlowRow>('SELECT id, name FROM flows WHERE id = ?', [flowId]);\n if (flows.length === 0) {\n throw new Error(`Flow not found: ${flowId}`);\n }\n\n const flow = flows[0];\n const id = randomUUID();\n const now = new Date().toISOString();\n\n const repo = input.repo ?? this.options.repo;\n const branch = input.branch ?? this.options.defaultBranch ?? 'main';\n const mode = input.mode ?? this.options.mode ?? 'direct-commit';\n const syncDirection = input.syncDirection ?? this.options.syncDirection ?? 'push';\n const filePath = input.filePath ?? this.buildFilePath(flow.name);\n\n // Upsert — delete existing config for this flow first\n await db.execute('DELETE FROM vc_sync_config WHERE flow_id = ?', [flowId]);\n\n await db.execute(\n `INSERT INTO vc_sync_config (id, flow_id, provider, repo, branch, file_path, mode, sync_direction, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [id, flowId, this.provider.id, repo, branch, filePath, mode, syncDirection, true, now, now],\n );\n\n return this.getSyncConfig(db, flowId) as Promise<VcSyncConfig>;\n }\n\n async getSyncConfig(db: PluginDatabaseApi, flowId: string): Promise<VcSyncConfig | null> {\n const rows = await db.query<VcSyncConfigRow>('SELECT * FROM vc_sync_config WHERE flow_id = ?', [\n flowId,\n ]);\n if (rows.length === 0) {\n return null;\n }\n return mapSyncConfigRow(rows[0]);\n }\n\n async disconnectFlow(db: PluginDatabaseApi, flowId: string): Promise<void> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n return;\n }\n\n // If there's an active PR, close it\n if (config.activePrNumber) {\n try {\n await this.provider.closePullRequest(\n config.repo,\n config.activePrNumber,\n 'Sync disconnected — flow unlinked from version control.',\n );\n } catch (err) {\n this.logger.warn('Failed to close PR on disconnect', { error: (err as Error).message });\n }\n }\n\n // If there's a draft branch, try to clean it up\n if (config.draftBranch) {\n try {\n await this.provider.deleteBranch(config.repo, config.draftBranch);\n } catch {\n // Ignore — branch may already be deleted\n }\n }\n\n await db.execute('DELETE FROM vc_sync_config WHERE flow_id = ?', [flowId]);\n }\n\n // =========================================================================\n // Push (DB → Remote)\n // =========================================================================\n\n async pushFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n const { content, version } = await this.exportFlow(db, flowId);\n\n try {\n if (config.mode === 'direct-commit') {\n return await this.directCommit(db, config, content, version, identity);\n } else if (config.mode === 'pr-per-save') {\n return await this.commitToPrBranch(db, config, content, version, identity, true);\n } else {\n // pr-per-publish: commit to draft branch, no PR yet\n return await this.commitToDraftBranch(db, config, content, version, identity);\n }\n } catch (err) {\n const message = (err as Error).message;\n\n // SHA mismatch = conflict\n if (message.includes('409') || message.includes('sha')) {\n await this.recordHistory(db, flowId, 'conflict', { version, message, createdBy: identity });\n return {\n success: false,\n error: 'Conflict: remote file has changed. Use force-push or force-pull.',\n action: 'conflict',\n };\n }\n\n throw err;\n }\n }\n\n async forcePushFlow(\n db: PluginDatabaseApi,\n flowId: string,\n identity?: string,\n ): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n const { content, version } = await this.exportFlow(db, flowId);\n\n // Get current remote SHA (if file exists) to force update\n const remote = await this.provider.getFileContent(config.repo, config.filePath, config.branch);\n const sha = remote?.sha;\n\n const result = await this.provider.createOrUpdateFile(\n config.repo,\n config.filePath,\n content,\n `chore(flow): force-push ${this.flowFileName(config.filePath)} v${version}`,\n { branch: config.branch, sha },\n );\n\n await this.updateConfigAfterSync(db, flowId, result.commitSha, version);\n await this.recordHistory(db, flowId, 'push', {\n commitSha: result.commitSha,\n version,\n message: 'Force push (local wins)',\n createdBy: identity,\n });\n\n return { success: true, commitSha: result.commitSha, action: 'push' };\n }\n\n // =========================================================================\n // Pull (Remote → DB)\n // =========================================================================\n\n async pullFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n const remote = await this.provider.getFileContent(config.repo, config.filePath, config.branch);\n\n if (!remote) {\n return { success: false, error: 'Remote file not found', action: 'pull' };\n }\n\n // Check if we're already in sync\n if (config.lastCommitSha && remote.sha === config.lastCommitSha) {\n return { success: true, action: 'pull' }; // Already up to date\n }\n\n await this.importFlowContent(db, flowId, remote.content, identity);\n await this.updateConfigAfterSync(db, flowId, remote.sha, null);\n await this.recordHistory(db, flowId, 'pull', {\n commitSha: remote.sha,\n message: 'Pulled from remote',\n createdBy: identity,\n });\n\n return { success: true, commitSha: remote.sha, action: 'pull' };\n }\n\n async forcePullFlow(\n db: PluginDatabaseApi,\n flowId: string,\n identity?: string,\n ): Promise<VcSyncResult> {\n // Same as pull but ignores SHA check — always overwrites local\n const config = await this.requireConfig(db, flowId);\n const remote = await this.provider.getFileContent(config.repo, config.filePath, config.branch);\n\n if (!remote) {\n return { success: false, error: 'Remote file not found', action: 'pull' };\n }\n\n await this.importFlowContent(db, flowId, remote.content, identity);\n await this.updateConfigAfterSync(db, flowId, remote.sha, null);\n await this.recordHistory(db, flowId, 'pull', {\n commitSha: remote.sha,\n message: 'Force pull (remote wins)',\n createdBy: identity,\n });\n\n return { success: true, commitSha: remote.sha, action: 'pull' };\n }\n\n // =========================================================================\n // Publish (pr-per-publish mode — open PR from draft branch)\n // =========================================================================\n\n async publishFlow(\n db: PluginDatabaseApi,\n flowId: string,\n identity?: string,\n ): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n\n if (config.mode !== 'pr-per-publish') {\n return {\n success: false,\n error: 'Publish is only available in pr-per-publish mode',\n action: 'pr-created',\n };\n }\n\n if (!config.draftBranch) {\n return {\n success: false,\n error: 'No draft branch found — push changes first',\n action: 'pr-created',\n };\n }\n\n // Check if there's already an active PR\n if (config.activePrNumber) {\n const pr = await this.provider.getPullRequest(config.repo, config.activePrNumber);\n if (pr.state === 'open') {\n return {\n success: true,\n prNumber: config.activePrNumber,\n prUrl: config.activePrUrl ?? undefined,\n action: 'pr-created',\n };\n }\n // PR was closed/merged — clear it and create a new one\n }\n\n const fileName = this.flowFileName(config.filePath);\n const pr = await this.provider.createPullRequest(config.repo, {\n title: `feat(flow): publish ${fileName}`,\n body: `Automated PR from Invect — publishing flow changes for \\`${fileName}\\`.`,\n head: config.draftBranch,\n base: config.branch,\n });\n\n await db.execute(\n 'UPDATE vc_sync_config SET active_pr_number = ?, active_pr_url = ?, updated_at = ? WHERE flow_id = ?',\n [pr.number, pr.url, new Date().toISOString(), flowId],\n );\n\n await this.recordHistory(db, flowId, 'pr-created', {\n prNumber: pr.number,\n message: `PR #${pr.number} created`,\n createdBy: identity,\n });\n\n return { success: true, prNumber: pr.number, prUrl: pr.url, action: 'pr-created' };\n }\n\n // =========================================================================\n // Status & History\n // =========================================================================\n\n async getFlowSyncStatus(\n db: PluginDatabaseApi,\n flowId: string,\n ): Promise<{\n status: VcSyncStatus;\n config: VcSyncConfig | null;\n lastSync: VcSyncHistoryRecord | null;\n }> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n return { status: 'not-connected', config: null, lastSync: null };\n }\n\n const history = await db.query<VcSyncHistoryRow>(\n 'SELECT * FROM vc_sync_history WHERE flow_id = ? ORDER BY created_at DESC LIMIT 1',\n [flowId],\n );\n\n const lastSync = history.length > 0 ? mapHistoryRow(history[0]) : null;\n\n let status: VcSyncStatus = 'synced';\n if (!config.enabled) {\n status = 'not-connected';\n } else if (lastSync?.action === 'conflict') {\n status = 'conflict';\n } else if (!config.lastSyncedAt) {\n status = 'pending';\n } else {\n // Check if there are newer versions than what was synced\n const versions = await db.query<{ version: number }>(\n 'SELECT MAX(version) as version FROM flow_versions WHERE flow_id = ?',\n [flowId],\n );\n const latestVersion = versions[0]?.version;\n if (latestVersion && config.lastSyncedVersion && latestVersion > config.lastSyncedVersion) {\n status = 'pending';\n }\n }\n\n return { status, config, lastSync };\n }\n\n async getSyncHistory(\n db: PluginDatabaseApi,\n flowId: string,\n limit = 20,\n ): Promise<VcSyncHistoryRecord[]> {\n const rows = await db.query<VcSyncHistoryRow>(\n 'SELECT * FROM vc_sync_history WHERE flow_id = ? ORDER BY created_at DESC LIMIT ?',\n [flowId, limit],\n );\n return rows.map(mapHistoryRow);\n }\n\n async listSyncedFlows(\n db: PluginDatabaseApi,\n ): Promise<Array<VcSyncConfig & { flowName: string }>> {\n const rows = await db.query<VcSyncConfigRow & { flow_name: string }>(\n `SELECT vc_sync_config.*, flows.name as flow_name\n FROM vc_sync_config\n JOIN flows ON flows.id = vc_sync_config.flow_id\n ORDER BY vc_sync_config.updated_at DESC`,\n );\n return rows.map((r) => ({ ...mapSyncConfigRow(r), flowName: r.flow_name }));\n }\n\n // =========================================================================\n // Flow deletion hook\n // =========================================================================\n\n async onFlowDeleted(db: PluginDatabaseApi, flowId: string): Promise<void> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n return;\n }\n\n // Delete the file from the remote\n try {\n const remote = await this.provider.getFileContent(\n config.repo,\n config.filePath,\n config.branch,\n );\n if (remote) {\n await this.provider.deleteFile(\n config.repo,\n config.filePath,\n `chore(flow): delete ${this.flowFileName(config.filePath)}`,\n { branch: config.branch, sha: remote.sha },\n );\n this.logger.info('Deleted flow file from remote', { flowId, filePath: config.filePath });\n }\n } catch (err) {\n this.logger.warn('Failed to delete flow file from remote', {\n flowId,\n error: (err as Error).message,\n });\n }\n\n // Close active PR if any\n if (config.activePrNumber) {\n try {\n await this.provider.closePullRequest(\n config.repo,\n config.activePrNumber,\n 'Flow deleted — closing PR.',\n );\n } catch {\n // Ignore\n }\n }\n\n // Clean up draft branch\n if (config.draftBranch) {\n try {\n await this.provider.deleteBranch(config.repo, config.draftBranch);\n } catch {\n // Ignore\n }\n }\n\n // DB records cascade-delete from the flows FK\n }\n\n // =========================================================================\n // Internal — commit strategies\n // =========================================================================\n\n private async directCommit(\n db: PluginDatabaseApi,\n config: VcSyncConfig,\n content: string,\n version: number,\n identity?: string,\n ): Promise<VcSyncResult> {\n const sha = config.lastCommitSha ?? undefined;\n\n // Try to get remote SHA if we don't have one (first push)\n let remoteSha = sha;\n if (!remoteSha) {\n const remote = await this.provider.getFileContent(\n config.repo,\n config.filePath,\n config.branch,\n );\n remoteSha = remote?.sha;\n }\n\n const result = await this.provider.createOrUpdateFile(\n config.repo,\n config.filePath,\n content,\n `chore(flow): update ${this.flowFileName(config.filePath)} v${version}`,\n { branch: config.branch, sha: remoteSha },\n );\n\n await this.updateConfigAfterSync(db, config.flowId, result.commitSha, version);\n await this.recordHistory(db, config.flowId, 'push', {\n commitSha: result.commitSha,\n version,\n message: `Direct commit v${version}`,\n createdBy: identity,\n });\n\n return { success: true, commitSha: result.commitSha, action: 'push' };\n }\n\n private async commitToPrBranch(\n db: PluginDatabaseApi,\n config: VcSyncConfig,\n content: string,\n version: number,\n identity?: string,\n openPr: boolean = true,\n ): Promise<VcSyncResult> {\n const branchName = config.draftBranch ?? `invect/flow/${this.flowSlug(config.filePath)}`;\n\n // Create branch if it doesn't exist\n const existing = await this.provider.getBranch(config.repo, branchName);\n if (!existing) {\n await this.provider.createBranch(config.repo, branchName, config.branch);\n }\n\n // Get current file SHA on the branch\n const remote = await this.provider.getFileContent(config.repo, config.filePath, branchName);\n\n const result = await this.provider.createOrUpdateFile(\n config.repo,\n config.filePath,\n content,\n `chore(flow): update ${this.flowFileName(config.filePath)} v${version}`,\n { branch: branchName, sha: remote?.sha },\n );\n\n // Save draft branch reference\n await db.execute(\n 'UPDATE vc_sync_config SET draft_branch = ?, updated_at = ? WHERE flow_id = ?',\n [branchName, new Date().toISOString(), config.flowId],\n );\n\n let prNumber = config.activePrNumber ?? undefined;\n let prUrl = config.activePrUrl ?? undefined;\n\n // Open PR if needed\n if (openPr && !prNumber) {\n const pr = await this.provider.createPullRequest(config.repo, {\n title: `feat(flow): update ${this.flowFileName(config.filePath)}`,\n body: `Automated PR from Invect — flow changes for \\`${this.flowFileName(config.filePath)}\\`.`,\n head: branchName,\n base: config.branch,\n });\n prNumber = pr.number;\n prUrl = pr.url;\n\n await db.execute(\n 'UPDATE vc_sync_config SET active_pr_number = ?, active_pr_url = ?, updated_at = ? WHERE flow_id = ?',\n [prNumber, prUrl, new Date().toISOString(), config.flowId],\n );\n\n await this.recordHistory(db, config.flowId, 'pr-created', {\n commitSha: result.commitSha,\n prNumber,\n version,\n message: `PR #${prNumber} created`,\n createdBy: identity,\n });\n } else {\n await this.recordHistory(db, config.flowId, 'push', {\n commitSha: result.commitSha,\n version,\n message: `Updated PR branch v${version}`,\n createdBy: identity,\n });\n }\n\n await this.updateConfigAfterSync(db, config.flowId, result.commitSha, version);\n\n return {\n success: true,\n commitSha: result.commitSha,\n prNumber,\n prUrl,\n action: prNumber ? 'pr-created' : 'push',\n };\n }\n\n private async commitToDraftBranch(\n db: PluginDatabaseApi,\n config: VcSyncConfig,\n content: string,\n version: number,\n identity?: string,\n ): Promise<VcSyncResult> {\n // Same as PR branch commit but without opening a PR\n return this.commitToPrBranch(db, config, content, version, identity, false);\n }\n\n // =========================================================================\n // Internal — flow export / import\n // =========================================================================\n\n private async exportFlow(\n db: PluginDatabaseApi,\n flowId: string,\n ): Promise<{ content: string; version: number }> {\n const flows = await db.query<FlowRow>(\n 'SELECT id, name, description, tags FROM flows WHERE id = ?',\n [flowId],\n );\n if (flows.length === 0) {\n throw new Error(`Flow not found: ${flowId}`);\n }\n const flow = flows[0];\n\n const versions = await db.query<FlowVersionRow>(\n 'SELECT flow_id, version, invect_definition FROM flow_versions WHERE flow_id = ? ORDER BY version DESC LIMIT 1',\n [flowId],\n );\n if (versions.length === 0) {\n throw new Error(`No versions found for flow: ${flowId}`);\n }\n const fv = versions[0];\n\n const definition =\n typeof fv.invectDefinition === 'string'\n ? JSON.parse(fv.invectDefinition)\n : fv.invectDefinition;\n\n let tags: string[] | undefined;\n if (flow.tags) {\n try {\n tags = typeof flow.tags === 'string' ? JSON.parse(flow.tags) : flow.tags;\n } catch {\n tags = undefined;\n }\n }\n\n const content = serializeFlowToTs(definition, {\n name: flow.name,\n description: flow.description ?? undefined,\n tags,\n });\n\n return { content, version: fv.version };\n }\n\n private async importFlowContent(\n db: PluginDatabaseApi,\n flowId: string,\n content: string,\n identity?: string,\n ): Promise<void> {\n // 1. Write .flow.ts content to a temp file\n const { writeFileSync, unlinkSync, mkdtempSync } = await import('node:fs');\n const { join } = await import('node:path');\n const { tmpdir } = await import('node:os');\n\n const tmpDir = mkdtempSync(join(tmpdir(), 'invect-vc-'));\n const tmpFile = join(tmpDir, 'import.flow.ts');\n\n try {\n writeFileSync(tmpFile, content, 'utf-8');\n\n // 2. Load the .flow.ts file via jiti (resolves defineFlow + helpers)\n const { createJiti } = await import('jiti');\n const jiti = createJiti(import.meta.url, { interopDefault: true });\n const result = await jiti.import(tmpFile);\n\n // The file's default export should be an InvectDefinition (from defineFlow)\n const definition = (result as Record<string, unknown>).default ?? result;\n\n if (\n !definition ||\n typeof definition !== 'object' ||\n !('nodes' in definition) ||\n !('edges' in definition)\n ) {\n throw new Error(\n 'Imported .flow.ts file did not produce a valid InvectDefinition. ' +\n 'Expected an object with \"nodes\" and \"edges\" arrays.',\n );\n }\n\n // 3. Get current latest version number\n const versions = await db.query<{ version: number }>(\n 'SELECT MAX(version) as version FROM flow_versions WHERE flow_id = ?',\n [flowId],\n );\n const nextVersion = (versions[0]?.version ?? 0) + 1;\n\n // 4. Insert new flow version\n const defJson = typeof definition === 'string' ? definition : JSON.stringify(definition);\n\n await db.execute(\n `INSERT INTO flow_versions (flow_id, version, invect_definition, created_at, created_by)\n VALUES (?, ?, ?, ?, ?)`,\n [flowId, nextVersion, defJson, new Date().toISOString(), identity ?? null],\n );\n\n // 5. Update flow's live version\n await db.execute('UPDATE flows SET live_version_number = ?, updated_at = ? WHERE id = ?', [\n nextVersion,\n new Date().toISOString(),\n flowId,\n ]);\n\n this.logger.info('Flow imported from remote', {\n flowId,\n version: nextVersion,\n });\n } finally {\n // Clean up temp file\n try {\n unlinkSync(tmpFile);\n const { rmdirSync } = await import('node:fs');\n rmdirSync(tmpDir);\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n\n // =========================================================================\n // Internal — helpers\n // =========================================================================\n\n private buildFilePath(flowName: string): string {\n const basePath = this.options.path ?? 'workflows/';\n const slug = flowName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n return `${basePath}${slug}.flow.ts`;\n }\n\n private flowFileName(filePath: string): string {\n return filePath.split('/').pop() ?? filePath;\n }\n\n private flowSlug(filePath: string): string {\n const name = this.flowFileName(filePath);\n return name.replace(/\\.flow\\.ts$/, '');\n }\n\n private async requireConfig(db: PluginDatabaseApi, flowId: string): Promise<VcSyncConfig> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n throw new Error(`Flow ${flowId} is not connected to version control`);\n }\n if (!config.enabled) {\n throw new Error(`Version control sync is disabled for flow ${flowId}`);\n }\n return config;\n }\n\n private async updateConfigAfterSync(\n db: PluginDatabaseApi,\n flowId: string,\n commitSha: string,\n version: number | null,\n ): Promise<void> {\n const now = new Date().toISOString();\n if (version !== null) {\n await db.execute(\n 'UPDATE vc_sync_config SET last_synced_at = ?, last_commit_sha = ?, last_synced_version = ?, updated_at = ? WHERE flow_id = ?',\n [now, commitSha, version, now, flowId],\n );\n } else {\n await db.execute(\n 'UPDATE vc_sync_config SET last_synced_at = ?, last_commit_sha = ?, updated_at = ? WHERE flow_id = ?',\n [now, commitSha, now, flowId],\n );\n }\n }\n\n private async recordHistory(\n db: PluginDatabaseApi,\n flowId: string,\n action: import('../shared/types').VcSyncAction,\n opts: {\n commitSha?: string;\n prNumber?: number;\n version?: number;\n message?: string;\n createdBy?: string;\n },\n ): Promise<void> {\n await db.execute(\n `INSERT INTO vc_sync_history (id, flow_id, action, commit_sha, pr_number, version, message, created_at, created_by)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n randomUUID(),\n flowId,\n action,\n opts.commitSha ?? null,\n opts.prNumber ?? null,\n opts.version ?? null,\n opts.message ?? null,\n new Date().toISOString(),\n opts.createdBy ?? null,\n ],\n );\n }\n}\n\n// =============================================================================\n// Row mappers (snake_case DB rows → camelCase types)\n// =============================================================================\n\ninterface VcSyncConfigRow {\n id: string;\n flow_id: string;\n provider: string;\n repo: string;\n branch: string;\n file_path: string;\n mode: string;\n sync_direction: string;\n last_synced_at: string | null;\n last_commit_sha: string | null;\n last_synced_version: number | null;\n draft_branch: string | null;\n active_pr_number: number | null;\n active_pr_url: string | null;\n enabled: boolean | number;\n created_at: string;\n updated_at: string;\n}\n\ninterface VcSyncHistoryRow {\n id: string;\n flow_id: string;\n action: string;\n commit_sha: string | null;\n pr_number: number | null;\n version: number | null;\n message: string | null;\n created_at: string;\n created_by: string | null;\n}\n\nfunction mapSyncConfigRow(r: VcSyncConfigRow): VcSyncConfig {\n return {\n id: r.id,\n flowId: r.flow_id,\n provider: r.provider,\n repo: r.repo,\n branch: r.branch,\n filePath: r.file_path,\n mode: r.mode as VcSyncConfig['mode'],\n syncDirection: r.sync_direction as VcSyncConfig['syncDirection'],\n lastSyncedAt: r.last_synced_at,\n lastCommitSha: r.last_commit_sha,\n lastSyncedVersion: r.last_synced_version,\n draftBranch: r.draft_branch,\n activePrNumber: r.active_pr_number,\n activePrUrl: r.active_pr_url,\n enabled: r.enabled === true || r.enabled === 1,\n };\n}\n\nfunction mapHistoryRow(r: VcSyncHistoryRow): VcSyncHistoryRecord {\n return {\n id: r.id,\n flowId: r.flow_id,\n action: r.action as VcSyncHistoryRecord['action'],\n commitSha: r.commit_sha,\n prNumber: r.pr_number,\n version: r.version,\n message: r.message,\n createdAt: r.created_at,\n createdBy: r.created_by,\n };\n}\n","// =============================================================================\n// Version Control Plugin — Main Entry Point\n// =============================================================================\n\nimport type { InvectPlugin, PluginEndpointContext } from '@invect/core';\n\nimport type { VersionControlPluginOptions } from './types';\nimport type { ConfigureSyncInput } from '../shared/types';\nimport { VC_SCHEMA } from './schema';\nimport { VcSyncService } from './sync-service';\n\n/**\n * Create the Version Control plugin.\n *\n * Syncs Invect flows to a Git remote as readable `.flow.ts` files.\n *\n * ```ts\n * import { versionControl } from '@invect/version-control';\n * import { githubProvider } from '@invect/version-control/providers/github';\n *\n * new Invect({\n * plugins: [\n * versionControl({\n * provider: githubProvider({ auth: { type: 'token', token: process.env.GITHUB_TOKEN! } }),\n * repo: 'acme/workflows',\n * mode: 'pr-per-publish',\n * }),\n * ],\n * });\n * ```\n */\nexport function versionControl(options: VersionControlPluginOptions): InvectPlugin {\n let syncService: VcSyncService;\n let pluginLogger: { debug: Function; info: Function; warn: Function; error: Function } = console;\n\n return {\n id: 'version-control',\n name: 'Version Control',\n\n schema: VC_SCHEMA,\n\n setupInstructions:\n 'Run `npx invect-cli generate` then `npx invect-cli migrate` to create the vc_sync_config and vc_sync_history tables.',\n\n // =======================================================================\n // Initialization\n // =======================================================================\n\n init: async (ctx) => {\n pluginLogger = ctx.logger;\n syncService = new VcSyncService(options.provider, options, ctx.logger);\n ctx.logger.info(\n `Version control plugin initialized (provider: ${options.provider.id}, repo: ${options.repo})`,\n );\n },\n\n // =======================================================================\n // Endpoints\n // =======================================================================\n\n endpoints: [\n // -- Configure sync for a flow --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/configure',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const input = ctx.body as ConfigureSyncInput;\n const config = await syncService.configureSyncForFlow(ctx.database, flowId, input);\n return { status: 200, body: config };\n },\n },\n\n // -- Get sync status for a flow --\n {\n method: 'GET',\n path: '/vc/flows/:flowId/status',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const status = await syncService.getFlowSyncStatus(ctx.database, flowId);\n return { status: 200, body: { flowId, ...status } };\n },\n },\n\n // -- Disconnect sync for a flow --\n {\n method: 'DELETE',\n path: '/vc/flows/:flowId/disconnect',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n await syncService.disconnectFlow(ctx.database, flowId);\n return { status: 200, body: { success: true } };\n },\n },\n\n // -- Push (DB → remote) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/push',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.pushFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 409, body: result };\n },\n },\n\n // -- Pull (remote → DB) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/pull',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.pullFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 404, body: result };\n },\n },\n\n // -- Publish (pr-per-publish mode) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/publish',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.publishFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 400, body: result };\n },\n },\n\n // -- Force push (conflict resolution — DB wins) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/force-push',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.forcePushFlow(ctx.database, flowId, identity);\n return { status: 200, body: result };\n },\n },\n\n // -- Force pull (conflict resolution — remote wins) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/force-pull',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.forcePullFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 404, body: result };\n },\n },\n\n // -- Bulk push all synced flows --\n {\n method: 'POST',\n path: '/vc/push-all',\n handler: async (ctx: PluginEndpointContext) => {\n const configs = await syncService.listSyncedFlows(ctx.database);\n const identity = ctx.identity?.id;\n const results = [];\n for (const config of configs) {\n if (!config.enabled) {\n continue;\n }\n try {\n const result = await syncService.pushFlow(ctx.database, config.flowId, identity);\n results.push({ flowId: config.flowId, flowName: config.flowName, ...result });\n } catch (err) {\n results.push({\n flowId: config.flowId,\n flowName: config.flowName,\n success: false,\n error: (err as Error).message,\n action: 'push' as const,\n });\n }\n }\n return { status: 200, body: { results } };\n },\n },\n\n // -- Bulk pull all synced flows --\n {\n method: 'POST',\n path: '/vc/pull-all',\n handler: async (ctx: PluginEndpointContext) => {\n const configs = await syncService.listSyncedFlows(ctx.database);\n const identity = ctx.identity?.id;\n const results = [];\n for (const config of configs) {\n if (!config.enabled) {\n continue;\n }\n try {\n const result = await syncService.pullFlow(ctx.database, config.flowId, identity);\n results.push({ flowId: config.flowId, flowName: config.flowName, ...result });\n } catch (err) {\n results.push({\n flowId: config.flowId,\n flowName: config.flowName,\n success: false,\n error: (err as Error).message,\n action: 'pull' as const,\n });\n }\n }\n return { status: 200, body: { results } };\n },\n },\n\n // -- Webhook receiver --\n {\n method: 'POST',\n path: '/vc/webhook',\n isPublic: true,\n handler: async (ctx: PluginEndpointContext) => {\n if (!options.webhookSecret) {\n return { status: 400, body: { error: 'Webhook secret not configured' } };\n }\n\n // Verify signature\n const signature = ctx.headers['x-hub-signature-256'] ?? '';\n const body = JSON.stringify(ctx.body);\n if (!options.provider.verifyWebhookSignature(body, signature, options.webhookSecret)) {\n return { status: 401, body: { error: 'Invalid webhook signature' } };\n }\n\n // Handle PR merge events\n const action = (ctx.body as Record<string, unknown>).action;\n const pullRequest = (ctx.body as Record<string, unknown>).pull_request as\n | { merged: boolean; number: number }\n | undefined;\n\n if (action === 'closed' && pullRequest?.merged) {\n await handlePrMerged(ctx.database, pullRequest.number);\n }\n\n return { status: 200, body: { received: true } };\n },\n },\n\n // -- List all synced flows --\n {\n method: 'GET',\n path: '/vc/flows',\n handler: async (ctx: PluginEndpointContext) => {\n const flows = await syncService.listSyncedFlows(ctx.database);\n return { status: 200, body: { flows } };\n },\n },\n\n // -- Get sync history for a flow --\n {\n method: 'GET',\n path: '/vc/flows/:flowId/history',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 20;\n const history = await syncService.getSyncHistory(ctx.database, flowId, limit);\n return { status: 200, body: { flowId, history } };\n },\n },\n ],\n\n // =======================================================================\n // Hooks\n // =======================================================================\n\n // NOTE: Auto-sync on flow version creation is intentionally NOT implemented\n // as a hook. The onResponse hook context doesn't include database access.\n // Instead, auto-sync is handled by the frontend calling POST /vc/flows/:id/push\n // after saving a flow version. This matches the \"opt-in per flow\" decision —\n // only flows with an enabled vc_sync_config record get synced.\n\n hooks: {},\n };\n\n // =========================================================================\n // Webhook handlers (internal)\n // =========================================================================\n\n async function handlePrMerged(\n db: import('@invect/core').PluginDatabaseApi,\n prNumber: number,\n ): Promise<void> {\n // Find the sync config with this active PR\n const rows = await db.query<{ flow_id: string }>(\n 'SELECT flow_id FROM vc_sync_config WHERE active_pr_number = ?',\n [prNumber],\n );\n\n for (const row of rows) {\n // Clear PR state, update sync status\n await db.execute(\n `UPDATE vc_sync_config\n SET active_pr_number = NULL, active_pr_url = NULL, draft_branch = NULL, updated_at = ?\n WHERE flow_id = ?`,\n [new Date().toISOString(), row.flow_id],\n );\n\n // Record history\n const { randomUUID } = await import('node:crypto');\n await db.execute(\n `INSERT INTO vc_sync_history (id, flow_id, action, pr_number, message, created_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n [\n randomUUID(),\n row.flow_id,\n 'pr-merged',\n prNumber,\n `PR #${prNumber} merged`,\n new Date().toISOString(),\n ],\n );\n\n // Try to clean up the draft branch\n const configs = await db.query<{ draft_branch: string | null; repo: string }>(\n 'SELECT draft_branch, repo FROM vc_sync_config WHERE flow_id = ?',\n [row.flow_id],\n );\n if (configs[0]?.draft_branch) {\n try {\n await options.provider.deleteBranch(configs[0].repo, configs[0].draft_branch);\n } catch {\n // Branch may already be deleted by the merge\n }\n }\n\n pluginLogger.info('PR merged — sync updated', { flowId: row.flow_id, prNumber });\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { InvectPlugin } from '@invect/core';
2
+ import type { VersionControlPluginOptions } from './types';
3
+ /**
4
+ * Create the Version Control plugin.
5
+ *
6
+ * Syncs Invect flows to a Git remote as readable `.flow.ts` files.
7
+ *
8
+ * ```ts
9
+ * import { versionControl } from '@invect/version-control';
10
+ * import { githubProvider } from '@invect/version-control/providers/github';
11
+ *
12
+ * new Invect({
13
+ * plugins: [
14
+ * versionControl({
15
+ * provider: githubProvider({ auth: { type: 'token', token: process.env.GITHUB_TOKEN! } }),
16
+ * repo: 'acme/workflows',
17
+ * mode: 'pr-per-publish',
18
+ * }),
19
+ * ],
20
+ * });
21
+ * ```
22
+ */
23
+ export declare function versionControl(options: VersionControlPluginOptions): InvectPlugin;
24
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAyB,MAAM,cAAc,CAAC;AAExE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAK3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,2BAA2B,GAAG,YAAY,CA+SjF"}
@@ -0,0 +1,3 @@
1
+ import type { InvectPluginSchema } from '@invect/core';
2
+ export declare const VC_SCHEMA: InvectPluginSchema;
3
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/backend/schema.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAMvD,eAAO,MAAM,SAAS,EAAE,kBAmDvB,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type { GitProvider } from './git-provider';
2
+ import type { PluginDatabaseApi } from '@invect/core';
3
+ import type { VersionControlPluginOptions } from './types';
4
+ import type { VcSyncConfig, VcSyncHistoryRecord, VcSyncResult, VcSyncStatus, ConfigureSyncInput } from '../shared/types';
5
+ type Logger = {
6
+ debug(msg: string, meta?: Record<string, unknown>): void;
7
+ info(msg: string, meta?: Record<string, unknown>): void;
8
+ warn(msg: string, meta?: Record<string, unknown>): void;
9
+ error(msg: string, meta?: Record<string, unknown>): void;
10
+ };
11
+ export declare class VcSyncService {
12
+ private provider;
13
+ private options;
14
+ private logger;
15
+ constructor(provider: GitProvider, options: VersionControlPluginOptions, logger: Logger);
16
+ configureSyncForFlow(db: PluginDatabaseApi, flowId: string, input: ConfigureSyncInput): Promise<VcSyncConfig>;
17
+ getSyncConfig(db: PluginDatabaseApi, flowId: string): Promise<VcSyncConfig | null>;
18
+ disconnectFlow(db: PluginDatabaseApi, flowId: string): Promise<void>;
19
+ pushFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult>;
20
+ forcePushFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult>;
21
+ pullFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult>;
22
+ forcePullFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult>;
23
+ publishFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult>;
24
+ getFlowSyncStatus(db: PluginDatabaseApi, flowId: string): Promise<{
25
+ status: VcSyncStatus;
26
+ config: VcSyncConfig | null;
27
+ lastSync: VcSyncHistoryRecord | null;
28
+ }>;
29
+ getSyncHistory(db: PluginDatabaseApi, flowId: string, limit?: number): Promise<VcSyncHistoryRecord[]>;
30
+ listSyncedFlows(db: PluginDatabaseApi): Promise<Array<VcSyncConfig & {
31
+ flowName: string;
32
+ }>>;
33
+ onFlowDeleted(db: PluginDatabaseApi, flowId: string): Promise<void>;
34
+ private directCommit;
35
+ private commitToPrBranch;
36
+ private commitToDraftBranch;
37
+ private exportFlow;
38
+ private importFlowContent;
39
+ private buildFilePath;
40
+ private flowFileName;
41
+ private flowSlug;
42
+ private requireConfig;
43
+ private updateConfigAfterSync;
44
+ private recordHistory;
45
+ }
46
+ export {};
47
+ //# sourceMappingURL=sync-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-service.d.ts","sourceRoot":"","sources":["../../src/backend/sync-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { GitProvider } from './git-provider';
2
+ import type { VcSyncMode, VcSyncDirection } from '../shared/types';
3
+ /** Options for the versionControl() plugin factory */
4
+ export interface VersionControlPluginOptions {
5
+ /** Git hosting provider (e.g. githubProvider({ auth: ... })) */
6
+ provider: GitProvider;
7
+ /** Default repository (owner/name) */
8
+ repo: string;
9
+ /** Default target branch */
10
+ defaultBranch?: string;
11
+ /** Directory in the repo for flow files (trailing slash) */
12
+ path?: string;
13
+ /** Default sync mode */
14
+ mode?: VcSyncMode;
15
+ /** Default sync direction */
16
+ syncDirection?: VcSyncDirection;
17
+ /** Webhook secret for verifying incoming webhooks */
18
+ webhookSecret?: string;
19
+ }
20
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEnE,sDAAsD;AACtD,MAAM,WAAW,2BAA2B;IAC1C,gEAAgE;IAChE,QAAQ,EAAE,WAAW,CAAC;IAEtB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IAEb,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,wBAAwB;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,eAAe,CAAC;IAEhC,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
File without changes
@@ -0,0 +1,2 @@
1
+ import { a as VcSyncConfig, c as VcSyncMode, l as VcSyncResult, o as VcSyncDirection, r as VcFlowSyncStatus, s as VcSyncHistoryRecord, t as ConfigureSyncInput, u as VcSyncStatus } from "../types-B32wGtx7.cjs";
2
+ export { type ConfigureSyncInput, type VcFlowSyncStatus, type VcSyncConfig, type VcSyncDirection, type VcSyncHistoryRecord, type VcSyncMode, type VcSyncResult, type VcSyncStatus };
@@ -0,0 +1,2 @@
1
+ import { a as VcSyncConfig, c as VcSyncMode, l as VcSyncResult, o as VcSyncDirection, r as VcFlowSyncStatus, s as VcSyncHistoryRecord, t as ConfigureSyncInput, u as VcSyncStatus } from "../types-B7fFBAOX.mjs";
2
+ export { type ConfigureSyncInput, type VcFlowSyncStatus, type VcSyncConfig, type VcSyncDirection, type VcSyncHistoryRecord, type VcSyncMode, type VcSyncResult, type VcSyncStatus };
@@ -0,0 +1,2 @@
1
+ export type { VcSyncConfig, VcSyncHistoryRecord, VcFlowSyncStatus, VcSyncStatus, VcSyncMode, VcSyncDirection, VcSyncResult, ConfigureSyncInput, } from '../shared/types';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/frontend/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,eAAe,EACf,YAAY,EACZ,kBAAkB,GACnB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,80 @@
1
+ //#region src/backend/git-provider.d.ts
2
+ /** Result of fetching a file from the remote */
3
+ interface GitFileContent {
4
+ content: string;
5
+ sha: string;
6
+ }
7
+ /** Result of creating/updating a file on the remote */
8
+ interface GitCommitResult {
9
+ commitSha: string;
10
+ }
11
+ /** Branch info */
12
+ interface GitBranchInfo {
13
+ sha: string;
14
+ }
15
+ /** Pull request / merge request creation options */
16
+ interface CreatePullRequestOptions {
17
+ title: string;
18
+ body: string;
19
+ head: string;
20
+ base: string;
21
+ draft?: boolean;
22
+ }
23
+ /** Pull request / merge request result */
24
+ interface GitPullRequestResult {
25
+ number: number;
26
+ url: string;
27
+ }
28
+ /** Pull request / merge request info */
29
+ interface GitPullRequestInfo {
30
+ state: 'open' | 'closed' | 'merged';
31
+ mergedAt?: string;
32
+ }
33
+ /** File update options */
34
+ interface GitFileUpdateOptions {
35
+ branch: string;
36
+ sha?: string;
37
+ }
38
+ /**
39
+ * Abstraction over a Git hosting provider (GitHub, GitLab, Bitbucket).
40
+ *
41
+ * All methods operate on a specific repo (owner/name string).
42
+ * The provider handles authentication internally.
43
+ */
44
+ interface GitProvider {
45
+ /** Provider identifier, e.g. 'github', 'gitlab', 'bitbucket' */
46
+ readonly id: string;
47
+ /** Human-readable name */
48
+ readonly name: string;
49
+ /** Get file content at a specific ref (branch/SHA). Returns null if file doesn't exist. */
50
+ getFileContent(repo: string, path: string, ref?: string): Promise<GitFileContent | null>;
51
+ /** Create or update a file. If sha is provided, it's an update (must match current SHA for conflict detection). */
52
+ createOrUpdateFile(repo: string, path: string, content: string, message: string, opts: GitFileUpdateOptions): Promise<GitCommitResult>;
53
+ /** Delete a file from the repo. */
54
+ deleteFile(repo: string, path: string, message: string, opts: {
55
+ branch: string;
56
+ sha: string;
57
+ }): Promise<void>;
58
+ /** Create a new branch from a ref (branch name or SHA). */
59
+ createBranch(repo: string, branch: string, fromRef: string): Promise<void>;
60
+ /** Delete a branch. */
61
+ deleteBranch(repo: string, branch: string): Promise<void>;
62
+ /** Get branch info. Returns null if branch doesn't exist. */
63
+ getBranch(repo: string, branch: string): Promise<GitBranchInfo | null>;
64
+ /** Create a PR/MR. */
65
+ createPullRequest(repo: string, opts: CreatePullRequestOptions): Promise<GitPullRequestResult>;
66
+ /** Update an existing PR/MR title/body. */
67
+ updatePullRequest(repo: string, number: number, opts: {
68
+ title?: string;
69
+ body?: string;
70
+ }): Promise<void>;
71
+ /** Get PR/MR status. */
72
+ getPullRequest(repo: string, number: number): Promise<GitPullRequestInfo>;
73
+ /** Close a PR/MR with an optional comment. */
74
+ closePullRequest(repo: string, number: number, comment?: string): Promise<void>;
75
+ /** Verify a webhook signature. Returns true if valid. */
76
+ verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
77
+ }
78
+ //#endregion
79
+ export { GitProvider as t };
80
+ //# sourceMappingURL=git-provider-BD8MMEXB.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-provider-BD8MMEXB.d.mts","names":[],"sources":["../src/backend/git-provider.ts"],"mappings":";;UAKiB,cAAA;EACf,OAAA;EACA,GAAA;AAAA;;UAIe,eAAA;EACf,SAAA;AAAA;;UAIe,aAAA;EACf,GAAA;AAAA;;UAIe,wBAAA;EACf,KAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;AAAA;;UAIe,oBAAA;EACf,MAAA;EACA,GAAA;AAAA;;UAIe,kBAAA;EACf,KAAA;EACA,QAAA;AAAA;;UAIe,oBAAA;EACf,MAAA;EACA,GAAA;AAAA;;AAFF;;;;;UAWiB,WAAA;EAAW;EAAA,SAEjB,EAAA;EAOyD;EAAA,SALzD,IAAA;EAaD;EARR,cAAA,CAAe,IAAA,UAAc,IAAA,UAAc,GAAA,YAAe,OAAA,CAAQ,cAAA;EAS/D;EANH,kBAAA,CACE,IAAA,UACA,IAAA,UACA,OAAA,UACA,OAAA,UACA,IAAA,EAAM,oBAAA,GACL,OAAA,CAAQ,eAAA;EAakD;EAV7D,UAAA,CACE,IAAA,UACA,IAAA,UACA,OAAA,UACA,IAAA;IAAQ,MAAA;IAAgB,GAAA;EAAA,IACvB,OAAA;EAgBsE;EAXzE,YAAA,CAAa,IAAA,UAAc,MAAA,UAAgB,OAAA,WAAkB,OAAA;EAkB1D;EAfH,YAAA,CAAa,IAAA,UAAc,MAAA,WAAiB,OAAA;EAkBE;EAf9C,SAAA,CAAU,IAAA,UAAc,MAAA,WAAiB,OAAA,CAAQ,aAAA;EAkBwB;EAbzE,iBAAA,CAAkB,IAAA,UAAc,IAAA,EAAM,wBAAA,GAA2B,OAAA,CAAQ,oBAAA;EAxChE;EA2CT,iBAAA,CACE,IAAA,UACA,MAAA,UACA,IAAA;IAAQ,KAAA;IAAgB,IAAA;EAAA,IACvB,OAAA;EAxCwC;EA2C3C,cAAA,CAAe,IAAA,UAAc,MAAA,WAAiB,OAAA,CAAQ,kBAAA;EA3CY;EA8ClE,gBAAA,CAAiB,IAAA,UAAc,MAAA,UAAgB,OAAA,YAAmB,OAAA;EA1ChE;EA+CF,sBAAA,CAAuB,OAAA,UAAiB,SAAA,UAAmB,MAAA;AAAA"}
@@ -0,0 +1,80 @@
1
+ //#region src/backend/git-provider.d.ts
2
+ /** Result of fetching a file from the remote */
3
+ interface GitFileContent {
4
+ content: string;
5
+ sha: string;
6
+ }
7
+ /** Result of creating/updating a file on the remote */
8
+ interface GitCommitResult {
9
+ commitSha: string;
10
+ }
11
+ /** Branch info */
12
+ interface GitBranchInfo {
13
+ sha: string;
14
+ }
15
+ /** Pull request / merge request creation options */
16
+ interface CreatePullRequestOptions {
17
+ title: string;
18
+ body: string;
19
+ head: string;
20
+ base: string;
21
+ draft?: boolean;
22
+ }
23
+ /** Pull request / merge request result */
24
+ interface GitPullRequestResult {
25
+ number: number;
26
+ url: string;
27
+ }
28
+ /** Pull request / merge request info */
29
+ interface GitPullRequestInfo {
30
+ state: 'open' | 'closed' | 'merged';
31
+ mergedAt?: string;
32
+ }
33
+ /** File update options */
34
+ interface GitFileUpdateOptions {
35
+ branch: string;
36
+ sha?: string;
37
+ }
38
+ /**
39
+ * Abstraction over a Git hosting provider (GitHub, GitLab, Bitbucket).
40
+ *
41
+ * All methods operate on a specific repo (owner/name string).
42
+ * The provider handles authentication internally.
43
+ */
44
+ interface GitProvider {
45
+ /** Provider identifier, e.g. 'github', 'gitlab', 'bitbucket' */
46
+ readonly id: string;
47
+ /** Human-readable name */
48
+ readonly name: string;
49
+ /** Get file content at a specific ref (branch/SHA). Returns null if file doesn't exist. */
50
+ getFileContent(repo: string, path: string, ref?: string): Promise<GitFileContent | null>;
51
+ /** Create or update a file. If sha is provided, it's an update (must match current SHA for conflict detection). */
52
+ createOrUpdateFile(repo: string, path: string, content: string, message: string, opts: GitFileUpdateOptions): Promise<GitCommitResult>;
53
+ /** Delete a file from the repo. */
54
+ deleteFile(repo: string, path: string, message: string, opts: {
55
+ branch: string;
56
+ sha: string;
57
+ }): Promise<void>;
58
+ /** Create a new branch from a ref (branch name or SHA). */
59
+ createBranch(repo: string, branch: string, fromRef: string): Promise<void>;
60
+ /** Delete a branch. */
61
+ deleteBranch(repo: string, branch: string): Promise<void>;
62
+ /** Get branch info. Returns null if branch doesn't exist. */
63
+ getBranch(repo: string, branch: string): Promise<GitBranchInfo | null>;
64
+ /** Create a PR/MR. */
65
+ createPullRequest(repo: string, opts: CreatePullRequestOptions): Promise<GitPullRequestResult>;
66
+ /** Update an existing PR/MR title/body. */
67
+ updatePullRequest(repo: string, number: number, opts: {
68
+ title?: string;
69
+ body?: string;
70
+ }): Promise<void>;
71
+ /** Get PR/MR status. */
72
+ getPullRequest(repo: string, number: number): Promise<GitPullRequestInfo>;
73
+ /** Close a PR/MR with an optional comment. */
74
+ closePullRequest(repo: string, number: number, comment?: string): Promise<void>;
75
+ /** Verify a webhook signature. Returns true if valid. */
76
+ verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
77
+ }
78
+ //#endregion
79
+ export { GitProvider as t };
80
+ //# sourceMappingURL=git-provider-CjMtpb86.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-provider-CjMtpb86.d.cts","names":[],"sources":["../src/backend/git-provider.ts"],"mappings":";;UAKiB,cAAA;EACf,OAAA;EACA,GAAA;AAAA;;UAIe,eAAA;EACf,SAAA;AAAA;;UAIe,aAAA;EACf,GAAA;AAAA;;UAIe,wBAAA;EACf,KAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;AAAA;;UAIe,oBAAA;EACf,MAAA;EACA,GAAA;AAAA;;UAIe,kBAAA;EACf,KAAA;EACA,QAAA;AAAA;;UAIe,oBAAA;EACf,MAAA;EACA,GAAA;AAAA;;AAFF;;;;;UAWiB,WAAA;EAAW;EAAA,SAEjB,EAAA;EAOyD;EAAA,SALzD,IAAA;EAaD;EARR,cAAA,CAAe,IAAA,UAAc,IAAA,UAAc,GAAA,YAAe,OAAA,CAAQ,cAAA;EAS/D;EANH,kBAAA,CACE,IAAA,UACA,IAAA,UACA,OAAA,UACA,OAAA,UACA,IAAA,EAAM,oBAAA,GACL,OAAA,CAAQ,eAAA;EAakD;EAV7D,UAAA,CACE,IAAA,UACA,IAAA,UACA,OAAA,UACA,IAAA;IAAQ,MAAA;IAAgB,GAAA;EAAA,IACvB,OAAA;EAgBsE;EAXzE,YAAA,CAAa,IAAA,UAAc,MAAA,UAAgB,OAAA,WAAkB,OAAA;EAkB1D;EAfH,YAAA,CAAa,IAAA,UAAc,MAAA,WAAiB,OAAA;EAkBE;EAf9C,SAAA,CAAU,IAAA,UAAc,MAAA,WAAiB,OAAA,CAAQ,aAAA;EAkBwB;EAbzE,iBAAA,CAAkB,IAAA,UAAc,IAAA,EAAM,wBAAA,GAA2B,OAAA,CAAQ,oBAAA;EAxChE;EA2CT,iBAAA,CACE,IAAA,UACA,MAAA,UACA,IAAA;IAAQ,KAAA;IAAgB,IAAA;EAAA,IACvB,OAAA;EAxCwC;EA2C3C,cAAA,CAAe,IAAA,UAAc,MAAA,WAAiB,OAAA,CAAQ,kBAAA;EA3CY;EA8ClE,gBAAA,CAAiB,IAAA,UAAc,MAAA,UAAgB,OAAA,YAAmB,OAAA;EA1ChE;EA+CF,sBAAA,CAAuB,OAAA,UAAiB,SAAA,UAAmB,MAAA;AAAA"}