openzca 0.1.52 → 0.1.54

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.
@@ -0,0 +1,52 @@
1
+ // src/lib/db-migrations.ts
2
+ import fs from "fs/promises";
3
+ var MIGRATIONS = [
4
+ { version: "001", file: "001-initial-schema.sql" },
5
+ { version: "002", file: "002-add-contacts.sql" },
6
+ { version: "003", file: "003-backfill-contacts.sql" }
7
+ ];
8
+ var CREATE_MIGRATIONS_TABLE_SQL = `
9
+ CREATE TABLE IF NOT EXISTS schema_migrations (
10
+ version TEXT NOT NULL PRIMARY KEY,
11
+ applied_at TEXT NOT NULL
12
+ )
13
+ `;
14
+ function nowIso() {
15
+ return (/* @__PURE__ */ new Date()).toISOString();
16
+ }
17
+ async function readMigrationSql(file) {
18
+ return await fs.readFile(new URL(`./migrations/${file}`, import.meta.url), "utf8");
19
+ }
20
+ function listAppliedVersions(db) {
21
+ const rows = db.prepare("SELECT version FROM schema_migrations").all();
22
+ return new Set(
23
+ rows.map((row) => typeof row.version === "string" ? row.version : "").filter(Boolean)
24
+ );
25
+ }
26
+ async function runMigrations(db) {
27
+ db.exec(CREATE_MIGRATIONS_TABLE_SQL);
28
+ const applied = listAppliedVersions(db);
29
+ for (const migration of MIGRATIONS) {
30
+ if (applied.has(migration.version)) {
31
+ continue;
32
+ }
33
+ const sql = await readMigrationSql(migration.file);
34
+ db.exec("BEGIN");
35
+ try {
36
+ db.exec(sql);
37
+ db.prepare(
38
+ "INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)"
39
+ ).run(migration.version, nowIso());
40
+ db.exec("COMMIT");
41
+ } catch (error) {
42
+ try {
43
+ db.exec("ROLLBACK");
44
+ } catch {
45
+ }
46
+ throw error;
47
+ }
48
+ }
49
+ }
50
+ export {
51
+ runMigrations
52
+ };
package/dist/db-worker.js CHANGED
@@ -1,150 +1,5 @@
1
1
  // src/lib/db-worker.ts
2
2
  import { parentPort, workerData } from "worker_threads";
3
- var INIT_SQL = `
4
- PRAGMA journal_mode = WAL;
5
- PRAGMA synchronous = FULL;
6
- PRAGMA busy_timeout = 5000;
7
- PRAGMA foreign_keys = ON;
8
-
9
- CREATE TABLE IF NOT EXISTS threads (
10
- profile TEXT NOT NULL,
11
- scope_thread_id TEXT NOT NULL,
12
- raw_thread_id TEXT NOT NULL,
13
- thread_type TEXT NOT NULL,
14
- peer_id TEXT,
15
- title TEXT,
16
- is_pinned INTEGER NOT NULL DEFAULT 0,
17
- is_hidden INTEGER NOT NULL DEFAULT 0,
18
- is_archived INTEGER NOT NULL DEFAULT 0,
19
- raw_json TEXT,
20
- created_at TEXT NOT NULL,
21
- updated_at TEXT NOT NULL,
22
- PRIMARY KEY (profile, scope_thread_id)
23
- );
24
-
25
- CREATE TABLE IF NOT EXISTS thread_members (
26
- profile TEXT NOT NULL,
27
- scope_thread_id TEXT NOT NULL,
28
- user_id TEXT NOT NULL,
29
- display_name TEXT,
30
- zalo_name TEXT,
31
- avatar TEXT,
32
- account_status INTEGER,
33
- member_type INTEGER,
34
- raw_json TEXT,
35
- snapshot_at_ms INTEGER NOT NULL,
36
- created_at TEXT NOT NULL,
37
- updated_at TEXT NOT NULL,
38
- PRIMARY KEY (profile, scope_thread_id, user_id)
39
- );
40
-
41
- CREATE TABLE IF NOT EXISTS friends (
42
- profile TEXT NOT NULL,
43
- user_id TEXT NOT NULL,
44
- display_name TEXT,
45
- zalo_name TEXT,
46
- avatar TEXT,
47
- account_status INTEGER,
48
- raw_json TEXT,
49
- created_at TEXT NOT NULL,
50
- updated_at TEXT NOT NULL,
51
- PRIMARY KEY (profile, user_id)
52
- );
53
-
54
- CREATE TABLE IF NOT EXISTS self_profiles (
55
- profile TEXT NOT NULL,
56
- user_id TEXT NOT NULL,
57
- display_name TEXT,
58
- info_json TEXT,
59
- created_at TEXT NOT NULL,
60
- updated_at TEXT NOT NULL,
61
- PRIMARY KEY (profile)
62
- );
63
-
64
- CREATE TABLE IF NOT EXISTS messages (
65
- profile TEXT NOT NULL,
66
- message_uid TEXT NOT NULL,
67
- scope_thread_id TEXT NOT NULL,
68
- raw_thread_id TEXT NOT NULL,
69
- thread_type TEXT NOT NULL,
70
- msg_id TEXT,
71
- cli_msg_id TEXT,
72
- action_id TEXT,
73
- sender_id TEXT,
74
- sender_name TEXT,
75
- to_id TEXT,
76
- timestamp_ms INTEGER NOT NULL,
77
- msg_type TEXT,
78
- content_text TEXT,
79
- content_json TEXT,
80
- quote_msg_id TEXT,
81
- quote_cli_msg_id TEXT,
82
- quote_owner_id TEXT,
83
- quote_text TEXT,
84
- source TEXT NOT NULL,
85
- raw_message_json TEXT,
86
- raw_payload_json TEXT,
87
- created_at TEXT NOT NULL,
88
- updated_at TEXT NOT NULL,
89
- PRIMARY KEY (profile, message_uid)
90
- );
91
-
92
- CREATE TABLE IF NOT EXISTS message_media (
93
- profile TEXT NOT NULL,
94
- message_uid TEXT NOT NULL,
95
- item_index INTEGER NOT NULL,
96
- media_kind TEXT,
97
- media_url TEXT,
98
- media_path TEXT,
99
- media_type TEXT,
100
- raw_json TEXT,
101
- created_at TEXT NOT NULL,
102
- updated_at TEXT NOT NULL,
103
- PRIMARY KEY (profile, message_uid, item_index)
104
- );
105
-
106
- CREATE TABLE IF NOT EXISTS message_mentions (
107
- profile TEXT NOT NULL,
108
- message_uid TEXT NOT NULL,
109
- item_index INTEGER NOT NULL,
110
- target_user_id TEXT NOT NULL,
111
- pos INTEGER,
112
- len INTEGER,
113
- mention_type INTEGER,
114
- raw_json TEXT,
115
- created_at TEXT NOT NULL,
116
- updated_at TEXT NOT NULL,
117
- PRIMARY KEY (profile, message_uid, item_index)
118
- );
119
-
120
- CREATE TABLE IF NOT EXISTS sync_state (
121
- profile TEXT NOT NULL,
122
- scope TEXT NOT NULL,
123
- scope_thread_id TEXT NOT NULL,
124
- thread_type TEXT NOT NULL,
125
- status TEXT NOT NULL,
126
- completeness TEXT,
127
- cursor TEXT,
128
- last_sync_at TEXT,
129
- error TEXT,
130
- created_at TEXT NOT NULL,
131
- updated_at TEXT NOT NULL,
132
- PRIMARY KEY (profile, scope)
133
- );
134
-
135
- CREATE INDEX IF NOT EXISTS idx_messages_thread_time
136
- ON messages (profile, scope_thread_id, timestamp_ms DESC);
137
- CREATE INDEX IF NOT EXISTS idx_messages_msg_id
138
- ON messages (profile, msg_id);
139
- CREATE INDEX IF NOT EXISTS idx_messages_cli_msg_id
140
- ON messages (profile, cli_msg_id);
141
- CREATE INDEX IF NOT EXISTS idx_threads_type
142
- ON threads (profile, thread_type, updated_at DESC);
143
- CREATE INDEX IF NOT EXISTS idx_members_thread
144
- ON thread_members (profile, scope_thread_id);
145
- CREATE INDEX IF NOT EXISTS idx_friends_name
146
- ON friends (profile, display_name, zalo_name, user_id);
147
- `;
148
3
  if (!parentPort) {
149
4
  throw new Error("DB worker requires parentPort");
150
5
  }
@@ -180,6 +35,12 @@ async function loadSqliteModule() {
180
35
  process.emitWarning = originalEmitWarning;
181
36
  }
182
37
  }
38
+ async function loadMigrationModule() {
39
+ const currentUrl = new URL(import.meta.url);
40
+ const specifier = currentUrl.pathname.endsWith("/src/lib/db-worker.ts") ? new URL("./db-migrations.ts", currentUrl).href : new URL("./db-migrations.js", currentUrl).href;
41
+ const importDynamic = new Function("specifier", "return import(specifier);");
42
+ return await importDynamic(specifier);
43
+ }
183
44
  function setDefensiveMode(db) {
184
45
  const maybeDb = db;
185
46
  if (typeof maybeDb.enableDefensive === "function") {
@@ -197,9 +58,16 @@ function allStatement(db, statement) {
197
58
  }
198
59
  async function main() {
199
60
  const { DatabaseSync } = await loadSqliteModule();
61
+ const { runMigrations } = await loadMigrationModule();
200
62
  const { filename } = workerData;
201
63
  const db = new DatabaseSync(filename);
202
- db.exec(INIT_SQL);
64
+ db.exec(`
65
+ PRAGMA journal_mode = WAL;
66
+ PRAGMA synchronous = FULL;
67
+ PRAGMA busy_timeout = 5000;
68
+ PRAGMA foreign_keys = ON;
69
+ `);
70
+ await runMigrations(db);
203
71
  setDefensiveMode(db);
204
72
  port.postMessage({ type: "ready" });
205
73
  port.on("message", (message) => {
@@ -0,0 +1,138 @@
1
+ CREATE TABLE IF NOT EXISTS threads (
2
+ profile TEXT NOT NULL,
3
+ scope_thread_id TEXT NOT NULL,
4
+ raw_thread_id TEXT NOT NULL,
5
+ thread_type TEXT NOT NULL,
6
+ peer_id TEXT,
7
+ title TEXT,
8
+ is_pinned INTEGER NOT NULL DEFAULT 0,
9
+ is_hidden INTEGER NOT NULL DEFAULT 0,
10
+ is_archived INTEGER NOT NULL DEFAULT 0,
11
+ raw_json TEXT,
12
+ created_at TEXT NOT NULL,
13
+ updated_at TEXT NOT NULL,
14
+ PRIMARY KEY (profile, scope_thread_id)
15
+ );
16
+
17
+ CREATE TABLE IF NOT EXISTS thread_members (
18
+ profile TEXT NOT NULL,
19
+ scope_thread_id TEXT NOT NULL,
20
+ user_id TEXT NOT NULL,
21
+ display_name TEXT,
22
+ zalo_name TEXT,
23
+ avatar TEXT,
24
+ account_status INTEGER,
25
+ member_type INTEGER,
26
+ raw_json TEXT,
27
+ snapshot_at_ms INTEGER NOT NULL,
28
+ created_at TEXT NOT NULL,
29
+ updated_at TEXT NOT NULL,
30
+ PRIMARY KEY (profile, scope_thread_id, user_id)
31
+ );
32
+
33
+ CREATE TABLE IF NOT EXISTS friends (
34
+ profile TEXT NOT NULL,
35
+ user_id TEXT NOT NULL,
36
+ display_name TEXT,
37
+ zalo_name TEXT,
38
+ avatar TEXT,
39
+ account_status INTEGER,
40
+ raw_json TEXT,
41
+ created_at TEXT NOT NULL,
42
+ updated_at TEXT NOT NULL,
43
+ PRIMARY KEY (profile, user_id)
44
+ );
45
+
46
+ CREATE TABLE IF NOT EXISTS self_profiles (
47
+ profile TEXT NOT NULL,
48
+ user_id TEXT NOT NULL,
49
+ display_name TEXT,
50
+ info_json TEXT,
51
+ created_at TEXT NOT NULL,
52
+ updated_at TEXT NOT NULL,
53
+ PRIMARY KEY (profile)
54
+ );
55
+
56
+ CREATE TABLE IF NOT EXISTS messages (
57
+ profile TEXT NOT NULL,
58
+ message_uid TEXT NOT NULL,
59
+ scope_thread_id TEXT NOT NULL,
60
+ raw_thread_id TEXT NOT NULL,
61
+ thread_type TEXT NOT NULL,
62
+ msg_id TEXT,
63
+ cli_msg_id TEXT,
64
+ action_id TEXT,
65
+ sender_id TEXT,
66
+ sender_name TEXT,
67
+ to_id TEXT,
68
+ timestamp_ms INTEGER NOT NULL,
69
+ msg_type TEXT,
70
+ content_text TEXT,
71
+ content_json TEXT,
72
+ quote_msg_id TEXT,
73
+ quote_cli_msg_id TEXT,
74
+ quote_owner_id TEXT,
75
+ quote_text TEXT,
76
+ source TEXT NOT NULL,
77
+ raw_message_json TEXT,
78
+ raw_payload_json TEXT,
79
+ created_at TEXT NOT NULL,
80
+ updated_at TEXT NOT NULL,
81
+ PRIMARY KEY (profile, message_uid)
82
+ );
83
+
84
+ CREATE TABLE IF NOT EXISTS message_media (
85
+ profile TEXT NOT NULL,
86
+ message_uid TEXT NOT NULL,
87
+ item_index INTEGER NOT NULL,
88
+ media_kind TEXT,
89
+ media_url TEXT,
90
+ media_path TEXT,
91
+ media_type TEXT,
92
+ raw_json TEXT,
93
+ created_at TEXT NOT NULL,
94
+ updated_at TEXT NOT NULL,
95
+ PRIMARY KEY (profile, message_uid, item_index)
96
+ );
97
+
98
+ CREATE TABLE IF NOT EXISTS message_mentions (
99
+ profile TEXT NOT NULL,
100
+ message_uid TEXT NOT NULL,
101
+ item_index INTEGER NOT NULL,
102
+ target_user_id TEXT NOT NULL,
103
+ pos INTEGER,
104
+ len INTEGER,
105
+ mention_type INTEGER,
106
+ raw_json TEXT,
107
+ created_at TEXT NOT NULL,
108
+ updated_at TEXT NOT NULL,
109
+ PRIMARY KEY (profile, message_uid, item_index)
110
+ );
111
+
112
+ CREATE TABLE IF NOT EXISTS sync_state (
113
+ profile TEXT NOT NULL,
114
+ scope TEXT NOT NULL,
115
+ scope_thread_id TEXT NOT NULL,
116
+ thread_type TEXT NOT NULL,
117
+ status TEXT NOT NULL,
118
+ completeness TEXT,
119
+ cursor TEXT,
120
+ last_sync_at TEXT,
121
+ error TEXT,
122
+ created_at TEXT NOT NULL,
123
+ updated_at TEXT NOT NULL,
124
+ PRIMARY KEY (profile, scope)
125
+ );
126
+
127
+ CREATE INDEX IF NOT EXISTS idx_messages_thread_time
128
+ ON messages (profile, scope_thread_id, timestamp_ms DESC);
129
+ CREATE INDEX IF NOT EXISTS idx_messages_msg_id
130
+ ON messages (profile, msg_id);
131
+ CREATE INDEX IF NOT EXISTS idx_messages_cli_msg_id
132
+ ON messages (profile, cli_msg_id);
133
+ CREATE INDEX IF NOT EXISTS idx_threads_type
134
+ ON threads (profile, thread_type, updated_at DESC);
135
+ CREATE INDEX IF NOT EXISTS idx_members_thread
136
+ ON thread_members (profile, scope_thread_id);
137
+ CREATE INDEX IF NOT EXISTS idx_friends_name
138
+ ON friends (profile, display_name, zalo_name, user_id);
@@ -0,0 +1,18 @@
1
+ CREATE TABLE IF NOT EXISTS contacts (
2
+ profile TEXT NOT NULL,
3
+ user_id TEXT NOT NULL,
4
+ display_name TEXT,
5
+ zalo_name TEXT,
6
+ avatar TEXT,
7
+ account_status INTEGER,
8
+ relationship TEXT NOT NULL DEFAULT 'unknown',
9
+ first_seen_at_ms INTEGER,
10
+ last_seen_at_ms INTEGER,
11
+ raw_json TEXT,
12
+ created_at TEXT NOT NULL,
13
+ updated_at TEXT NOT NULL,
14
+ PRIMARY KEY (profile, user_id)
15
+ );
16
+
17
+ CREATE INDEX IF NOT EXISTS idx_contacts_name
18
+ ON contacts (profile, display_name, zalo_name, user_id);
@@ -0,0 +1,158 @@
1
+ INSERT INTO contacts (
2
+ profile,
3
+ user_id,
4
+ display_name,
5
+ zalo_name,
6
+ avatar,
7
+ account_status,
8
+ relationship,
9
+ first_seen_at_ms,
10
+ last_seen_at_ms,
11
+ raw_json,
12
+ created_at,
13
+ updated_at
14
+ )
15
+ SELECT
16
+ profile,
17
+ user_id,
18
+ display_name,
19
+ zalo_name,
20
+ avatar,
21
+ account_status,
22
+ 'friend',
23
+ NULL,
24
+ NULL,
25
+ raw_json,
26
+ created_at,
27
+ updated_at
28
+ FROM friends
29
+ WHERE 1 = 1
30
+ ON CONFLICT(profile, user_id) DO UPDATE SET
31
+ display_name = COALESCE(excluded.display_name, contacts.display_name),
32
+ zalo_name = COALESCE(excluded.zalo_name, contacts.zalo_name),
33
+ avatar = COALESCE(excluded.avatar, contacts.avatar),
34
+ account_status = COALESCE(excluded.account_status, contacts.account_status),
35
+ relationship = CASE
36
+ WHEN contacts.relationship = 'friend' OR excluded.relationship = 'friend' THEN 'friend'
37
+ WHEN contacts.relationship = 'seen_dm' OR excluded.relationship = 'seen_dm' THEN 'seen_dm'
38
+ WHEN contacts.relationship = 'seen_group' OR excluded.relationship = 'seen_group' THEN 'seen_group'
39
+ ELSE COALESCE(excluded.relationship, contacts.relationship, 'unknown')
40
+ END,
41
+ raw_json = COALESCE(excluded.raw_json, contacts.raw_json),
42
+ updated_at = excluded.updated_at;
43
+
44
+ INSERT INTO contacts (
45
+ profile,
46
+ user_id,
47
+ display_name,
48
+ zalo_name,
49
+ avatar,
50
+ account_status,
51
+ relationship,
52
+ first_seen_at_ms,
53
+ last_seen_at_ms,
54
+ raw_json,
55
+ created_at,
56
+ updated_at
57
+ )
58
+ SELECT
59
+ profile,
60
+ user_id,
61
+ NULLIF(MAX(display_name), ''),
62
+ NULLIF(MAX(zalo_name), ''),
63
+ NULLIF(MAX(avatar), ''),
64
+ MAX(account_status),
65
+ 'seen_group',
66
+ MIN(snapshot_at_ms),
67
+ MAX(snapshot_at_ms),
68
+ NULLIF(MAX(raw_json), ''),
69
+ MIN(created_at),
70
+ MAX(updated_at)
71
+ FROM thread_members
72
+ WHERE 1 = 1
73
+ GROUP BY profile, user_id
74
+ ON CONFLICT(profile, user_id) DO UPDATE SET
75
+ display_name = COALESCE(excluded.display_name, contacts.display_name),
76
+ zalo_name = COALESCE(excluded.zalo_name, contacts.zalo_name),
77
+ avatar = COALESCE(excluded.avatar, contacts.avatar),
78
+ account_status = COALESCE(excluded.account_status, contacts.account_status),
79
+ relationship = CASE
80
+ WHEN contacts.relationship = 'friend' OR excluded.relationship = 'friend' THEN 'friend'
81
+ WHEN contacts.relationship = 'seen_dm' OR excluded.relationship = 'seen_dm' THEN 'seen_dm'
82
+ WHEN contacts.relationship = 'seen_group' OR excluded.relationship = 'seen_group' THEN 'seen_group'
83
+ ELSE COALESCE(excluded.relationship, contacts.relationship, 'unknown')
84
+ END,
85
+ first_seen_at_ms = CASE
86
+ WHEN contacts.first_seen_at_ms IS NULL THEN excluded.first_seen_at_ms
87
+ WHEN excluded.first_seen_at_ms IS NULL THEN contacts.first_seen_at_ms
88
+ ELSE MIN(contacts.first_seen_at_ms, excluded.first_seen_at_ms)
89
+ END,
90
+ last_seen_at_ms = CASE
91
+ WHEN contacts.last_seen_at_ms IS NULL THEN excluded.last_seen_at_ms
92
+ WHEN excluded.last_seen_at_ms IS NULL THEN contacts.last_seen_at_ms
93
+ ELSE MAX(contacts.last_seen_at_ms, excluded.last_seen_at_ms)
94
+ END,
95
+ raw_json = COALESCE(excluded.raw_json, contacts.raw_json),
96
+ updated_at = excluded.updated_at;
97
+
98
+ INSERT INTO contacts (
99
+ profile,
100
+ user_id,
101
+ display_name,
102
+ zalo_name,
103
+ avatar,
104
+ account_status,
105
+ relationship,
106
+ first_seen_at_ms,
107
+ last_seen_at_ms,
108
+ raw_json,
109
+ created_at,
110
+ updated_at
111
+ )
112
+ SELECT
113
+ t.profile,
114
+ COALESCE(NULLIF(t.peer_id, ''), t.scope_thread_id) AS user_id,
115
+ COALESCE(
116
+ NULLIF(MAX(CASE
117
+ WHEN m.sender_id = COALESCE(NULLIF(t.peer_id, ''), t.scope_thread_id) THEN m.sender_name
118
+ ELSE NULL
119
+ END), ''),
120
+ NULLIF(MAX(t.title), '')
121
+ ) AS display_name,
122
+ NULL,
123
+ NULL,
124
+ NULL,
125
+ 'seen_dm',
126
+ MIN(m.timestamp_ms),
127
+ MAX(m.timestamp_ms),
128
+ t.raw_json,
129
+ MIN(t.created_at),
130
+ MAX(COALESCE(m.updated_at, t.updated_at))
131
+ FROM threads t
132
+ LEFT JOIN messages m
133
+ ON m.profile = t.profile
134
+ AND m.scope_thread_id = t.scope_thread_id
135
+ AND m.thread_type = 'user'
136
+ WHERE t.thread_type = 'user'
137
+ AND COALESCE(NULLIF(t.peer_id, ''), t.scope_thread_id) <> ''
138
+ GROUP BY t.profile, COALESCE(NULLIF(t.peer_id, ''), t.scope_thread_id), t.raw_json
139
+ ON CONFLICT(profile, user_id) DO UPDATE SET
140
+ display_name = COALESCE(excluded.display_name, contacts.display_name),
141
+ relationship = CASE
142
+ WHEN contacts.relationship = 'friend' OR excluded.relationship = 'friend' THEN 'friend'
143
+ WHEN contacts.relationship = 'seen_dm' OR excluded.relationship = 'seen_dm' THEN 'seen_dm'
144
+ WHEN contacts.relationship = 'seen_group' OR excluded.relationship = 'seen_group' THEN 'seen_group'
145
+ ELSE COALESCE(excluded.relationship, contacts.relationship, 'unknown')
146
+ END,
147
+ first_seen_at_ms = CASE
148
+ WHEN contacts.first_seen_at_ms IS NULL THEN excluded.first_seen_at_ms
149
+ WHEN excluded.first_seen_at_ms IS NULL THEN contacts.first_seen_at_ms
150
+ ELSE MIN(contacts.first_seen_at_ms, excluded.first_seen_at_ms)
151
+ END,
152
+ last_seen_at_ms = CASE
153
+ WHEN contacts.last_seen_at_ms IS NULL THEN excluded.last_seen_at_ms
154
+ WHEN excluded.last_seen_at_ms IS NULL THEN contacts.last_seen_at_ms
155
+ ELSE MAX(contacts.last_seen_at_ms, excluded.last_seen_at_ms)
156
+ END,
157
+ raw_json = COALESCE(excluded.raw_json, contacts.raw_json),
158
+ updated_at = excluded.updated_at;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzca",
3
- "version": "0.1.52",
3
+ "version": "0.1.54",
4
4
  "description": "Open-source zca-compatible CLI to integrate Zalo with OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "scripts": {
17
17
  "build": "npm run build:cli && npm run build:worker",
18
18
  "build:cli": "tsup src/cli.ts --format esm --target node22 --out-dir dist --clean",
19
- "build:worker": "tsup src/lib/db-worker.ts --format esm --target node22 --out-dir dist",
19
+ "build:worker": "tsup src/lib/db-worker.ts src/lib/db-migrations.ts --format esm --target node22 --out-dir dist && node scripts/copy-db-migrations.mjs",
20
20
  "dev": "tsx src/cli.ts",
21
21
  "test": "tsx --test tests/*.test.ts",
22
22
  "typecheck": "tsc -p tsconfig.json",