Merge pull request #6550 from Kilo-Org/eshurakov/ingest-import
IngestImport - Bootstrap ingest for imported sessions
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import type { Argv } from "yargs"
|
||||
import type { Session as SDKSession, Message, Part } from "@kilocode/sdk/v2"
|
||||
import { Session } from "../../session"
|
||||
import { Bus } from "../../bus"
|
||||
import { cmd } from "./cmd"
|
||||
import { bootstrap } from "../bootstrap"
|
||||
import { Database } from "../../storage/db"
|
||||
@@ -10,6 +9,9 @@ import { Instance } from "../../project/instance"
|
||||
import { ShareNext } from "../../share/share-next"
|
||||
import { EOL } from "os"
|
||||
import { Filesystem } from "../../util/filesystem"
|
||||
import { Log } from "../../util/log"
|
||||
|
||||
const log = Log.create({ service: "import-command" })
|
||||
|
||||
/** Discriminated union returned by the ShareNext API (GET /api/share/:id/data) */
|
||||
export type ShareData =
|
||||
@@ -65,6 +67,44 @@ export function transformShareData(shareData: ShareData[]): {
|
||||
}
|
||||
}
|
||||
|
||||
// kilocode_change start
|
||||
export function ingestBootstrapWarning(sessionId: string, error: unknown) {
|
||||
const details = error instanceof Error ? error.message : String(error)
|
||||
return `Warning: imported session ${sessionId} locally, but ingest bootstrap failed: ${details}`
|
||||
}
|
||||
|
||||
async function ingestBootstrap(sessionId: string) {
|
||||
const { KiloSessions } = await import("../../kilo-sessions/kilo-sessions")
|
||||
return KiloSessions.bootstrap(sessionId)
|
||||
}
|
||||
|
||||
export async function bootstrapImportedSessionIngest(
|
||||
sessionId: string,
|
||||
input?: {
|
||||
bootstrap?: (sessionId: string) => Promise<unknown>
|
||||
warn?: (message: string) => void
|
||||
},
|
||||
) {
|
||||
const run = input?.bootstrap ?? ingestBootstrap
|
||||
const warn =
|
||||
input?.warn ??
|
||||
((message: string) => {
|
||||
process.stderr.write(message)
|
||||
process.stderr.write(EOL)
|
||||
})
|
||||
|
||||
log.info("ingest bootstrap started", { sessionId })
|
||||
await run(sessionId)
|
||||
.then(() => {
|
||||
log.info("ingest bootstrap completed", { sessionId })
|
||||
})
|
||||
.catch((error) => {
|
||||
log.error("ingest bootstrap failed", { sessionId, error })
|
||||
warn(ingestBootstrapWarning(sessionId, error))
|
||||
})
|
||||
}
|
||||
// kilocode_change end
|
||||
|
||||
export const ImportCommand = cmd({
|
||||
command: "import <file>",
|
||||
describe: "import session data from JSON file or URL",
|
||||
@@ -134,7 +174,6 @@ export const ImportCommand = cmd({
|
||||
|
||||
Database.use((db) => {
|
||||
db.insert(SessionTable).values(Session.toRow(exportData.info)).onConflictDoNothing().run()
|
||||
Database.effect(() => Bus.publish(Session.Event.Created, { info: exportData.info }))
|
||||
})
|
||||
|
||||
for (const msg of exportData.messages) {
|
||||
@@ -167,6 +206,10 @@ export const ImportCommand = cmd({
|
||||
}
|
||||
}
|
||||
|
||||
// kilocode_change start
|
||||
await bootstrapImportedSessionIngest(exportData.info.id)
|
||||
// kilocode_change end
|
||||
|
||||
process.stdout.write(`Imported session: ${exportData.info.id}`)
|
||||
process.stdout.write(EOL)
|
||||
})
|
||||
|
||||
@@ -198,8 +198,25 @@ export namespace KiloSessions {
|
||||
}
|
||||
|
||||
export async function create(sessionId: string) {
|
||||
const result = await bootstrap(sessionId)
|
||||
if (!result) return { id: "", ingestPath: "" }
|
||||
|
||||
void fullSync(sessionId).catch((error) => log.error("share full sync failed", { sessionId, error }))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function bootstrap(sessionId: string) {
|
||||
if (ingestDisabled) {
|
||||
log.info("session bootstrap skipped: ingest disabled", { sessionId })
|
||||
return
|
||||
}
|
||||
|
||||
const client = await getClient()
|
||||
if (!client) return { id: "", ingestPath: "" }
|
||||
if (!client) {
|
||||
log.info("session bootstrap skipped: no client", { sessionId })
|
||||
return
|
||||
}
|
||||
|
||||
log.info("creating session", { sessionId })
|
||||
|
||||
@@ -216,7 +233,7 @@ export namespace KiloSessions {
|
||||
|
||||
await Storage.write(["session_share", sessionId], result)
|
||||
|
||||
void fullSync(sessionId).catch((error) => log.error("share full sync failed", { sessionId, error }))
|
||||
log.info("session bootstrap completed", { sessionId })
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { test, expect } from "bun:test"
|
||||
import { parseShareUrl, transformShareData, type ShareData } from "../../src/cli/cmd/import"
|
||||
import {
|
||||
parseShareUrl,
|
||||
transformShareData,
|
||||
bootstrapImportedSessionIngest,
|
||||
ingestBootstrapWarning,
|
||||
type ShareData,
|
||||
} from "../../src/cli/cmd/import"
|
||||
|
||||
// parseShareUrl tests
|
||||
test("parses valid share URLs", () => {
|
||||
@@ -36,3 +42,41 @@ test("returns null for invalid share data", () => {
|
||||
expect(transformShareData([{ type: "message", data: {} as any }])).toBeNull()
|
||||
expect(transformShareData([{ type: "session", data: { id: "s" } as any }])).toBeNull() // no messages
|
||||
})
|
||||
|
||||
test("formats ingest bootstrap warning", () => {
|
||||
expect(ingestBootstrapWarning("session-123", new Error("network failed"))).toContain("session-123")
|
||||
expect(ingestBootstrapWarning("session-123", new Error("network failed"))).toContain("network failed")
|
||||
expect(ingestBootstrapWarning("session-123", "oops")).toContain("oops")
|
||||
})
|
||||
|
||||
test("bootstrapImportedSessionIngest runs bootstrap and does not warn on success", async () => {
|
||||
const calls: string[] = []
|
||||
const warnings: string[] = []
|
||||
|
||||
await bootstrapImportedSessionIngest("session-success", {
|
||||
bootstrap: async (sessionId) => {
|
||||
calls.push(sessionId)
|
||||
},
|
||||
warn: (message) => warnings.push(message),
|
||||
})
|
||||
|
||||
expect(calls).toEqual(["session-success"])
|
||||
expect(warnings).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("bootstrapImportedSessionIngest warns and continues on failure", async () => {
|
||||
const warnings: string[] = []
|
||||
|
||||
await expect(
|
||||
bootstrapImportedSessionIngest("session-fail", {
|
||||
bootstrap: async () => {
|
||||
throw new Error("boom")
|
||||
},
|
||||
warn: (message) => warnings.push(message),
|
||||
}),
|
||||
).resolves.toBeUndefined()
|
||||
|
||||
expect(warnings).toHaveLength(1)
|
||||
expect(warnings[0]).toContain("session-fail")
|
||||
expect(warnings[0]).toContain("boom")
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user