Forráskód Böngészése

feat(v0.3.0): add workflow engine — scheduled tasks, file-change triggers

- packages/kirincode-workflow/ — cron scheduler + file watcher + task store
- Cron expression parser (minute/hour/day/month/weekday)
- File watcher with debounce support
- /workflow command: list, create, run, reload
- Workflow definitions: .kirincode/workflows/*.yml
- State persistence: JSON-based runs and state store
- Default config: includes kirincode-workflow plugin
KirinCode 1 hónapja
szülő
commit
f682239d21
35 módosított fájl, 490 hozzáadás és 29 törlés
  1. 4 1
      kirincode.json
  2. 1 1
      packages/app/package.json
  3. 1 1
      packages/cli/package.json
  4. 1 1
      packages/codemode/package.json
  5. 1 1
      packages/console/app/package.json
  6. 1 1
      packages/console/core/package.json
  7. 1 1
      packages/console/function/package.json
  8. 1 1
      packages/console/mail/package.json
  9. 1 1
      packages/console/support/package.json
  10. 1 1
      packages/core/package.json
  11. 1 1
      packages/desktop/package.json
  12. 1 1
      packages/effect-drizzle-sqlite/package.json
  13. 1 1
      packages/effect-sqlite-node/package.json
  14. 1 1
      packages/enterprise/package.json
  15. 1 1
      packages/function/package.json
  16. 1 1
      packages/http-recorder/package.json
  17. 18 0
      packages/kirincode-workflow/package.json
  18. 168 0
      packages/kirincode-workflow/src/index.ts
  19. 84 0
      packages/kirincode-workflow/src/scheduler.ts
  20. 108 0
      packages/kirincode-workflow/src/store.ts
  21. 31 0
      packages/kirincode-workflow/src/types.ts
  22. 49 0
      packages/kirincode-workflow/src/watcher.ts
  23. 1 1
      packages/kirincode/package.json
  24. 1 1
      packages/llm/package.json
  25. 1 1
      packages/plugin/package.json
  26. 1 1
      packages/sdk/js/package.json
  27. 1 1
      packages/server/package.json
  28. 1 1
      packages/session-ui/package.json
  29. 1 1
      packages/slack/package.json
  30. 1 1
      packages/stats/app/package.json
  31. 1 1
      packages/stats/core/package.json
  32. 1 1
      packages/stats/server/package.json
  33. 1 1
      packages/tui/package.json
  34. 1 1
      packages/ui/package.json
  35. 1 1
      packages/web/package.json

+ 4 - 1
kirincode.json

@@ -4,7 +4,10 @@
   "theme": "kirincode",
   "share": "disabled",
   "autoupdate": "notify",
-  "plugin": ["@kirincode-ai/kirincode-core"],
+  "plugin": [
+    "@kirincode-ai/kirincode-core",
+    "@kirincode-ai/kirincode-workflow"
+  ],
   "permission": {
     "edit": "allow",
     "bash": "ask"

+ 1 - 1
packages/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/app",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "description": "",
   "type": "module",
   "exports": {

+ 1 - 1
packages/cli/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/cli",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "type": "module",
   "license": "MIT",
   "bin": {

+ 1 - 1
packages/codemode/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/codemode",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "description": "Effect-native confined code execution over schema-described tools",
   "private": true,
   "type": "module",

+ 1 - 1
packages/console/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/console-app",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 1 - 1
packages/console/core/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/console-core",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/console/function/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/console-function",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "$schema": "https://json.schemastore.org/package.json",
   "private": true,
   "type": "module",

+ 1 - 1
packages/console/mail/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/console-mail",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "dependencies": {
     "@jsx-email/all": "2.2.3",
     "@jsx-email/cli": "1.4.3",

+ 1 - 1
packages/console/support/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/console-support",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 1 - 1
packages/core/package.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "name": "@kirincode-ai/core",
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/desktop/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@kirincode-ai/desktop",
   "private": true,
-  "version": "0.2.2",
+  "version": "0.3.0",
   "type": "module",
   "license": "MIT",
   "homepage": "https://kirincode.ai",

+ 1 - 1
packages/effect-drizzle-sqlite/package.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "name": "@kirincode-ai/effect-drizzle-sqlite",
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/effect-sqlite-node/package.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "name": "@kirincode-ai/effect-sqlite-node",
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/enterprise/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/enterprise",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/function/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/function",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "$schema": "https://json.schemastore.org/package.json",
   "private": true,
   "type": "module",

+ 1 - 1
packages/http-recorder/package.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "name": "@kirincode-ai/http-recorder",
   "description": "Record and replay Effect HTTP client traffic with deterministic cassettes",
   "type": "module",

+ 18 - 0
packages/kirincode-workflow/package.json

@@ -0,0 +1,18 @@
+{
+  "$schema": "https://json.schemastore.org/package.json",
+  "name": "@kirincode-ai/kirincode-workflow",
+  "version": "0.3.0",
+  "type": "module",
+  "description": "KirinCode workflow engine — scheduled tasks, event triggers, background agents",
+  "license": "MIT",
+  "exports": {
+    ".": "./src/index.ts"
+  },
+  "dependencies": {
+    "@kirincode-ai/plugin": "workspace:*",
+    "@kirincode-ai/sdk": "workspace:*"
+  },
+  "devDependencies": {
+    "typescript": "catalog:"
+  }
+}

+ 168 - 0
packages/kirincode-workflow/src/index.ts

@@ -0,0 +1,168 @@
+import type { Plugin } from "@kirincode-ai/plugin"
+import type { Part } from "@kirincode-ai/sdk"
+import { CronScheduler } from "./scheduler"
+import { FileWatcher } from "./watcher"
+import { WorkflowStore } from "./store"
+import type { WorkflowDef, TaskRun } from "./types"
+
+const RUNNING_TASKS = new Map<string, TaskRun>()
+
+function randomId(): string {
+  return Math.random().toString(36).slice(2, 10)
+}
+
+export const KirinCodeWorkflowPlugin: Plugin = async ({ client, directory, worktree, $ }) => {
+  const store = new WorkflowStore(worktree)
+  const scheduler = new CronScheduler()
+  const watcher = new FileWatcher()
+
+  async function executeWorkflow(def: WorkflowDef): Promise<void> {
+    const now = Date.now()
+    const run: TaskRun = {
+      id: `run_${randomId()}`,
+      workflow: def.name,
+      startedAt: now,
+      status: "running",
+    }
+    RUNNING_TASKS.set(run.id, run)
+    store.saveRun(run)
+
+    try {
+      const result = await client.session.create({ body: { title: `[WF] ${def.name}` } })
+      if (result.error) throw new Error(result.error as string)
+
+      const sessionId = result.data!.id
+      run.sessionId = sessionId
+      store.saveRun(run)
+
+      await client.session.prompt({
+        path: { id: sessionId },
+        body: {
+          parts: [{ type: "text", text: def.prompt }],
+          model: def.model && def.agent
+            ? { providerID: def.model.split("/")[0], modelID: def.model.split("/").slice(1).join("/") }
+            : undefined,
+        },
+      })
+
+      run.status = "completed"
+      run.endedAt = Date.now()
+    } catch (e: any) {
+      run.status = "failed"
+      run.error = e?.message ?? String(e)
+      run.endedAt = Date.now()
+    } finally {
+      RUNNING_TASKS.delete(run.id)
+      store.saveRun(run)
+
+      const state = store.loadState(def.name)
+      state.lastRun = run.startedAt
+      state.runCount++
+      store.saveState(state)
+    }
+  }
+
+  // Load and register all workflow definitions
+  function reloadWorkflows(): void {
+    scheduler.stopAll()
+    watcher.stopAll()
+
+    const defs = store.loadDefs()
+    for (const def of defs) {
+      if (def.enabled === false) continue
+
+      if (def.schedule) {
+        const cron = scheduler.parse(def.schedule)
+        const intervalMs = 30000 // check every 30s
+        scheduler.start(`wf_${def.name}`, intervalMs, () => {
+          const next = cron.next()
+          const now = Date.now()
+          if (Math.abs(next - now) < intervalMs) {
+            executeWorkflow(def)
+          }
+        })
+      }
+
+      if (def.trigger === "file_change") {
+        watcher.watch(def, worktree, () => executeWorkflow(def))
+      }
+    }
+  }
+
+  reloadWorkflows()
+
+  return {
+    "command.execute.before": async (input, output) => {
+      const args = input.arguments.trim()
+
+      if (input.command === "workflow") {
+        if (!args || args === "list") {
+          const defs = store.loadDefs()
+          const runs = store.listRuns(10)
+          const lines = ["## Workflows", ""]
+          for (const d of defs) {
+            const state = store.loadState(d.name)
+            const icon = d.enabled === false ? "⏸" : "▶"
+            const sch = d.schedule ? ` schedule: ${d.schedule}` : ""
+            const trig = d.trigger ? ` trigger: ${d.trigger}` : ""
+            lines.push(`- ${icon} **${d.name}** — ${d.description || d.prompt.slice(0, 60)}${sch}${trig} (runs: ${state.runCount})`)
+          }
+          if (defs.length === 0) lines.push("*No workflows defined. Create .kirincode/workflows/*.yml files.*")
+          lines.push("", "## Recent Runs", "")
+          for (const r of runs) {
+            const icon = r.status === "completed" ? "✅" : r.status === "failed" ? "❌" : "🔄"
+            lines.push(`- ${icon} ${r.workflow} — ${new Date(r.startedAt).toLocaleTimeString()}`)
+          }
+          output.parts = [{ type: "text", text: lines.join("\n") }]
+          return
+        }
+
+        if (args === "reload") {
+          reloadWorkflows()
+          output.parts = [{ type: "text", text: `Workflows reloaded. ${store.loadDefs().length} active.` }]
+          return
+        }
+
+        if (args.startsWith("run ")) {
+          const name = args.slice(4).trim()
+          const def = store.loadDefs().find((d) => d.name === name)
+          if (!def) {
+            output.parts = [{ type: "text", text: `Workflow "${name}" not found.` }]
+            return
+          }
+          executeWorkflow(def)
+          output.parts = [{ type: "text", text: `Started workflow: ${name}` }]
+          return
+        }
+
+        if (args.startsWith("create ")) {
+          const parts = args.slice(7).split(/\s+/)
+          if (parts.length < 2) {
+            output.parts = [{ type: "text", text: "Usage: /workflow create <name> <cron|file_change> [prompt...]" }]
+            return
+          }
+          const name = parts[0]
+          const trigger = parts[1]
+          const prompt = parts.slice(2).join(" ") || "TODO: add your prompt here"
+          
+          const def: WorkflowDef = { name, prompt }
+          if (trigger.match(/^[\d\*,\-\/ ]{9,}$/)) def.schedule = trigger
+          else if (trigger === "file_change") def.trigger = "file_change"
+          else {
+            output.parts = [{ type: "text", text: "Invalid trigger. Use a cron expression or 'file_change'." }]
+            return
+          }
+          store.saveDef(def)
+          reloadWorkflows()
+          output.parts = [{ type: "text", text: `Workflow '${name}' created at .kirincode/workflows/${name}.yml` }]
+          return
+        }
+      }
+    },
+
+    dispose: async () => {
+      scheduler.stopAll()
+      watcher.stopAll()
+    },
+  }
+}

+ 84 - 0
packages/kirincode-workflow/src/scheduler.ts

@@ -0,0 +1,84 @@
+export class CronScheduler {
+  private timers: Map<string, ReturnType<typeof setInterval>> = new Map()
+
+  parse(cron: string): { next: () => number } {
+    const parts = cron.trim().split(/\s+/)
+    if (parts.length !== 5) throw new Error(`Invalid cron: ${cron}`)
+
+    const [min, hour, day, month, weekday] = parts
+    const mins = this.parseField(min, 0, 59)
+    const hours = this.parseField(hour, 0, 23)
+    const days = this.parseField(day, 1, 31)
+    const months = this.parseField(month, 1, 12)
+    const weekdays = this.parseField(weekday, 0, 6)
+
+    return {
+      next: () => {
+        const now = new Date()
+        const candidates: number[] = []
+        for (let m = 0; m < 60; m++) {
+          for (let h = 0; h < 24; h++) {
+            for (let d = 1; d <= 31; d++) {
+              for (let mo = 1; mo <= 12; mo++) {
+                if (mins.has(m) && hours.has(h) && days.has(d) && months.has(mo)) {
+                  const candidate = new Date(now.getFullYear(), mo - 1, d, h, m, 0, 0)
+                  if (candidate > now && weekdays.has(candidate.getDay())) {
+                    candidates.push(candidate.getTime())
+                  }
+                }
+              }
+            }
+          }
+        }
+        candidates.sort((a, b) => a - b)
+        return candidates[0] || (now.getTime() + 60000)
+      }
+    }
+  }
+
+  private parseField(field: string, min: number, max: number): Set<number> {
+    const result = new Set<number>()
+    if (field === "*") {
+      for (let i = min; i <= max; i++) result.add(i)
+      return result
+    }
+    for (const part of field.split(",")) {
+      if (part.includes("/")) {
+        const [range, step] = part.split("/")
+        const s = parseInt(step)
+        const [rMin, rMax] = range === "*" ? [min, max] : range.split("-").map(Number)
+        for (let i = rMin!; i <= (rMax ?? rMin!); i += s) {
+          if (i >= min && i <= max) result.add(i)
+        }
+      } else if (part.includes("-")) {
+        const [lo, hi] = part.split("-").map(Number)
+        for (let i = lo!; i <= hi!; i++) {
+          if (i >= min && i <= max) result.add(i)
+        }
+      } else {
+        const v = parseInt(part)
+        if (v >= min && v <= max) result.add(v)
+      }
+    }
+    return result
+  }
+
+  start(name: string, intervalMs: number, fn: () => void): void {
+    this.stop(name)
+    const t = setInterval(fn, intervalMs)
+    this.timers.set(name, t)
+  }
+
+  stop(name: string): void {
+    const t = this.timers.get(name)
+    if (t) {
+      clearInterval(t)
+      this.timers.delete(name)
+    }
+  }
+
+  stopAll(): void {
+    for (const t of this.timers.values()) clearInterval(t)
+    this.timers.clear()
+  }
+}

+ 108 - 0
packages/kirincode-workflow/src/store.ts

@@ -0,0 +1,108 @@
+import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from "node:fs"
+import { join, basename } from "node:path"
+import type { WorkflowDef, WorkflowState, TaskRun } from "./types"
+
+export class WorkflowStore {
+  private statePath: string
+  private runsPath: string
+  private defsPath: string
+  private worktree: string
+
+  constructor(worktree: string) {
+    this.worktree = worktree
+    const base = join(worktree, ".kirincode", "workflows")
+    this.defsPath = base
+    this.statePath = join(base, ".state")
+    this.runsPath = join(base, ".runs")
+    mkdirSync(base, { recursive: true })
+    mkdirSync(this.statePath, { recursive: true })
+    mkdirSync(this.runsPath, { recursive: true })
+  }
+
+  loadDefs(): WorkflowDef[] {
+    if (!existsSync(this.defsPath)) return []
+    const files = readdirSync(this.defsPath).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"))
+    const defs: WorkflowDef[] = []
+    for (const file of files) {
+      try {
+        const content = readFileSync(join(this.defsPath, file), "utf-8")
+        const def = this.parseYaml(content)
+        def.name = def.name || basename(file, file.includes(".yml") ? ".yml" : ".yaml")
+        defs.push(def)
+      } catch {
+        // skip invalid
+      }
+    }
+    return defs
+  }
+
+  saveDef(def: WorkflowDef): void {
+    const yaml = this.toYaml(def)
+    writeFileSync(join(this.defsPath, `${def.name}.yml`), yaml, "utf-8")
+  }
+
+  loadState(name: string): WorkflowState {
+    const p = join(this.statePath, `${name}.json`)
+    if (!existsSync(p)) return { name, runCount: 0, enabled: true }
+    return JSON.parse(readFileSync(p, "utf-8"))
+  }
+
+  saveState(state: WorkflowState): void {
+    writeFileSync(join(this.statePath, `${state.name}.json`), JSON.stringify(state, null, 2), "utf-8")
+  }
+
+  saveRun(run: TaskRun): void {
+    writeFileSync(join(this.runsPath, `${run.id}.json`), JSON.stringify(run, null, 2), "utf-8")
+  }
+
+  listRuns(limit = 20): TaskRun[] {
+    if (!existsSync(this.runsPath)) return []
+    return readdirSync(this.runsPath)
+      .filter((f) => f.endsWith(".json"))
+      .map((f) => {
+        try { return JSON.parse(readFileSync(join(this.runsPath, f), "utf-8")) }
+        catch { return null }
+      })
+      .filter(Boolean)
+      .sort((a, b) => b!.startedAt - a!.startedAt)
+      .slice(0, limit) as TaskRun[]
+  }
+
+  private parseYaml(s: string): WorkflowDef {
+    const def: any = {}
+    const lines = s.split("\n")
+    let key = ""
+    for (const line of lines) {
+      const m = line.match(/^(\w[\w_-]*):\s*(.*)$/)
+      if (m) {
+        key = m[1]
+        const val = m[2].trim()
+        if (val.startsWith("[") && val.endsWith("]")) {
+          def[key] = val.slice(1, -1).split(",").map((x) => x.trim().replace(/['"]/g, ""))
+        } else if (val === "true") def[key] = true
+        else if (val === "false") def[key] = false
+        else if (/^\d+$/.test(val)) def[key] = parseInt(val)
+        else def[key] = val
+      } else if (line.startsWith("  ") && key) {
+        if (typeof def[key] === "string") def[key] += "\n" + line.trim()
+      }
+    }
+    return def as WorkflowDef
+  }
+
+  private toYaml(def: WorkflowDef): string {
+    const lines: string[] = []
+    for (const [k, v] of Object.entries(def)) {
+      if (v === undefined) continue
+      if (Array.isArray(v)) lines.push(`${k}: [${v.map((x) => `"${x}"`).join(", ")}]`)
+      else if (typeof v === "boolean") lines.push(`${k}: ${v}`)
+      else if (typeof v === "number") lines.push(`${k}: ${v}`)
+      else if (typeof v === "string" && v.includes("\n")) {
+        lines.push(`${k}: |`)
+        for (const l of v.split("\n")) lines.push(`  ${l}`)
+      }
+      else lines.push(`${k}: "${v}"`)
+    }
+    return lines.join("\n") + "\n"
+  }
+}

+ 31 - 0
packages/kirincode-workflow/src/types.ts

@@ -0,0 +1,31 @@
+export interface WorkflowDef {
+  name: string
+  description?: string
+  schedule?: string       // cron expression
+  trigger?: "file_change" | "manual"
+  paths?: string[]        // file patterns to watch
+  debounce?: number       // seconds, for file_change trigger
+  prompt: string
+  model?: string
+  agent?: string
+  enabled?: boolean
+}
+
+export interface WorkflowState {
+  name: string
+  lastRun?: number
+  nextRun?: number
+  runCount: number
+  lastError?: string
+  enabled: boolean
+}
+
+export interface TaskRun {
+  id: string
+  workflow: string
+  startedAt: number
+  endedAt?: number
+  status: "running" | "completed" | "failed"
+  sessionId?: string
+  error?: string
+}

+ 49 - 0
packages/kirincode-workflow/src/watcher.ts

@@ -0,0 +1,49 @@
+import { watch } from "node:fs"
+import { join } from "node:path"
+import type { WorkflowDef } from "./types"
+
+export class FileWatcher {
+  private watchers: Map<string, ReturnType<typeof watch>> = new Map()
+  private timers: Map<string, ReturnType<typeof setTimeout>> = new Map()
+
+  watch(def: WorkflowDef, worktree: string, onTrigger: () => void): void {
+    if (def.trigger !== "file_change" || !def.paths) return
+    const key = def.name
+    this.unwatch(key)
+
+    const patterns = def.paths.map((p) => {
+      const cleaned = p.replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*")
+      return new RegExp(cleaned)
+    })
+
+    const debounceMs = (def.debounce ?? 60) * 1000
+
+    const watcher = watch(worktree, { recursive: true, persistent: false }, (_event, filename) => {
+      if (!filename) return
+      if (patterns.some((r) => r.test(filename))) {
+        const existing = this.timers.get(key)
+        if (existing) clearTimeout(existing)
+        this.timers.set(key, setTimeout(onTrigger, debounceMs))
+      }
+    })
+
+    this.watchers.set(key, watcher)
+  }
+
+  unwatch(key: string): void {
+    const w = this.watchers.get(key)
+    if (w) {
+      w.close()
+      this.watchers.delete(key)
+    }
+    const t = this.timers.get(key)
+    if (t) {
+      clearTimeout(t)
+      this.timers.delete(key)
+    }
+  }
+
+  stopAll(): void {
+    for (const [k] of this.watchers) this.unwatch(k)
+  }
+}

+ 1 - 1
packages/kirincode/package.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "name": "kirincode",
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/llm/package.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "name": "@kirincode-ai/llm",
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/plugin/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/plugin",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 1 - 1
packages/sdk/js/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/sdk",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 1 - 1
packages/server/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/server",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/session-ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/session-ui",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/slack/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/slack",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 1 - 1
packages/stats/app/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/stats-app",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/stats/core/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/stats-core",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/stats/server/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/stats-server",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/tui/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@kirincode-ai/tui",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@kirincode-ai/ui",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "type": "module",
   "license": "MIT",
   "repository": {

+ 1 - 1
packages/web/package.json

@@ -2,7 +2,7 @@
   "name": "@kirincode-ai/web",
   "type": "module",
   "license": "MIT",
-  "version": "0.2.2",
+  "version": "0.3.0",
   "scripts": {
     "dev": "astro dev",
     "dev:remote": "VITE_API_URL=https://api.kirincode.ai astro dev",