@deeplake/hivemind 0.7.52 → 0.7.53
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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/bundle/cli.js +8 -1
- package/codex/bundle/commands/auth-login.js +8 -1
- package/codex/bundle/session-start.js +61 -1
- package/cursor/bundle/commands/auth-login.js +8 -1
- package/cursor/bundle/session-start.js +61 -1
- package/hermes/bundle/commands/auth-login.js +8 -1
- package/hermes/bundle/session-start.js +62 -1
- package/openclaw/dist/index.js +66 -2
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/package.json +1 -1
- package/pi/extension-source/hivemind.ts +48 -1
|
@@ -6,18 +6,18 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Cloud-backed persistent shared memory for AI agents powered by Deeplake",
|
|
9
|
-
"version": "0.7.
|
|
9
|
+
"version": "0.7.53"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "hivemind",
|
|
14
14
|
"description": "Persistent shared memory powered by Deeplake — captures all session activity and provides cross-session, cross-agent memory search",
|
|
15
|
-
"version": "0.7.
|
|
15
|
+
"version": "0.7.53",
|
|
16
16
|
"source": {
|
|
17
17
|
"source": "git-subdir",
|
|
18
18
|
"url": "https://github.com/activeloopai/hivemind.git",
|
|
19
19
|
"path": "claude-code",
|
|
20
|
-
"sha": "
|
|
20
|
+
"sha": "2f9768a8c9ed54952bdc272261f2a527f5482fe2"
|
|
21
21
|
},
|
|
22
22
|
"homepage": "https://github.com/activeloopai/hivemind"
|
|
23
23
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hivemind",
|
|
3
3
|
"description": "Cloud-backed persistent memory powered by Deeplake — read, write, and share memory across Claude Code sessions and agents",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.53",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Activeloop",
|
|
7
7
|
"url": "https://deeplake.ai"
|
package/bundle/cli.js
CHANGED
|
@@ -4270,7 +4270,14 @@ async function switchOrg(orgId, orgName) {
|
|
|
4270
4270
|
const creds = loadCredentials();
|
|
4271
4271
|
if (!creds)
|
|
4272
4272
|
throw new Error("Not logged in. Run deeplake login first.");
|
|
4273
|
-
|
|
4273
|
+
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
|
|
4274
|
+
const tokenName = `deeplake-plugin-switch-${Date.now()}`;
|
|
4275
|
+
const tokenData = await apiPost("/users/me/tokens", {
|
|
4276
|
+
name: tokenName,
|
|
4277
|
+
duration: 365 * 24 * 3600,
|
|
4278
|
+
organization_id: orgId
|
|
4279
|
+
}, creds.token, apiUrl);
|
|
4280
|
+
saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
|
|
4274
4281
|
}
|
|
4275
4282
|
async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
|
|
4276
4283
|
const raw = await apiGet("/workspaces", token, apiUrl, orgId);
|
|
@@ -259,7 +259,14 @@ async function switchOrg(orgId, orgName) {
|
|
|
259
259
|
const creds = loadCredentials();
|
|
260
260
|
if (!creds)
|
|
261
261
|
throw new Error("Not logged in. Run deeplake login first.");
|
|
262
|
-
|
|
262
|
+
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
|
|
263
|
+
const tokenName = `deeplake-plugin-switch-${Date.now()}`;
|
|
264
|
+
const tokenData = await apiPost("/users/me/tokens", {
|
|
265
|
+
name: tokenName,
|
|
266
|
+
duration: 365 * 24 * 3600,
|
|
267
|
+
organization_id: orgId
|
|
268
|
+
}, creds.token, apiUrl);
|
|
269
|
+
saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
|
|
263
270
|
}
|
|
264
271
|
async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
|
|
265
272
|
const raw = await apiGet("/workspaces", token, apiUrl, orgId);
|
|
@@ -92,6 +92,65 @@ function loadCredentials() {
|
|
|
92
92
|
return null;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
+
function saveCredentials(creds) {
|
|
96
|
+
mkdirSync2(configDir(), { recursive: true, mode: 448 });
|
|
97
|
+
writeFileSync2(credsPath(), JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// dist/src/commands/auth.js
|
|
101
|
+
var DEFAULT_API_URL = "https://api.deeplake.ai";
|
|
102
|
+
function decodeJwtPayload(token) {
|
|
103
|
+
try {
|
|
104
|
+
const parts = token.split(".");
|
|
105
|
+
if (parts.length !== 3)
|
|
106
|
+
return null;
|
|
107
|
+
let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
108
|
+
while (payload.length % 4)
|
|
109
|
+
payload += "=";
|
|
110
|
+
return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function apiPost(path, body, token, apiUrl, orgId) {
|
|
116
|
+
const headers = {
|
|
117
|
+
Authorization: `Bearer ${token}`,
|
|
118
|
+
"Content-Type": "application/json",
|
|
119
|
+
...deeplakeClientHeader()
|
|
120
|
+
};
|
|
121
|
+
if (orgId)
|
|
122
|
+
headers["X-Activeloop-Org-Id"] = orgId;
|
|
123
|
+
const resp = await fetch(`${apiUrl}${path}`, { method: "POST", headers, body: JSON.stringify(body) });
|
|
124
|
+
if (!resp.ok)
|
|
125
|
+
throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
|
|
126
|
+
return resp.json();
|
|
127
|
+
}
|
|
128
|
+
async function healDriftedOrgToken(creds, log6 = () => {
|
|
129
|
+
}) {
|
|
130
|
+
if (!creds.token || !creds.orgId)
|
|
131
|
+
return creds;
|
|
132
|
+
const payload = decodeJwtPayload(creds.token);
|
|
133
|
+
const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : void 0;
|
|
134
|
+
if (!claimOrg || claimOrg === creds.orgId)
|
|
135
|
+
return creds;
|
|
136
|
+
log6(`token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} \u2014 re-minting`);
|
|
137
|
+
try {
|
|
138
|
+
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
|
|
139
|
+
const tokenName = `deeplake-plugin-heal-${Date.now()}`;
|
|
140
|
+
const tokenData = await apiPost("/users/me/tokens", {
|
|
141
|
+
name: tokenName,
|
|
142
|
+
duration: 365 * 24 * 3600,
|
|
143
|
+
organization_id: creds.orgId
|
|
144
|
+
}, creds.token, apiUrl);
|
|
145
|
+
const healed = { ...creds, token: tokenData.token.token };
|
|
146
|
+
saveCredentials(healed);
|
|
147
|
+
log6(`token re-minted for org=${creds.orgId}`);
|
|
148
|
+
return healed;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
log6(`token re-mint failed (continuing with stale token): ${err.message}`);
|
|
151
|
+
return creds;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
95
154
|
|
|
96
155
|
// dist/src/utils/stdin.js
|
|
97
156
|
function readStdin() {
|
|
@@ -1827,13 +1886,14 @@ async function main() {
|
|
|
1827
1886
|
if (process.env.HIVEMIND_WIKI_WORKER === "1")
|
|
1828
1887
|
return;
|
|
1829
1888
|
const input = await readStdin();
|
|
1830
|
-
|
|
1889
|
+
let creds = loadCredentials();
|
|
1831
1890
|
if (!creds?.token) {
|
|
1832
1891
|
log5("no credentials found \u2014 run auth login to authenticate");
|
|
1833
1892
|
const auto = maybeAutoMineLocal();
|
|
1834
1893
|
log5(`auto-mine: ${auto.triggered ? "triggered (background)" : `skipped (${auto.reason})`}`);
|
|
1835
1894
|
} else {
|
|
1836
1895
|
log5(`credentials loaded: org=${creds.orgName ?? creds.orgId}`);
|
|
1896
|
+
creds = await healDriftedOrgToken(creds, log5);
|
|
1837
1897
|
}
|
|
1838
1898
|
if (creds?.token) {
|
|
1839
1899
|
const setupScript = join17(__bundleDir, "session-start-setup.js");
|
|
@@ -259,7 +259,14 @@ async function switchOrg(orgId, orgName) {
|
|
|
259
259
|
const creds = loadCredentials();
|
|
260
260
|
if (!creds)
|
|
261
261
|
throw new Error("Not logged in. Run deeplake login first.");
|
|
262
|
-
|
|
262
|
+
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
|
|
263
|
+
const tokenName = `deeplake-plugin-switch-${Date.now()}`;
|
|
264
|
+
const tokenData = await apiPost("/users/me/tokens", {
|
|
265
|
+
name: tokenName,
|
|
266
|
+
duration: 365 * 24 * 3600,
|
|
267
|
+
organization_id: orgId
|
|
268
|
+
}, creds.token, apiUrl);
|
|
269
|
+
saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
|
|
263
270
|
}
|
|
264
271
|
async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
|
|
265
272
|
const raw = await apiGet("/workspaces", token, apiUrl, orgId);
|
|
@@ -91,6 +91,65 @@ function loadCredentials() {
|
|
|
91
91
|
return null;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
function saveCredentials(creds) {
|
|
95
|
+
mkdirSync2(configDir(), { recursive: true, mode: 448 });
|
|
96
|
+
writeFileSync2(credsPath(), JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// dist/src/commands/auth.js
|
|
100
|
+
var DEFAULT_API_URL = "https://api.deeplake.ai";
|
|
101
|
+
function decodeJwtPayload(token) {
|
|
102
|
+
try {
|
|
103
|
+
const parts = token.split(".");
|
|
104
|
+
if (parts.length !== 3)
|
|
105
|
+
return null;
|
|
106
|
+
let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
107
|
+
while (payload.length % 4)
|
|
108
|
+
payload += "=";
|
|
109
|
+
return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function apiPost(path, body, token, apiUrl, orgId) {
|
|
115
|
+
const headers = {
|
|
116
|
+
Authorization: `Bearer ${token}`,
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
...deeplakeClientHeader()
|
|
119
|
+
};
|
|
120
|
+
if (orgId)
|
|
121
|
+
headers["X-Activeloop-Org-Id"] = orgId;
|
|
122
|
+
const resp = await fetch(`${apiUrl}${path}`, { method: "POST", headers, body: JSON.stringify(body) });
|
|
123
|
+
if (!resp.ok)
|
|
124
|
+
throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
|
|
125
|
+
return resp.json();
|
|
126
|
+
}
|
|
127
|
+
async function healDriftedOrgToken(creds, log7 = () => {
|
|
128
|
+
}) {
|
|
129
|
+
if (!creds.token || !creds.orgId)
|
|
130
|
+
return creds;
|
|
131
|
+
const payload = decodeJwtPayload(creds.token);
|
|
132
|
+
const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : void 0;
|
|
133
|
+
if (!claimOrg || claimOrg === creds.orgId)
|
|
134
|
+
return creds;
|
|
135
|
+
log7(`token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} \u2014 re-minting`);
|
|
136
|
+
try {
|
|
137
|
+
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
|
|
138
|
+
const tokenName = `deeplake-plugin-heal-${Date.now()}`;
|
|
139
|
+
const tokenData = await apiPost("/users/me/tokens", {
|
|
140
|
+
name: tokenName,
|
|
141
|
+
duration: 365 * 24 * 3600,
|
|
142
|
+
organization_id: creds.orgId
|
|
143
|
+
}, creds.token, apiUrl);
|
|
144
|
+
const healed = { ...creds, token: tokenData.token.token };
|
|
145
|
+
saveCredentials(healed);
|
|
146
|
+
log7(`token re-minted for org=${creds.orgId}`);
|
|
147
|
+
return healed;
|
|
148
|
+
} catch (err) {
|
|
149
|
+
log7(`token re-mint failed (continuing with stale token): ${err.message}`);
|
|
150
|
+
return creds;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
94
153
|
|
|
95
154
|
// dist/src/config.js
|
|
96
155
|
import { readFileSync as readFileSync3, existsSync } from "node:fs";
|
|
@@ -2156,13 +2215,14 @@ async function main() {
|
|
|
2156
2215
|
const input = await readStdin();
|
|
2157
2216
|
const sessionId = resolveSessionId(input);
|
|
2158
2217
|
const cwd = resolveCwd(input);
|
|
2159
|
-
|
|
2218
|
+
let creds = loadCredentials();
|
|
2160
2219
|
if (!creds?.token) {
|
|
2161
2220
|
log6("no credentials found");
|
|
2162
2221
|
const auto = maybeAutoMineLocal();
|
|
2163
2222
|
log6(`auto-mine: ${auto.triggered ? "triggered (background)" : `skipped (${auto.reason})`}`);
|
|
2164
2223
|
} else {
|
|
2165
2224
|
log6(`credentials loaded: org=${creds.orgName ?? creds.orgId}`);
|
|
2225
|
+
creds = await healDriftedOrgToken(creds, log6);
|
|
2166
2226
|
}
|
|
2167
2227
|
await autoUpdate(creds, { agent: "cursor" });
|
|
2168
2228
|
const current = getInstalledVersion(__bundleDir, ".claude-plugin");
|
|
@@ -259,7 +259,14 @@ async function switchOrg(orgId, orgName) {
|
|
|
259
259
|
const creds = loadCredentials();
|
|
260
260
|
if (!creds)
|
|
261
261
|
throw new Error("Not logged in. Run deeplake login first.");
|
|
262
|
-
|
|
262
|
+
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
|
|
263
|
+
const tokenName = `deeplake-plugin-switch-${Date.now()}`;
|
|
264
|
+
const tokenData = await apiPost("/users/me/tokens", {
|
|
265
|
+
name: tokenName,
|
|
266
|
+
duration: 365 * 24 * 3600,
|
|
267
|
+
organization_id: orgId
|
|
268
|
+
}, creds.token, apiUrl);
|
|
269
|
+
saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
|
|
263
270
|
}
|
|
264
271
|
async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
|
|
265
272
|
const raw = await apiGet("/workspaces", token, apiUrl, orgId);
|
|
@@ -90,6 +90,65 @@ function loadCredentials() {
|
|
|
90
90
|
return null;
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
function saveCredentials(creds) {
|
|
94
|
+
mkdirSync2(configDir(), { recursive: true, mode: 448 });
|
|
95
|
+
writeFileSync2(credsPath(), JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// dist/src/commands/auth.js
|
|
99
|
+
var DEFAULT_API_URL = "https://api.deeplake.ai";
|
|
100
|
+
function decodeJwtPayload(token) {
|
|
101
|
+
try {
|
|
102
|
+
const parts = token.split(".");
|
|
103
|
+
if (parts.length !== 3)
|
|
104
|
+
return null;
|
|
105
|
+
let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
106
|
+
while (payload.length % 4)
|
|
107
|
+
payload += "=";
|
|
108
|
+
return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function apiPost(path, body, token, apiUrl, orgId) {
|
|
114
|
+
const headers = {
|
|
115
|
+
Authorization: `Bearer ${token}`,
|
|
116
|
+
"Content-Type": "application/json",
|
|
117
|
+
...deeplakeClientHeader()
|
|
118
|
+
};
|
|
119
|
+
if (orgId)
|
|
120
|
+
headers["X-Activeloop-Org-Id"] = orgId;
|
|
121
|
+
const resp = await fetch(`${apiUrl}${path}`, { method: "POST", headers, body: JSON.stringify(body) });
|
|
122
|
+
if (!resp.ok)
|
|
123
|
+
throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
|
|
124
|
+
return resp.json();
|
|
125
|
+
}
|
|
126
|
+
async function healDriftedOrgToken(creds, log7 = () => {
|
|
127
|
+
}) {
|
|
128
|
+
if (!creds.token || !creds.orgId)
|
|
129
|
+
return creds;
|
|
130
|
+
const payload = decodeJwtPayload(creds.token);
|
|
131
|
+
const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : void 0;
|
|
132
|
+
if (!claimOrg || claimOrg === creds.orgId)
|
|
133
|
+
return creds;
|
|
134
|
+
log7(`token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} \u2014 re-minting`);
|
|
135
|
+
try {
|
|
136
|
+
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
|
|
137
|
+
const tokenName = `deeplake-plugin-heal-${Date.now()}`;
|
|
138
|
+
const tokenData = await apiPost("/users/me/tokens", {
|
|
139
|
+
name: tokenName,
|
|
140
|
+
duration: 365 * 24 * 3600,
|
|
141
|
+
organization_id: creds.orgId
|
|
142
|
+
}, creds.token, apiUrl);
|
|
143
|
+
const healed = { ...creds, token: tokenData.token.token };
|
|
144
|
+
saveCredentials(healed);
|
|
145
|
+
log7(`token re-minted for org=${creds.orgId}`);
|
|
146
|
+
return healed;
|
|
147
|
+
} catch (err) {
|
|
148
|
+
log7(`token re-mint failed (continuing with stale token): ${err.message}`);
|
|
149
|
+
return creds;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
93
152
|
|
|
94
153
|
// dist/src/config.js
|
|
95
154
|
import { readFileSync as readFileSync3, existsSync } from "node:fs";
|
|
@@ -2146,10 +2205,12 @@ async function main() {
|
|
|
2146
2205
|
const input = await readStdin();
|
|
2147
2206
|
const sessionId = input.session_id ?? `hermes-${Date.now()}`;
|
|
2148
2207
|
const cwd = input.cwd ?? process.cwd();
|
|
2149
|
-
|
|
2208
|
+
let creds = loadCredentials();
|
|
2150
2209
|
const captureEnabled = process.env.HIVEMIND_CAPTURE !== "false";
|
|
2151
2210
|
if (!creds?.token) {
|
|
2152
2211
|
maybeAutoMineLocal();
|
|
2212
|
+
} else {
|
|
2213
|
+
creds = await healDriftedOrgToken(creds, log6);
|
|
2153
2214
|
}
|
|
2154
2215
|
await autoUpdate(creds, { agent: "hermes" });
|
|
2155
2216
|
const current = getInstalledVersion(__bundleDir, ".claude-plugin");
|
package/openclaw/dist/index.js
CHANGED
|
@@ -52,6 +52,17 @@ function hivemindInstallIDHeader() {
|
|
|
52
52
|
|
|
53
53
|
// src/commands/auth.ts
|
|
54
54
|
var DEFAULT_API_URL = "https://api.deeplake.ai";
|
|
55
|
+
function decodeJwtPayload(token) {
|
|
56
|
+
try {
|
|
57
|
+
const parts = token.split(".");
|
|
58
|
+
if (parts.length !== 3) return null;
|
|
59
|
+
let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
60
|
+
while (payload.length % 4) payload += "=";
|
|
61
|
+
return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
55
66
|
async function apiGet(path, token, apiUrl, orgId) {
|
|
56
67
|
const headers = {
|
|
57
68
|
Authorization: `Bearer ${token}`,
|
|
@@ -63,6 +74,17 @@ async function apiGet(path, token, apiUrl, orgId) {
|
|
|
63
74
|
if (!resp.ok) throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
|
|
64
75
|
return resp.json();
|
|
65
76
|
}
|
|
77
|
+
async function apiPost(path, body, token, apiUrl, orgId) {
|
|
78
|
+
const headers = {
|
|
79
|
+
Authorization: `Bearer ${token}`,
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
...deeplakeClientHeader()
|
|
82
|
+
};
|
|
83
|
+
if (orgId) headers["X-Activeloop-Org-Id"] = orgId;
|
|
84
|
+
const resp = await fetch(`${apiUrl}${path}`, { method: "POST", headers, body: JSON.stringify(body) });
|
|
85
|
+
if (!resp.ok) throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
|
|
86
|
+
return resp.json();
|
|
87
|
+
}
|
|
66
88
|
async function requestDeviceCode(apiUrl = DEFAULT_API_URL) {
|
|
67
89
|
const resp = await fetch(`${apiUrl}/auth/device/code`, {
|
|
68
90
|
method: "POST",
|
|
@@ -101,7 +123,38 @@ async function listOrgs(token, apiUrl = DEFAULT_API_URL) {
|
|
|
101
123
|
async function switchOrg(orgId, orgName) {
|
|
102
124
|
const creds = loadCredentials();
|
|
103
125
|
if (!creds) throw new Error("Not logged in. Run deeplake login first.");
|
|
104
|
-
|
|
126
|
+
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
|
|
127
|
+
const tokenName = `deeplake-plugin-switch-${Date.now()}`;
|
|
128
|
+
const tokenData = await apiPost("/users/me/tokens", {
|
|
129
|
+
name: tokenName,
|
|
130
|
+
duration: 365 * 24 * 3600,
|
|
131
|
+
organization_id: orgId
|
|
132
|
+
}, creds.token, apiUrl);
|
|
133
|
+
saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
|
|
134
|
+
}
|
|
135
|
+
async function healDriftedOrgToken(creds, log4 = () => {
|
|
136
|
+
}) {
|
|
137
|
+
if (!creds.token || !creds.orgId) return creds;
|
|
138
|
+
const payload = decodeJwtPayload(creds.token);
|
|
139
|
+
const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : void 0;
|
|
140
|
+
if (!claimOrg || claimOrg === creds.orgId) return creds;
|
|
141
|
+
log4(`token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} \u2014 re-minting`);
|
|
142
|
+
try {
|
|
143
|
+
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
|
|
144
|
+
const tokenName = `deeplake-plugin-heal-${Date.now()}`;
|
|
145
|
+
const tokenData = await apiPost("/users/me/tokens", {
|
|
146
|
+
name: tokenName,
|
|
147
|
+
duration: 365 * 24 * 3600,
|
|
148
|
+
organization_id: creds.orgId
|
|
149
|
+
}, creds.token, apiUrl);
|
|
150
|
+
const healed = { ...creds, token: tokenData.token.token };
|
|
151
|
+
saveCredentials(healed);
|
|
152
|
+
log4(`token re-minted for org=${creds.orgId}`);
|
|
153
|
+
return healed;
|
|
154
|
+
} catch (err) {
|
|
155
|
+
log4(`token re-mint failed (continuing with stale token): ${err.message}`);
|
|
156
|
+
return creds;
|
|
157
|
+
}
|
|
105
158
|
}
|
|
106
159
|
async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
|
|
107
160
|
const raw = await apiGet("/workspaces", token, apiUrl, orgId);
|
|
@@ -1727,7 +1780,7 @@ function extractLatestVersion(body) {
|
|
|
1727
1780
|
return typeof v === "string" && v.length > 0 ? v : null;
|
|
1728
1781
|
}
|
|
1729
1782
|
function getInstalledVersion() {
|
|
1730
|
-
return "0.7.
|
|
1783
|
+
return "0.7.53".length > 0 ? "0.7.53" : null;
|
|
1731
1784
|
}
|
|
1732
1785
|
function isNewer(latest, current) {
|
|
1733
1786
|
const parse = (v) => v.replace(/-.*$/, "").split(".").map(Number);
|
|
@@ -1752,6 +1805,7 @@ async function checkForUpdate(logger) {
|
|
|
1752
1805
|
}
|
|
1753
1806
|
var authPending = false;
|
|
1754
1807
|
var authUrl = null;
|
|
1808
|
+
var driftHealPromise = null;
|
|
1755
1809
|
var pendingUpdate = null;
|
|
1756
1810
|
var justAuthenticated = false;
|
|
1757
1811
|
async function requestAuth() {
|
|
@@ -2010,6 +2064,16 @@ function normalizeVirtualPath(p) {
|
|
|
2010
2064
|
}
|
|
2011
2065
|
async function getApi() {
|
|
2012
2066
|
if (api) return api;
|
|
2067
|
+
if (!driftHealPromise) {
|
|
2068
|
+
driftHealPromise = (async () => {
|
|
2069
|
+
try {
|
|
2070
|
+
const creds = await loadCredentials2();
|
|
2071
|
+
if (creds?.token) await healDriftedOrgToken(creds);
|
|
2072
|
+
} catch {
|
|
2073
|
+
}
|
|
2074
|
+
})();
|
|
2075
|
+
}
|
|
2076
|
+
await driftHealPromise;
|
|
2013
2077
|
const config = await loadConfig();
|
|
2014
2078
|
if (!config) {
|
|
2015
2079
|
if (!authPending) await requestAuth();
|
package/openclaw/package.json
CHANGED
package/package.json
CHANGED
|
@@ -93,6 +93,52 @@ function loadCreds(): Creds | null {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// Inline copies of decodeJwtPayload + healDriftedOrgToken (the shared helpers
|
|
97
|
+
// live in src/commands/auth.ts, but pi extensions ship as raw .ts with no
|
|
98
|
+
// shared-module imports — kept in lockstep with that file).
|
|
99
|
+
function decodeJwtPayloadInline(token: string): Record<string, unknown> | null {
|
|
100
|
+
try {
|
|
101
|
+
const parts = token.split(".");
|
|
102
|
+
if (parts.length !== 3) return null;
|
|
103
|
+
let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
104
|
+
while (payload.length % 4) payload += "=";
|
|
105
|
+
return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
|
|
106
|
+
} catch { return null; }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function healDriftedOrgTokenInline(creds: Creds): Promise<Creds> {
|
|
110
|
+
if (!creds.token || !creds.orgId) return creds;
|
|
111
|
+
const payload = decodeJwtPayloadInline(creds.token);
|
|
112
|
+
const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : undefined;
|
|
113
|
+
if (!claimOrg || claimOrg === creds.orgId) return creds;
|
|
114
|
+
logHm(`session_start: token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} — re-minting`);
|
|
115
|
+
try {
|
|
116
|
+
const resp = await fetch(`${creds.apiUrl}/users/me/tokens`, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: { Authorization: `Bearer ${creds.token}`, "Content-Type": "application/json" },
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
name: `deeplake-plugin-heal-${Date.now()}`,
|
|
121
|
+
duration: 365 * 24 * 3600,
|
|
122
|
+
organization_id: creds.orgId,
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
if (!resp.ok) throw new Error(`API ${resp.status}: ${(await resp.text().catch(() => "")).slice(0, 200)}`);
|
|
126
|
+
const data = await resp.json() as { token: { token: string } };
|
|
127
|
+
const newToken = data.token.token;
|
|
128
|
+
// Read + merge + write the WHOLE creds file so we don't drop fields pi
|
|
129
|
+
// doesn't model (e.g. savedAt). Atomic via writeFileSync with mode 0o600.
|
|
130
|
+
const path = join(homedir(), ".deeplake", "credentials.json");
|
|
131
|
+
const raw = JSON.parse(readFileSync(path, "utf-8"));
|
|
132
|
+
raw.token = newToken;
|
|
133
|
+
writeFileSync(path, JSON.stringify(raw, null, 2), { mode: 0o600 });
|
|
134
|
+
logHm(`session_start: token re-minted for org=${creds.orgId}`);
|
|
135
|
+
return { ...creds, token: newToken };
|
|
136
|
+
} catch (e: any) {
|
|
137
|
+
logHm(`session_start: token re-mint failed (continuing with stale token): ${e?.message ?? e}`);
|
|
138
|
+
return creds;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
96
142
|
const MEMORY_TABLE = process.env.HIVEMIND_TABLE ?? "memory";
|
|
97
143
|
const SESSIONS_TABLE = process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions";
|
|
98
144
|
|
|
@@ -1076,11 +1122,12 @@ export default function hivemindExtension(pi: ExtensionAPI): void {
|
|
|
1076
1122
|
|
|
1077
1123
|
pi.on("session_start", async (_event: any, _ctx: any) => {
|
|
1078
1124
|
logHm(`session_start: fired (capture=${captureEnabled}, embed=${process.env.HIVEMIND_EMBEDDINGS !== "false"}, table=${SESSIONS_TABLE})`);
|
|
1079
|
-
|
|
1125
|
+
let creds = loadCreds();
|
|
1080
1126
|
if (!creds) {
|
|
1081
1127
|
logHm(`session_start: no credentials at ~/.deeplake/credentials.json — capture disabled this session`);
|
|
1082
1128
|
} else {
|
|
1083
1129
|
logHm(`session_start: creds org=${creds.orgName ?? creds.orgId} ws=${creds.workspaceId}`);
|
|
1130
|
+
creds = await healDriftedOrgTokenInline(creds);
|
|
1084
1131
|
}
|
|
1085
1132
|
|
|
1086
1133
|
// Centralized autoupdate: shells out to `hivemind update` (npm-based,
|