getmnemo-migrate 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/dist/adapters/base.d.ts +34 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +7 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/directory.d.ts +9 -0
- package/dist/adapters/directory.d.ts.map +1 -0
- package/dist/adapters/directory.js +69 -0
- package/dist/adapters/directory.js.map +1 -0
- package/dist/adapters/index.d.ts +7 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +47 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/letta.d.ts +12 -0
- package/dist/adapters/letta.d.ts.map +1 -0
- package/dist/adapters/letta.js +91 -0
- package/dist/adapters/letta.js.map +1 -0
- package/dist/adapters/mem0.d.ts +12 -0
- package/dist/adapters/mem0.d.ts.map +1 -0
- package/dist/adapters/mem0.js +62 -0
- package/dist/adapters/mem0.js.map +1 -0
- package/dist/adapters/raw-csv.d.ts +9 -0
- package/dist/adapters/raw-csv.d.ts.map +1 -0
- package/dist/adapters/raw-csv.js +34 -0
- package/dist/adapters/raw-csv.js.map +1 -0
- package/dist/adapters/raw-json.d.ts +10 -0
- package/dist/adapters/raw-json.d.ts.map +1 -0
- package/dist/adapters/raw-json.js +35 -0
- package/dist/adapters/raw-json.js.map +1 -0
- package/dist/adapters/raw-jsonl.d.ts +11 -0
- package/dist/adapters/raw-jsonl.d.ts.map +1 -0
- package/dist/adapters/raw-jsonl.js +52 -0
- package/dist/adapters/raw-jsonl.js.map +1 -0
- package/dist/adapters/record-mapping.d.ts +6 -0
- package/dist/adapters/record-mapping.d.ts.map +1 -0
- package/dist/adapters/record-mapping.js +59 -0
- package/dist/adapters/record-mapping.js.map +1 -0
- package/dist/adapters/supermemory.d.ts +12 -0
- package/dist/adapters/supermemory.d.ts.map +1 -0
- package/dist/adapters/supermemory.js +58 -0
- package/dist/adapters/supermemory.js.map +1 -0
- package/dist/adapters/text-file.d.ts +9 -0
- package/dist/adapters/text-file.d.ts.map +1 -0
- package/dist/adapters/text-file.js +28 -0
- package/dist/adapters/text-file.js.map +1 -0
- package/dist/adapters/zep.d.ts +12 -0
- package/dist/adapters/zep.d.ts.map +1 -0
- package/dist/adapters/zep.js +79 -0
- package/dist/adapters/zep.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +220 -0
- package/dist/cli.js.map +1 -0
- package/dist/lib/jobs.d.ts +44 -0
- package/dist/lib/jobs.d.ts.map +1 -0
- package/dist/lib/jobs.js +146 -0
- package/dist/lib/jobs.js.map +1 -0
- package/dist/lib/migrator.d.ts +24 -0
- package/dist/lib/migrator.d.ts.map +1 -0
- package/dist/lib/migrator.js +188 -0
- package/dist/lib/migrator.js.map +1 -0
- package/dist/lib/mnemo-writer.d.ts +42 -0
- package/dist/lib/mnemo-writer.d.ts.map +1 -0
- package/dist/lib/mnemo-writer.js +155 -0
- package/dist/lib/mnemo-writer.js.map +1 -0
- package/docs/pilot-integration.md +81 -0
- package/package.json +59 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command, Option } from "commander";
|
|
3
|
+
import cliProgress from "cli-progress";
|
|
4
|
+
import kleur from "kleur";
|
|
5
|
+
import ora from "ora";
|
|
6
|
+
import { ADAPTER_NAMES, createAdapter, } from "./adapters/index.js";
|
|
7
|
+
import { listJobs, readJob, requestCancellation, writeJob, } from "./lib/jobs.js";
|
|
8
|
+
import { migrate, planMigration, reconcileMigration } from "./lib/migrator.js";
|
|
9
|
+
import { MnemoWriter } from "./lib/mnemo-writer.js";
|
|
10
|
+
const VERSION = "0.1.0";
|
|
11
|
+
function parsePositiveInt(value, fallback, name) {
|
|
12
|
+
if (value === undefined)
|
|
13
|
+
return fallback;
|
|
14
|
+
const parsed = Number(value);
|
|
15
|
+
if (!Number.isInteger(parsed) || parsed < 1)
|
|
16
|
+
throw new Error(`${name} must be a positive integer`);
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
function buildAdapterConfig(opts) {
|
|
20
|
+
return {
|
|
21
|
+
...(opts.file !== undefined ? { file: opts.file } : {}),
|
|
22
|
+
...(opts.map !== undefined ? { map: opts.map } : {}),
|
|
23
|
+
...(opts.user !== undefined ? { user: opts.user } : {}),
|
|
24
|
+
...(opts.baseUrl !== undefined ? { baseUrl: opts.baseUrl } : {}),
|
|
25
|
+
...(opts.pageSize !== undefined
|
|
26
|
+
? { pageSize: parsePositiveInt(opts.pageSize, 100, "--page-size") }
|
|
27
|
+
: {}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function mergeAdapterConfig(saved, opts) {
|
|
31
|
+
return { ...saved, ...buildAdapterConfig(opts) };
|
|
32
|
+
}
|
|
33
|
+
function parseLane(value, fallback) {
|
|
34
|
+
const lane = value ?? fallback;
|
|
35
|
+
if (lane !== "memory" && lane !== "document") {
|
|
36
|
+
throw new Error('--lane must be either "memory" or "document"');
|
|
37
|
+
}
|
|
38
|
+
return lane;
|
|
39
|
+
}
|
|
40
|
+
function requireContainer(value) {
|
|
41
|
+
const container = value ?? process.env.GETMNEMO_CONTAINER_TAG;
|
|
42
|
+
if (!container)
|
|
43
|
+
throw new Error("--container or GETMNEMO_CONTAINER_TAG is required");
|
|
44
|
+
return container;
|
|
45
|
+
}
|
|
46
|
+
function getWriter() {
|
|
47
|
+
const apiKey = process.env.GETMNEMO_API_KEY;
|
|
48
|
+
const workspaceId = process.env.GETMNEMO_WORKSPACE_ID;
|
|
49
|
+
if (!apiKey || !workspaceId) {
|
|
50
|
+
throw new Error("GETMNEMO_API_KEY and GETMNEMO_WORKSPACE_ID must be set");
|
|
51
|
+
}
|
|
52
|
+
return new MnemoWriter({
|
|
53
|
+
apiKey,
|
|
54
|
+
workspaceId,
|
|
55
|
+
apiUrl: process.env.GETMNEMO_API_URL,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function addAdapterOptions(command, includeDefaults) {
|
|
59
|
+
command
|
|
60
|
+
.option("--file <path>", "source file or directory")
|
|
61
|
+
.option("--map <spec>", "field mapping such as content=body,id=record_id")
|
|
62
|
+
.option("--user <id>", "source user id (required by some providers)")
|
|
63
|
+
.option("--base-url <url>", "override provider base URL")
|
|
64
|
+
.option("--page-size <n>", "source page-size hint", includeDefaults ? "100" : undefined);
|
|
65
|
+
return command;
|
|
66
|
+
}
|
|
67
|
+
function addRunOptions(command, includeDefaults) {
|
|
68
|
+
addAdapterOptions(command, includeDefaults)
|
|
69
|
+
.option("--concurrency <n>", "maximum concurrent writes", includeDefaults ? "5" : undefined)
|
|
70
|
+
.option("--container <tag>", "destination container tag")
|
|
71
|
+
.addOption(new Option("--lane <lane>", "memory or document").choices(["memory", "document"]))
|
|
72
|
+
.option("--no-wait", "return after document jobs are accepted")
|
|
73
|
+
.option("--wait-timeout <seconds>", "maximum ingestion wait", includeDefaults ? "86400" : undefined);
|
|
74
|
+
return command;
|
|
75
|
+
}
|
|
76
|
+
function startProgress(source, total) {
|
|
77
|
+
const bar = new cliProgress.SingleBar({
|
|
78
|
+
format: `${kleur.cyan("import")} ${source} |{bar}| {value}/{total} handled ({failed} failed) | ETA: {eta_formatted}`,
|
|
79
|
+
hideCursor: true,
|
|
80
|
+
}, cliProgress.Presets.shades_classic);
|
|
81
|
+
bar.start(total ?? 0, 0, { failed: 0 });
|
|
82
|
+
return bar;
|
|
83
|
+
}
|
|
84
|
+
function printSummary(state) {
|
|
85
|
+
process.stdout.write(`\n${kleur.green("✓")} job ${kleur.bold(state.id)}: ${state.status}\n` +
|
|
86
|
+
` records written: ${state.written}\n` +
|
|
87
|
+
` records reused: ${state.reused}\n` +
|
|
88
|
+
` write failures: ${state.failed}\n` +
|
|
89
|
+
` jobs completed: ${state.remoteCompleted}\n` +
|
|
90
|
+
` jobs failed: ${state.remoteFailed}\n` +
|
|
91
|
+
` jobs pending: ${state.remotePending}\n`);
|
|
92
|
+
}
|
|
93
|
+
export function buildCli() {
|
|
94
|
+
const program = new Command();
|
|
95
|
+
program
|
|
96
|
+
.name("getmnemo-migrate")
|
|
97
|
+
.description(`${kleur.cyan("Mnemo migrate")} — import external memories and customer exports.`)
|
|
98
|
+
.version(VERSION, "-v, --version")
|
|
99
|
+
.showHelpAfterError("(add --help for additional information)");
|
|
100
|
+
const from = program.command("from <source>").description(`import from ${ADAPTER_NAMES.join(", ")}`);
|
|
101
|
+
addRunOptions(from, true).action(async (source, opts) => {
|
|
102
|
+
const adapterConfig = buildAdapterConfig(opts);
|
|
103
|
+
const adapter = createAdapter(source, adapterConfig);
|
|
104
|
+
const lane = parseLane(opts.lane, adapter.defaultLane);
|
|
105
|
+
const containerTag = requireContainer(opts.container);
|
|
106
|
+
const writer = getWriter();
|
|
107
|
+
const total = await adapter.count();
|
|
108
|
+
const bar = startProgress(source, total);
|
|
109
|
+
let state = await migrate(adapter, writer, {
|
|
110
|
+
concurrency: parsePositiveInt(opts.concurrency, 5, "--concurrency"),
|
|
111
|
+
lane,
|
|
112
|
+
containerTag,
|
|
113
|
+
adapterConfig,
|
|
114
|
+
onProgress: (progress) => {
|
|
115
|
+
if (progress.total !== null && bar.getTotal() !== progress.total)
|
|
116
|
+
bar.setTotal(progress.total);
|
|
117
|
+
bar.update(progress.written + progress.failed, { failed: progress.failed });
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
bar.stop();
|
|
121
|
+
if (lane === "document" && opts.wait && state.status !== "paused") {
|
|
122
|
+
state = await reconcileMigration(writer, state, parsePositiveInt(opts.waitTimeout, 86_400, "--wait-timeout") * 1000);
|
|
123
|
+
}
|
|
124
|
+
printSummary(state);
|
|
125
|
+
});
|
|
126
|
+
const plan = program.command("plan").description("preview an import without writing");
|
|
127
|
+
const planFrom = plan.command("from <source>").description("count and sample source records");
|
|
128
|
+
addAdapterOptions(planFrom, true)
|
|
129
|
+
.option("--sample <n>", "sample size", "5")
|
|
130
|
+
.action(async (source, opts) => {
|
|
131
|
+
const adapter = createAdapter(source, buildAdapterConfig(opts));
|
|
132
|
+
const spinner = ora(`Inspecting ${source}...`).start();
|
|
133
|
+
const result = await planMigration(adapter, parsePositiveInt(opts.sample, 5, "--sample"));
|
|
134
|
+
spinner.stop();
|
|
135
|
+
process.stdout.write(`${kleur.cyan("source:")} ${source}\n`);
|
|
136
|
+
process.stdout.write(`${kleur.cyan("lane:")} ${adapter.defaultLane}\n`);
|
|
137
|
+
process.stdout.write(`${kleur.cyan("total:")} ${result.total ?? "unknown"}\n`);
|
|
138
|
+
process.stdout.write(`${kleur.cyan("sample:")}\n`);
|
|
139
|
+
for (const record of result.sample) {
|
|
140
|
+
const preview = record.content.length > 120 ? `${record.content.slice(0, 119)}…` : record.content;
|
|
141
|
+
process.stdout.write(` - ${kleur.dim(record.sourceId ?? "content hash")} ${preview}\n`);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
const resume = program.command("resume <jobId>").description("resume an interrupted or paused import");
|
|
145
|
+
addRunOptions(resume, false).action(async (jobId, opts) => {
|
|
146
|
+
const existing = await readJob(jobId);
|
|
147
|
+
const adapterConfig = mergeAdapterConfig(existing.adapterConfig, opts);
|
|
148
|
+
const adapter = createAdapter(existing.source, adapterConfig);
|
|
149
|
+
const lane = parseLane(opts.lane, existing.lane);
|
|
150
|
+
const containerTag = opts.container ?? existing.containerTag;
|
|
151
|
+
const writer = getWriter();
|
|
152
|
+
const bar = startProgress(existing.source, existing.total ?? null);
|
|
153
|
+
bar.update(existing.written + existing.failed, { failed: existing.failed });
|
|
154
|
+
let state = await migrate(adapter, writer, {
|
|
155
|
+
concurrency: parsePositiveInt(opts.concurrency, 5, "--concurrency"),
|
|
156
|
+
resumeJobId: jobId,
|
|
157
|
+
lane,
|
|
158
|
+
containerTag,
|
|
159
|
+
adapterConfig,
|
|
160
|
+
onProgress: (progress) => bar.update(progress.written + progress.failed, { failed: progress.failed }),
|
|
161
|
+
});
|
|
162
|
+
bar.stop();
|
|
163
|
+
if (lane === "document" && opts.wait && state.status !== "paused") {
|
|
164
|
+
state = await reconcileMigration(writer, state, parsePositiveInt(opts.waitTimeout, 86_400, "--wait-timeout") * 1000);
|
|
165
|
+
}
|
|
166
|
+
printSummary(state);
|
|
167
|
+
});
|
|
168
|
+
program
|
|
169
|
+
.command("reconcile <jobId>")
|
|
170
|
+
.description("refresh all remote ingestion jobs and print the completion report")
|
|
171
|
+
.option("--wait-timeout <seconds>", "maximum wait", "86400")
|
|
172
|
+
.action(async (jobId, opts) => {
|
|
173
|
+
const state = await reconcileMigration(getWriter(), await readJob(jobId), parsePositiveInt(opts.waitTimeout, 86_400, "--wait-timeout") * 1000);
|
|
174
|
+
printSummary(state);
|
|
175
|
+
});
|
|
176
|
+
program
|
|
177
|
+
.command("status [jobId]")
|
|
178
|
+
.description("show one import report or list recent imports")
|
|
179
|
+
.action(async (jobId) => {
|
|
180
|
+
if (jobId) {
|
|
181
|
+
process.stdout.write(`${JSON.stringify(await readJob(jobId), null, 2)}\n`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const jobs = await listJobs();
|
|
185
|
+
if (jobs.length === 0)
|
|
186
|
+
process.stdout.write("No migration jobs yet.\n");
|
|
187
|
+
for (const job of jobs.slice(0, 20)) {
|
|
188
|
+
process.stdout.write(`${job.id} ${job.status.padEnd(22)} ${job.source.padEnd(12)} ${job.written}/${job.total ?? "?"} written\n`);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
program
|
|
192
|
+
.command("cancel <jobId>")
|
|
193
|
+
.description("request a running import to pause after its current bounded batch")
|
|
194
|
+
.action(async (jobId) => {
|
|
195
|
+
const state = await readJob(jobId);
|
|
196
|
+
if (["completed", "completed_with_errors", "failed"].includes(state.status)) {
|
|
197
|
+
throw new Error(`job ${jobId} is already ${state.status}`);
|
|
198
|
+
}
|
|
199
|
+
await requestCancellation(jobId);
|
|
200
|
+
state.status = "paused";
|
|
201
|
+
await writeJob(state);
|
|
202
|
+
process.stdout.write(`${kleur.yellow("!")} pause requested for ${jobId}\n`);
|
|
203
|
+
});
|
|
204
|
+
return program;
|
|
205
|
+
}
|
|
206
|
+
async function main() {
|
|
207
|
+
try {
|
|
208
|
+
await buildCli().parseAsync(process.argv);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
process.stderr.write(kleur.red(`error: ${error instanceof Error ? error.message : String(error)}\n`));
|
|
212
|
+
process.exitCode = 1;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const isMain = import.meta.url === `file://${process.argv[1]}` ||
|
|
216
|
+
process.argv[1]?.endsWith("cli.js") ||
|
|
217
|
+
process.argv[1]?.endsWith("cli.ts");
|
|
218
|
+
if (isMain)
|
|
219
|
+
void main();
|
|
220
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AdapterConfig, ImportLane } from "../adapters/index.js";
|
|
2
|
+
export type JobStatus = "running" | "submitted" | "completed" | "completed_with_errors" | "failed" | "paused";
|
|
3
|
+
export interface JobState {
|
|
4
|
+
id: string;
|
|
5
|
+
source: string;
|
|
6
|
+
lane: ImportLane;
|
|
7
|
+
containerTag: string;
|
|
8
|
+
adapterConfig: AdapterConfig;
|
|
9
|
+
status: JobStatus;
|
|
10
|
+
startedAt: string;
|
|
11
|
+
updatedAt: string;
|
|
12
|
+
finishedAt?: string;
|
|
13
|
+
total?: number | null;
|
|
14
|
+
written: number;
|
|
15
|
+
reused: number;
|
|
16
|
+
failed: number;
|
|
17
|
+
remoteCompleted: number;
|
|
18
|
+
remoteFailed: number;
|
|
19
|
+
remotePending: number;
|
|
20
|
+
/** Backward compatibility with jobs written by 0.1.0. */
|
|
21
|
+
processedIds?: string[];
|
|
22
|
+
lastError?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface FailedRecord {
|
|
25
|
+
sourceId: string;
|
|
26
|
+
error: string;
|
|
27
|
+
occurredAt: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function jobsDir(): string;
|
|
30
|
+
export declare function jobPath(id: string): string;
|
|
31
|
+
export declare function newJobId(): string;
|
|
32
|
+
export declare function readJob(id: string): Promise<JobState>;
|
|
33
|
+
export declare function writeJob(state: JobState): Promise<void>;
|
|
34
|
+
export declare function appendProcessedId(jobId: string, sourceId: string): Promise<void>;
|
|
35
|
+
export declare function loadProcessedIds(jobId: string, legacy?: string[]): Promise<Set<string>>;
|
|
36
|
+
export declare function appendRemoteJob(jobId: string, remoteJobId: string): Promise<void>;
|
|
37
|
+
export declare function loadRemoteJobs(jobId: string): Promise<string[]>;
|
|
38
|
+
export declare function appendFailure(jobId: string, failure: FailedRecord): Promise<void>;
|
|
39
|
+
export declare function requestCancellation(jobId: string): Promise<void>;
|
|
40
|
+
export declare function clearCancellation(jobId: string): Promise<void>;
|
|
41
|
+
export declare function cancellationRequested(jobId: string): Promise<boolean>;
|
|
42
|
+
export declare function acquireSourceLock(lockKey: string): Promise<() => Promise<void>>;
|
|
43
|
+
export declare function listJobs(): Promise<JobState[]>;
|
|
44
|
+
//# sourceMappingURL=jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/lib/jobs.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEtE,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,WAAW,GACX,WAAW,GACX,uBAAuB,GACvB,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,EAAE,SAAS,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE1C;AAMD,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAG3D;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7D;AAOD,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtF;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAQjG;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvF;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQrE;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvF;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMpE;AAED,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ3E;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAqCrF;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAcpD"}
|
package/dist/lib/jobs.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
function resolveJobsDir() {
|
|
6
|
+
return process.env.GETMNEMO_MIGRATION_DIR ?? join(homedir(), ".getmnemo", "migrations");
|
|
7
|
+
}
|
|
8
|
+
export function jobsDir() {
|
|
9
|
+
return resolveJobsDir();
|
|
10
|
+
}
|
|
11
|
+
export function jobPath(id) {
|
|
12
|
+
return join(resolveJobsDir(), `${id}.json`);
|
|
13
|
+
}
|
|
14
|
+
function artifactPath(id, suffix) {
|
|
15
|
+
return join(resolveJobsDir(), `${id}.${suffix}`);
|
|
16
|
+
}
|
|
17
|
+
export function newJobId() {
|
|
18
|
+
return `mig_${randomUUID()}`;
|
|
19
|
+
}
|
|
20
|
+
export async function readJob(id) {
|
|
21
|
+
const raw = await fs.readFile(jobPath(id), "utf8");
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
}
|
|
24
|
+
export async function writeJob(state) {
|
|
25
|
+
const path = jobPath(state.id);
|
|
26
|
+
await fs.mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
27
|
+
const next = { ...state, updatedAt: new Date().toISOString() };
|
|
28
|
+
const tmp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
29
|
+
await fs.writeFile(tmp, JSON.stringify(next, null, 2) + "\n", { mode: 0o600 });
|
|
30
|
+
await fs.rename(tmp, path);
|
|
31
|
+
}
|
|
32
|
+
async function appendLine(path, line) {
|
|
33
|
+
await fs.mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
34
|
+
await fs.appendFile(path, line + "\n", { mode: 0o600 });
|
|
35
|
+
}
|
|
36
|
+
export async function appendProcessedId(jobId, sourceId) {
|
|
37
|
+
await appendLine(artifactPath(jobId, "processed"), sourceId);
|
|
38
|
+
}
|
|
39
|
+
export async function loadProcessedIds(jobId, legacy = []) {
|
|
40
|
+
try {
|
|
41
|
+
const raw = await fs.readFile(artifactPath(jobId, "processed"), "utf8");
|
|
42
|
+
return new Set([...legacy, ...raw.split("\n").filter(Boolean)]);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error.code === "ENOENT")
|
|
46
|
+
return new Set(legacy);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function appendRemoteJob(jobId, remoteJobId) {
|
|
51
|
+
await appendLine(artifactPath(jobId, "remote-jobs"), remoteJobId);
|
|
52
|
+
}
|
|
53
|
+
export async function loadRemoteJobs(jobId) {
|
|
54
|
+
try {
|
|
55
|
+
const raw = await fs.readFile(artifactPath(jobId, "remote-jobs"), "utf8");
|
|
56
|
+
return [...new Set(raw.split("\n").filter(Boolean))];
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error.code === "ENOENT")
|
|
60
|
+
return [];
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export async function appendFailure(jobId, failure) {
|
|
65
|
+
await appendLine(artifactPath(jobId, "failures.jsonl"), JSON.stringify(failure));
|
|
66
|
+
}
|
|
67
|
+
export async function requestCancellation(jobId) {
|
|
68
|
+
await fs.writeFile(artifactPath(jobId, "cancel"), `${new Date().toISOString()}\n`, { mode: 0o600 });
|
|
69
|
+
}
|
|
70
|
+
export async function clearCancellation(jobId) {
|
|
71
|
+
try {
|
|
72
|
+
await fs.unlink(artifactPath(jobId, "cancel"));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error.code !== "ENOENT")
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function cancellationRequested(jobId) {
|
|
80
|
+
try {
|
|
81
|
+
await fs.access(artifactPath(jobId, "cancel"));
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (error.code === "ENOENT")
|
|
86
|
+
return false;
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export async function acquireSourceLock(lockKey) {
|
|
91
|
+
const safe = createHash("sha256").update(lockKey).digest("hex").slice(0, 16);
|
|
92
|
+
const directory = resolveJobsDir();
|
|
93
|
+
const lockPath = join(directory, `${safe}.lock`);
|
|
94
|
+
await fs.mkdir(directory, { recursive: true, mode: 0o700 });
|
|
95
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
96
|
+
try {
|
|
97
|
+
const handle = await fs.open(lockPath, "wx", 0o600);
|
|
98
|
+
await handle.writeFile(`${process.pid}\n`);
|
|
99
|
+
await handle.close();
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error.code !== "EEXIST")
|
|
104
|
+
throw error;
|
|
105
|
+
const owner = Number.parseInt(await fs.readFile(lockPath, "utf8"), 10);
|
|
106
|
+
let ownerIsAlive = Number.isInteger(owner) && owner > 0;
|
|
107
|
+
if (ownerIsAlive) {
|
|
108
|
+
try {
|
|
109
|
+
process.kill(owner, 0);
|
|
110
|
+
}
|
|
111
|
+
catch (probeError) {
|
|
112
|
+
ownerIsAlive = probeError.code === "EPERM";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (ownerIsAlive || attempt > 0) {
|
|
116
|
+
throw new Error(`another migration is already running for this source (${lockPath})`);
|
|
117
|
+
}
|
|
118
|
+
await fs.unlink(lockPath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return async () => {
|
|
122
|
+
try {
|
|
123
|
+
await fs.unlink(lockPath);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (error.code !== "ENOENT")
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export async function listJobs() {
|
|
132
|
+
const directory = resolveJobsDir();
|
|
133
|
+
try {
|
|
134
|
+
const files = await fs.readdir(directory);
|
|
135
|
+
const jobs = await Promise.all(files
|
|
136
|
+
.filter((file) => /^mig_.+\.json$/.test(file))
|
|
137
|
+
.map(async (file) => JSON.parse(await fs.readFile(join(directory, file), "utf8"))));
|
|
138
|
+
return jobs.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (error.code === "ENOENT")
|
|
142
|
+
return [];
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=jobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/lib/jobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Adapter, AdapterConfig, ImportLane, SourceRecord } from "../adapters/index.js";
|
|
2
|
+
import { type JobState } from "./jobs.js";
|
|
3
|
+
import type { DestinationWriter } from "./mnemo-writer.js";
|
|
4
|
+
export interface MigrateOptions {
|
|
5
|
+
concurrency?: number;
|
|
6
|
+
resumeJobId?: string;
|
|
7
|
+
lane: ImportLane;
|
|
8
|
+
containerTag: string;
|
|
9
|
+
adapterConfig: AdapterConfig;
|
|
10
|
+
onProgress?: (state: {
|
|
11
|
+
written: number;
|
|
12
|
+
reused: number;
|
|
13
|
+
failed: number;
|
|
14
|
+
total: number | null;
|
|
15
|
+
}) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface PlanResult {
|
|
18
|
+
total: number | null;
|
|
19
|
+
sample: SourceRecord[];
|
|
20
|
+
}
|
|
21
|
+
export declare function planMigration(adapter: Adapter, sampleSize?: number): Promise<PlanResult>;
|
|
22
|
+
export declare function migrate(adapter: Adapter, writer: DestinationWriter, options: MigrateOptions): Promise<JobState>;
|
|
23
|
+
export declare function reconcileMigration(writer: DestinationWriter, input: JobState, timeoutMs: number): Promise<JobState>;
|
|
24
|
+
//# sourceMappingURL=migrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrator.d.ts","sourceRoot":"","sources":["../../src/lib/migrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAYL,KAAK,QAAQ,EACd,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,aAAa,CAAC;IAC7B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;CACzG;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAMD,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,SAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAQzF;AAiDD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,QAAQ,CAAC,CAyFnB;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,CAAC,CAwCnB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { acquireSourceLock, appendFailure, appendProcessedId, appendRemoteJob, cancellationRequested, clearCancellation, loadProcessedIds, loadRemoteJobs, newJobId, readJob, writeJob, } from "./jobs.js";
|
|
2
|
+
import { sourceIdentity } from "./mnemo-writer.js";
|
|
3
|
+
export async function planMigration(adapter, sampleSize = 5) {
|
|
4
|
+
const total = await adapter.count();
|
|
5
|
+
const sample = [];
|
|
6
|
+
for await (const record of adapter.iterate()) {
|
|
7
|
+
sample.push(record);
|
|
8
|
+
if (sample.length >= sampleSize)
|
|
9
|
+
break;
|
|
10
|
+
}
|
|
11
|
+
return { total, sample };
|
|
12
|
+
}
|
|
13
|
+
async function processBatch(records, adapter, writer, options) {
|
|
14
|
+
return Promise.all(records.map(async (record) => {
|
|
15
|
+
const sourceId = sourceIdentity(record);
|
|
16
|
+
try {
|
|
17
|
+
const result = await writer.write(record, {
|
|
18
|
+
lane: options.lane,
|
|
19
|
+
source: adapter.name,
|
|
20
|
+
containerTag: options.containerTag,
|
|
21
|
+
});
|
|
22
|
+
return { kind: "success", sourceId, ...result };
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return {
|
|
26
|
+
kind: "failure",
|
|
27
|
+
sourceId,
|
|
28
|
+
error: error instanceof Error ? error.message : String(error),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
function newState(adapter, options, total) {
|
|
34
|
+
return {
|
|
35
|
+
id: newJobId(),
|
|
36
|
+
source: adapter.name,
|
|
37
|
+
lane: options.lane,
|
|
38
|
+
containerTag: options.containerTag,
|
|
39
|
+
adapterConfig: options.adapterConfig,
|
|
40
|
+
status: "running",
|
|
41
|
+
startedAt: new Date().toISOString(),
|
|
42
|
+
updatedAt: new Date().toISOString(),
|
|
43
|
+
total,
|
|
44
|
+
written: 0,
|
|
45
|
+
reused: 0,
|
|
46
|
+
failed: 0,
|
|
47
|
+
remoteCompleted: 0,
|
|
48
|
+
remoteFailed: 0,
|
|
49
|
+
remotePending: 0,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function migrate(adapter, writer, options) {
|
|
53
|
+
const requestedConcurrency = options.concurrency ?? 5;
|
|
54
|
+
const concurrency = Number.isFinite(requestedConcurrency)
|
|
55
|
+
? Math.max(1, Math.min(25, Math.floor(requestedConcurrency)))
|
|
56
|
+
: 5;
|
|
57
|
+
let state = options.resumeJobId
|
|
58
|
+
? await readJob(options.resumeJobId)
|
|
59
|
+
: newState(adapter, options, await adapter.count());
|
|
60
|
+
if (options.resumeJobId) {
|
|
61
|
+
if (state.source !== adapter.name)
|
|
62
|
+
throw new Error("resume source does not match the original job");
|
|
63
|
+
if (state.lane !== options.lane)
|
|
64
|
+
throw new Error("resume lane does not match the original job");
|
|
65
|
+
if (state.containerTag !== options.containerTag) {
|
|
66
|
+
throw new Error("resume container does not match the original job");
|
|
67
|
+
}
|
|
68
|
+
state.status = "running";
|
|
69
|
+
state.finishedAt = undefined;
|
|
70
|
+
state.failed = 0;
|
|
71
|
+
state.lastError = undefined;
|
|
72
|
+
}
|
|
73
|
+
const lockKey = [adapter.name, options.containerTag, options.adapterConfig.file ?? "remote"].join(":");
|
|
74
|
+
const releaseLock = await acquireSourceLock(lockKey);
|
|
75
|
+
await clearCancellation(state.id);
|
|
76
|
+
await writeJob(state);
|
|
77
|
+
const seen = await loadProcessedIds(state.id, state.processedIds ?? []);
|
|
78
|
+
let batch = [];
|
|
79
|
+
const commitBatch = async () => {
|
|
80
|
+
if (batch.length === 0)
|
|
81
|
+
return;
|
|
82
|
+
const outcomes = await processBatch(batch, adapter, writer, options);
|
|
83
|
+
batch = [];
|
|
84
|
+
for (const outcome of outcomes) {
|
|
85
|
+
if (outcome.kind === "success") {
|
|
86
|
+
state.written += 1;
|
|
87
|
+
if (outcome.reused)
|
|
88
|
+
state.reused += 1;
|
|
89
|
+
seen.add(outcome.sourceId);
|
|
90
|
+
await appendProcessedId(state.id, outcome.sourceId);
|
|
91
|
+
for (const remoteJobId of outcome.remoteJobIds) {
|
|
92
|
+
await appendRemoteJob(state.id, remoteJobId);
|
|
93
|
+
state.remotePending += 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
state.failed += 1;
|
|
98
|
+
state.lastError = outcome.error;
|
|
99
|
+
await appendFailure(state.id, {
|
|
100
|
+
sourceId: outcome.sourceId,
|
|
101
|
+
error: outcome.error,
|
|
102
|
+
occurredAt: new Date().toISOString(),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
options.onProgress?.({
|
|
106
|
+
written: state.written,
|
|
107
|
+
reused: state.reused,
|
|
108
|
+
failed: state.failed,
|
|
109
|
+
total: state.total ?? null,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
await writeJob(state);
|
|
113
|
+
};
|
|
114
|
+
try {
|
|
115
|
+
for await (const record of adapter.iterate()) {
|
|
116
|
+
if (await cancellationRequested(state.id)) {
|
|
117
|
+
await commitBatch();
|
|
118
|
+
state.status = "paused";
|
|
119
|
+
await writeJob(state);
|
|
120
|
+
return state;
|
|
121
|
+
}
|
|
122
|
+
const id = sourceIdentity(record);
|
|
123
|
+
if (seen.has(id))
|
|
124
|
+
continue;
|
|
125
|
+
batch.push(record);
|
|
126
|
+
if (batch.length >= concurrency)
|
|
127
|
+
await commitBatch();
|
|
128
|
+
}
|
|
129
|
+
await commitBatch();
|
|
130
|
+
state.status = options.lane === "document" ? "submitted" : state.failed > 0 ? "completed_with_errors" : "completed";
|
|
131
|
+
state.finishedAt = new Date().toISOString();
|
|
132
|
+
await writeJob(state);
|
|
133
|
+
return state;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
state.status = "failed";
|
|
137
|
+
state.finishedAt = new Date().toISOString();
|
|
138
|
+
state.lastError = error instanceof Error ? error.message : String(error);
|
|
139
|
+
await writeJob(state);
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
await releaseLock();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export async function reconcileMigration(writer, input, timeoutMs) {
|
|
147
|
+
const state = { ...input };
|
|
148
|
+
const pending = new Set(await loadRemoteJobs(state.id));
|
|
149
|
+
const deadline = Date.now() + timeoutMs;
|
|
150
|
+
let completed = 0;
|
|
151
|
+
let failed = 0;
|
|
152
|
+
while (pending.size > 0 && Date.now() < deadline) {
|
|
153
|
+
const ids = [...pending];
|
|
154
|
+
for (let offset = 0; offset < ids.length; offset += 50) {
|
|
155
|
+
const jobs = await writer.getJobs(ids.slice(offset, offset + 50));
|
|
156
|
+
for (const job of jobs) {
|
|
157
|
+
if (job.status === "completed") {
|
|
158
|
+
pending.delete(job.id);
|
|
159
|
+
completed += 1;
|
|
160
|
+
}
|
|
161
|
+
else if (job.status === "failed") {
|
|
162
|
+
pending.delete(job.id);
|
|
163
|
+
failed += 1;
|
|
164
|
+
state.lastError = job.error?.message ?? `ingestion job ${job.id} failed`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
state.remoteCompleted = completed;
|
|
169
|
+
state.remoteFailed = failed;
|
|
170
|
+
state.remotePending = pending.size;
|
|
171
|
+
await writeJob(state);
|
|
172
|
+
if (pending.size > 0)
|
|
173
|
+
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
174
|
+
}
|
|
175
|
+
state.remoteCompleted = completed;
|
|
176
|
+
state.remoteFailed = failed;
|
|
177
|
+
state.remotePending = pending.size;
|
|
178
|
+
if (pending.size === 0) {
|
|
179
|
+
state.status = state.failed + failed > 0 ? "completed_with_errors" : "completed";
|
|
180
|
+
state.finishedAt = new Date().toISOString();
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
state.status = "submitted";
|
|
184
|
+
}
|
|
185
|
+
await writeJob(state);
|
|
186
|
+
return state;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=migrator.js.map
|