@memtensor/memos-local-openclaw-plugin 1.0.4-beta.10 → 1.0.4-beta.12
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/dist/client/connector.d.ts +5 -0
- package/dist/client/connector.d.ts.map +1 -1
- package/dist/client/connector.js +38 -8
- package/dist/client/connector.js.map +1 -1
- package/dist/hub/server.d.ts +1 -0
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +143 -32
- package/dist/hub/server.js.map +1 -1
- package/dist/hub/user-manager.d.ts +9 -0
- package/dist/hub/user-manager.d.ts.map +1 -1
- package/dist/hub/user-manager.js +26 -2
- package/dist/hub/user-manager.js.map +1 -1
- package/dist/ingest/chunker.d.ts +2 -1
- package/dist/ingest/chunker.d.ts.map +1 -1
- package/dist/ingest/chunker.js +14 -10
- package/dist/ingest/chunker.js.map +1 -1
- package/dist/recall/engine.d.ts.map +1 -1
- package/dist/recall/engine.js +7 -2
- package/dist/recall/engine.js.map +1 -1
- package/dist/sharing/types.d.ts +1 -1
- package/dist/sharing/types.d.ts.map +1 -1
- package/dist/skill/evolver.d.ts +2 -0
- package/dist/skill/evolver.d.ts.map +1 -1
- package/dist/skill/evolver.js +56 -5
- package/dist/skill/evolver.js.map +1 -1
- package/dist/skill/generator.d.ts +2 -0
- package/dist/skill/generator.d.ts.map +1 -1
- package/dist/skill/generator.js +45 -3
- package/dist/skill/generator.js.map +1 -1
- package/dist/skill/installer.d.ts +26 -0
- package/dist/skill/installer.d.ts.map +1 -1
- package/dist/skill/installer.js +80 -4
- package/dist/skill/installer.js.map +1 -1
- package/dist/skill/upgrader.d.ts +2 -0
- package/dist/skill/upgrader.d.ts.map +1 -1
- package/dist/skill/upgrader.js +139 -1
- package/dist/skill/upgrader.js.map +1 -1
- package/dist/skill/validator.d.ts +3 -0
- package/dist/skill/validator.d.ts.map +1 -1
- package/dist/skill/validator.js +75 -0
- package/dist/skill/validator.js.map +1 -1
- package/dist/storage/sqlite.d.ts +28 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +155 -16
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +64 -24
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +39 -20
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +338 -33
- package/package.json +1 -1
- package/src/client/connector.ts +43 -8
- package/src/hub/server.ts +142 -31
- package/src/hub/user-manager.ts +42 -6
- package/src/ingest/chunker.ts +19 -13
- package/src/recall/engine.ts +7 -2
- package/src/sharing/types.ts +1 -1
- package/src/skill/evolver.ts +58 -6
- package/src/skill/generator.ts +44 -5
- package/src/skill/installer.ts +107 -4
- package/src/skill/upgrader.ts +139 -1
- package/src/skill/validator.ts +79 -0
- package/src/storage/sqlite.ts +174 -16
- package/src/types.ts +11 -0
- package/src/viewer/html.ts +64 -24
- package/src/viewer/server.ts +39 -20
package/dist/storage/sqlite.js
CHANGED
|
@@ -148,6 +148,8 @@ class SqliteStore {
|
|
|
148
148
|
this.migrateHubTables();
|
|
149
149
|
this.migrateHubFtsToTrigram();
|
|
150
150
|
this.migrateLocalSharedTasksOwner();
|
|
151
|
+
this.migrateHubUserIdentityFields();
|
|
152
|
+
this.migrateClientHubConnectionIdentityFields();
|
|
151
153
|
this.log.debug("Database schema initialized");
|
|
152
154
|
}
|
|
153
155
|
migrateChunksIndexesForRecall() {
|
|
@@ -163,6 +165,51 @@ class SqliteStore {
|
|
|
163
165
|
}
|
|
164
166
|
catch { /* table may not exist yet */ }
|
|
165
167
|
}
|
|
168
|
+
migrateHubUserIdentityFields() {
|
|
169
|
+
try {
|
|
170
|
+
const cols = this.db.prepare("PRAGMA table_info(hub_users)").all();
|
|
171
|
+
if (cols.length === 0)
|
|
172
|
+
return;
|
|
173
|
+
if (!cols.some(c => c.name === "identity_key")) {
|
|
174
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN identity_key TEXT NOT NULL DEFAULT ''");
|
|
175
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_hub_users_identity_key ON hub_users(identity_key)");
|
|
176
|
+
this.log.info("Migrated: added identity_key to hub_users");
|
|
177
|
+
}
|
|
178
|
+
if (!cols.some(c => c.name === "left_at")) {
|
|
179
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN left_at INTEGER");
|
|
180
|
+
this.log.info("Migrated: added left_at to hub_users");
|
|
181
|
+
}
|
|
182
|
+
if (!cols.some(c => c.name === "removed_at")) {
|
|
183
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN removed_at INTEGER");
|
|
184
|
+
this.log.info("Migrated: added removed_at to hub_users");
|
|
185
|
+
}
|
|
186
|
+
if (!cols.some(c => c.name === "rejected_at")) {
|
|
187
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN rejected_at INTEGER");
|
|
188
|
+
this.log.info("Migrated: added rejected_at to hub_users");
|
|
189
|
+
}
|
|
190
|
+
if (!cols.some(c => c.name === "rejoin_requested_at")) {
|
|
191
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN rejoin_requested_at INTEGER");
|
|
192
|
+
this.log.info("Migrated: added rejoin_requested_at to hub_users");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch { /* table may not exist yet */ }
|
|
196
|
+
}
|
|
197
|
+
migrateClientHubConnectionIdentityFields() {
|
|
198
|
+
try {
|
|
199
|
+
const cols = this.db.prepare("PRAGMA table_info(client_hub_connection)").all();
|
|
200
|
+
if (cols.length === 0)
|
|
201
|
+
return;
|
|
202
|
+
if (!cols.some(c => c.name === "identity_key")) {
|
|
203
|
+
this.db.exec("ALTER TABLE client_hub_connection ADD COLUMN identity_key TEXT NOT NULL DEFAULT ''");
|
|
204
|
+
this.log.info("Migrated: added identity_key to client_hub_connection");
|
|
205
|
+
}
|
|
206
|
+
if (!cols.some(c => c.name === "last_known_status")) {
|
|
207
|
+
this.db.exec("ALTER TABLE client_hub_connection ADD COLUMN last_known_status TEXT NOT NULL DEFAULT ''");
|
|
208
|
+
this.log.info("Migrated: added last_known_status to client_hub_connection");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch { /* table may not exist yet */ }
|
|
212
|
+
}
|
|
166
213
|
migrateOwnerFields() {
|
|
167
214
|
const chunkCols = this.db.prepare("PRAGMA table_info(chunks)").all();
|
|
168
215
|
if (!chunkCols.some((c) => c.name === "owner")) {
|
|
@@ -726,6 +773,20 @@ class SqliteStore {
|
|
|
726
773
|
CREATE INDEX IF NOT EXISTS idx_hub_users_status ON hub_users(status);
|
|
727
774
|
CREATE INDEX IF NOT EXISTS idx_hub_users_role ON hub_users(role);
|
|
728
775
|
|
|
776
|
+
CREATE TABLE IF NOT EXISTS hub_groups (
|
|
777
|
+
id TEXT PRIMARY KEY,
|
|
778
|
+
name TEXT NOT NULL,
|
|
779
|
+
description TEXT NOT NULL DEFAULT '',
|
|
780
|
+
created_at INTEGER NOT NULL
|
|
781
|
+
);
|
|
782
|
+
|
|
783
|
+
CREATE TABLE IF NOT EXISTS hub_group_members (
|
|
784
|
+
group_id TEXT NOT NULL REFERENCES hub_groups(id) ON DELETE CASCADE,
|
|
785
|
+
user_id TEXT NOT NULL REFERENCES hub_users(id) ON DELETE CASCADE,
|
|
786
|
+
joined_at INTEGER NOT NULL,
|
|
787
|
+
PRIMARY KEY (group_id, user_id)
|
|
788
|
+
);
|
|
789
|
+
|
|
729
790
|
CREATE TABLE IF NOT EXISTS hub_tasks (
|
|
730
791
|
id TEXT PRIMARY KEY,
|
|
731
792
|
source_task_id TEXT NOT NULL,
|
|
@@ -1580,16 +1641,18 @@ class SqliteStore {
|
|
|
1580
1641
|
// ─── Hub / Client connection ───
|
|
1581
1642
|
setClientHubConnection(conn) {
|
|
1582
1643
|
this.db.prepare(`
|
|
1583
|
-
INSERT INTO client_hub_connection (id, hub_url, user_id, username, user_token, role, connected_at)
|
|
1584
|
-
VALUES (1, ?, ?, ?, ?, ?, ?)
|
|
1644
|
+
INSERT INTO client_hub_connection (id, hub_url, user_id, username, user_token, role, connected_at, identity_key, last_known_status)
|
|
1645
|
+
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1585
1646
|
ON CONFLICT(id) DO UPDATE SET
|
|
1586
1647
|
hub_url = excluded.hub_url,
|
|
1587
1648
|
user_id = excluded.user_id,
|
|
1588
1649
|
username = excluded.username,
|
|
1589
1650
|
user_token = excluded.user_token,
|
|
1590
1651
|
role = excluded.role,
|
|
1591
|
-
connected_at = excluded.connected_at
|
|
1592
|
-
|
|
1652
|
+
connected_at = excluded.connected_at,
|
|
1653
|
+
identity_key = excluded.identity_key,
|
|
1654
|
+
last_known_status = excluded.last_known_status
|
|
1655
|
+
`).run(conn.hubUrl, conn.userId, conn.username, conn.userToken, conn.role, conn.connectedAt, conn.identityKey ?? "", conn.lastKnownStatus ?? "");
|
|
1593
1656
|
}
|
|
1594
1657
|
getClientHubConnection() {
|
|
1595
1658
|
const row = this.db.prepare('SELECT * FROM client_hub_connection WHERE id = 1').get();
|
|
@@ -1682,8 +1745,8 @@ class SqliteStore {
|
|
|
1682
1745
|
// ─── Hub Users / Groups ───
|
|
1683
1746
|
upsertHubUser(user) {
|
|
1684
1747
|
this.db.prepare(`
|
|
1685
|
-
INSERT INTO hub_users (id, username, device_name, role, status, token_hash, created_at, approved_at)
|
|
1686
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1748
|
+
INSERT INTO hub_users (id, username, device_name, role, status, token_hash, created_at, approved_at, identity_key, left_at, removed_at, rejected_at, rejoin_requested_at)
|
|
1749
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1687
1750
|
ON CONFLICT(id) DO UPDATE SET
|
|
1688
1751
|
username = excluded.username,
|
|
1689
1752
|
device_name = excluded.device_name,
|
|
@@ -1691,20 +1754,31 @@ class SqliteStore {
|
|
|
1691
1754
|
status = excluded.status,
|
|
1692
1755
|
token_hash = excluded.token_hash,
|
|
1693
1756
|
created_at = excluded.created_at,
|
|
1694
|
-
approved_at = excluded.approved_at
|
|
1695
|
-
|
|
1757
|
+
approved_at = excluded.approved_at,
|
|
1758
|
+
identity_key = excluded.identity_key,
|
|
1759
|
+
left_at = excluded.left_at,
|
|
1760
|
+
removed_at = excluded.removed_at,
|
|
1761
|
+
rejected_at = excluded.rejected_at,
|
|
1762
|
+
rejoin_requested_at = excluded.rejoin_requested_at
|
|
1763
|
+
`).run(user.id, user.username, user.deviceName ?? "", user.role, user.status, user.tokenHash, user.createdAt, user.approvedAt, user.identityKey ?? "", user.leftAt ?? null, user.removedAt ?? null, user.rejectedAt ?? null, user.rejoinRequestedAt ?? null);
|
|
1696
1764
|
}
|
|
1697
1765
|
getHubUser(userId) {
|
|
1698
1766
|
const row = this.db.prepare('SELECT * FROM hub_users WHERE id = ?').get(userId);
|
|
1699
1767
|
if (!row)
|
|
1700
1768
|
return null;
|
|
1701
|
-
|
|
1769
|
+
const user = rowToHubUser(row);
|
|
1770
|
+
user.groups = this.getGroupsForHubUser(userId);
|
|
1771
|
+
return user;
|
|
1702
1772
|
}
|
|
1703
1773
|
listHubUsers(status) {
|
|
1704
1774
|
const rows = status
|
|
1705
1775
|
? this.db.prepare('SELECT * FROM hub_users WHERE status = ? ORDER BY created_at').all(status)
|
|
1706
1776
|
: this.db.prepare('SELECT * FROM hub_users ORDER BY created_at').all();
|
|
1707
|
-
return rows.map(
|
|
1777
|
+
return rows.map(r => {
|
|
1778
|
+
const user = rowToHubUser(r);
|
|
1779
|
+
user.groups = this.getGroupsForHubUser(r.id);
|
|
1780
|
+
return user;
|
|
1781
|
+
});
|
|
1708
1782
|
}
|
|
1709
1783
|
deleteHubUser(userId, cleanResources = false) {
|
|
1710
1784
|
if (cleanResources) {
|
|
@@ -1714,12 +1788,46 @@ class SqliteStore {
|
|
|
1714
1788
|
const result = this.db.prepare('DELETE FROM hub_users WHERE id = ?').run(userId);
|
|
1715
1789
|
return result.changes > 0;
|
|
1716
1790
|
}
|
|
1717
|
-
const result = this.db.prepare("UPDATE hub_users SET status = 'removed', token_hash = '' WHERE id = ?").run(userId);
|
|
1791
|
+
const result = this.db.prepare("UPDATE hub_users SET status = 'removed', token_hash = '', removed_at = ? WHERE id = ?").run(Date.now(), userId);
|
|
1792
|
+
return result.changes > 0;
|
|
1793
|
+
}
|
|
1794
|
+
findHubUserByIdentityKey(identityKey) {
|
|
1795
|
+
if (!identityKey)
|
|
1796
|
+
return null;
|
|
1797
|
+
const row = this.db.prepare('SELECT * FROM hub_users WHERE identity_key = ?').get(identityKey);
|
|
1798
|
+
return row ? rowToHubUser(row) : null;
|
|
1799
|
+
}
|
|
1800
|
+
markHubUserLeft(userId) {
|
|
1801
|
+
const result = this.db.prepare("UPDATE hub_users SET status = 'left', token_hash = '', left_at = ? WHERE id = ?").run(Date.now(), userId);
|
|
1718
1802
|
return result.changes > 0;
|
|
1719
1803
|
}
|
|
1720
1804
|
updateHubUserActivity(userId, ip, timestamp) {
|
|
1721
1805
|
this.db.prepare('UPDATE hub_users SET last_ip = ?, last_active_at = ? WHERE id = ?').run(ip, timestamp ?? Date.now(), userId);
|
|
1722
1806
|
}
|
|
1807
|
+
// ─── Hub Groups ───
|
|
1808
|
+
upsertHubGroup(group) {
|
|
1809
|
+
this.db.prepare(`
|
|
1810
|
+
INSERT INTO hub_groups (id, name, description, created_at)
|
|
1811
|
+
VALUES (?, ?, ?, ?)
|
|
1812
|
+
ON CONFLICT(id) DO UPDATE SET name = excluded.name, description = excluded.description
|
|
1813
|
+
`).run(group.id, group.name, group.description ?? "", group.createdAt);
|
|
1814
|
+
}
|
|
1815
|
+
addHubGroupMember(groupId, userId, joinedAt) {
|
|
1816
|
+
this.db.prepare(`
|
|
1817
|
+
INSERT OR IGNORE INTO hub_group_members (group_id, user_id, joined_at)
|
|
1818
|
+
VALUES (?, ?, ?)
|
|
1819
|
+
`).run(groupId, userId, joinedAt);
|
|
1820
|
+
}
|
|
1821
|
+
removeHubGroupMember(groupId, userId) {
|
|
1822
|
+
this.db.prepare('DELETE FROM hub_group_members WHERE group_id = ? AND user_id = ?').run(groupId, userId);
|
|
1823
|
+
}
|
|
1824
|
+
getGroupsForHubUser(userId) {
|
|
1825
|
+
return this.db.prepare(`
|
|
1826
|
+
SELECT g.id, g.name, g.description FROM hub_groups g
|
|
1827
|
+
JOIN hub_group_members m ON m.group_id = g.id
|
|
1828
|
+
WHERE m.user_id = ?
|
|
1829
|
+
`).all(userId);
|
|
1830
|
+
}
|
|
1723
1831
|
getHubUserContributions() {
|
|
1724
1832
|
const result = {};
|
|
1725
1833
|
const memRows = this.db.prepare('SELECT source_user_id, COUNT(*) as cnt FROM hub_memories GROUP BY source_user_id').all();
|
|
@@ -1836,20 +1944,36 @@ class SqliteStore {
|
|
|
1836
1944
|
out.push(row.vector.readFloatLE(i * 4));
|
|
1837
1945
|
return out;
|
|
1838
1946
|
}
|
|
1947
|
+
getVisibleHubSkillEmbeddings() {
|
|
1948
|
+
const rows = this.db.prepare(`
|
|
1949
|
+
SELECT hse.skill_id, hse.vector, hse.dimensions
|
|
1950
|
+
FROM hub_skill_embeddings hse
|
|
1951
|
+
JOIN hub_skills hs ON hs.id = hse.skill_id
|
|
1952
|
+
`).all();
|
|
1953
|
+
return rows.map(r => ({
|
|
1954
|
+
skillId: r.skill_id,
|
|
1955
|
+
vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
|
|
1956
|
+
}));
|
|
1957
|
+
}
|
|
1839
1958
|
searchHubChunks(query, options) {
|
|
1840
1959
|
const limit = options?.maxResults ?? 10;
|
|
1841
1960
|
const userId = options?.userId ?? "";
|
|
1842
1961
|
const rows = this.db.prepare(`
|
|
1843
|
-
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
1962
|
+
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
1963
|
+
COALESCE(hg.name, '') as group_name, hu.username as owner_name,
|
|
1844
1964
|
bm25(hub_chunks_fts) as rank
|
|
1845
1965
|
FROM hub_chunks_fts f
|
|
1846
1966
|
JOIN hub_chunks hc ON hc.rowid = f.rowid
|
|
1847
1967
|
JOIN hub_tasks ht ON ht.id = hc.hub_task_id
|
|
1848
1968
|
LEFT JOIN hub_users hu ON hu.id = ht.source_user_id
|
|
1969
|
+
LEFT JOIN hub_groups hg ON hg.id = ht.group_id
|
|
1849
1970
|
WHERE hub_chunks_fts MATCH ?
|
|
1971
|
+
AND (ht.visibility = 'public'
|
|
1972
|
+
OR ht.source_user_id = ?
|
|
1973
|
+
OR EXISTS (SELECT 1 FROM hub_group_members gm WHERE gm.group_id = ht.group_id AND gm.user_id = ?))
|
|
1850
1974
|
ORDER BY rank
|
|
1851
1975
|
LIMIT ?
|
|
1852
|
-
`).all(sanitizeFtsQuery(query), limit);
|
|
1976
|
+
`).all(sanitizeFtsQuery(query), userId, userId, limit);
|
|
1853
1977
|
return rows.map((row, idx) => ({ hit: row, rank: idx + 1 }));
|
|
1854
1978
|
}
|
|
1855
1979
|
upsertHubEmbedding(chunkId, vector) {
|
|
@@ -1872,7 +1996,10 @@ class SqliteStore {
|
|
|
1872
1996
|
FROM hub_embeddings he
|
|
1873
1997
|
JOIN hub_chunks hc ON hc.id = he.chunk_id
|
|
1874
1998
|
JOIN hub_tasks ht ON ht.id = hc.hub_task_id
|
|
1875
|
-
|
|
1999
|
+
WHERE ht.visibility = 'public'
|
|
2000
|
+
OR ht.source_user_id = ?
|
|
2001
|
+
OR EXISTS (SELECT 1 FROM hub_group_members gm WHERE gm.group_id = ht.group_id AND gm.user_id = ?)
|
|
2002
|
+
`).all(userId, userId);
|
|
1876
2003
|
return rows.map(r => ({
|
|
1877
2004
|
chunkId: r.chunk_id,
|
|
1878
2005
|
vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
|
|
@@ -1880,14 +2007,19 @@ class SqliteStore {
|
|
|
1880
2007
|
}
|
|
1881
2008
|
getVisibleHubSearchHitByChunkId(chunkId, userId) {
|
|
1882
2009
|
const row = this.db.prepare(`
|
|
1883
|
-
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
2010
|
+
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
2011
|
+
COALESCE(hg.name, '') as group_name, hu.username as owner_name,
|
|
1884
2012
|
0 as rank
|
|
1885
2013
|
FROM hub_chunks hc
|
|
1886
2014
|
JOIN hub_tasks ht ON ht.id = hc.hub_task_id
|
|
1887
2015
|
LEFT JOIN hub_users hu ON hu.id = ht.source_user_id
|
|
2016
|
+
LEFT JOIN hub_groups hg ON hg.id = ht.group_id
|
|
1888
2017
|
WHERE hc.id = ?
|
|
2018
|
+
AND (ht.visibility = 'public'
|
|
2019
|
+
OR ht.source_user_id = ?
|
|
2020
|
+
OR EXISTS (SELECT 1 FROM hub_group_members gm WHERE gm.group_id = ht.group_id AND gm.user_id = ?))
|
|
1889
2021
|
LIMIT 1
|
|
1890
|
-
`).get(chunkId);
|
|
2022
|
+
`).get(chunkId, userId, userId);
|
|
1891
2023
|
return row ?? null;
|
|
1892
2024
|
}
|
|
1893
2025
|
getHubChunkById(chunkId) {
|
|
@@ -2276,6 +2408,8 @@ function rowToClientHubConnection(row) {
|
|
|
2276
2408
|
userToken: row.user_token,
|
|
2277
2409
|
role: row.role,
|
|
2278
2410
|
connectedAt: row.connected_at,
|
|
2411
|
+
identityKey: row.identity_key || "",
|
|
2412
|
+
lastKnownStatus: row.last_known_status || "",
|
|
2279
2413
|
};
|
|
2280
2414
|
}
|
|
2281
2415
|
function rowToHubUser(row) {
|
|
@@ -2291,6 +2425,11 @@ function rowToHubUser(row) {
|
|
|
2291
2425
|
approvedAt: row.approved_at,
|
|
2292
2426
|
lastIp: row.last_ip || "",
|
|
2293
2427
|
lastActiveAt: row.last_active_at ?? null,
|
|
2428
|
+
identityKey: row.identity_key || "",
|
|
2429
|
+
leftAt: row.left_at ?? null,
|
|
2430
|
+
removedAt: row.removed_at ?? null,
|
|
2431
|
+
rejectedAt: row.rejected_at ?? null,
|
|
2432
|
+
rejoinRequestedAt: row.rejoin_requested_at ?? null,
|
|
2294
2433
|
};
|
|
2295
2434
|
}
|
|
2296
2435
|
function rowToHubTask(row) {
|