clawsocial-plugin 1.6.5 → 1.6.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawsocial-plugin",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "description": "ClawSocial OpenClaw Plugin — social discovery for AI agents",
5
5
  "type": "module",
6
6
  "author": "ClawSocial",
package/src/store.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import os from "node:os";
2
3
  import path from "node:path";
3
4
 
4
5
  let _stateDir: string | null = null;
@@ -78,12 +79,26 @@ function settingsFile(): string {
78
79
  }
79
80
 
80
81
  export function getSettings(): Settings {
81
- return { ...DEFAULT_SETTINGS, ...readJSON<Partial<Settings>>(settingsFile(), {}) };
82
+ const s = readJSON<Partial<Settings>>(settingsFile(), {});
83
+ if (Object.keys(s).length === 0) {
84
+ const agentId = getState().agent_id;
85
+ if (agentId) {
86
+ const backup = backupRead<Partial<Settings>>(agentId, "settings.json", {});
87
+ if (Object.keys(backup).length > 0) {
88
+ writeJSON(settingsFile(), backup);
89
+ return { ...DEFAULT_SETTINGS, ...backup };
90
+ }
91
+ }
92
+ }
93
+ return { ...DEFAULT_SETTINGS, ...s };
82
94
  }
83
95
 
84
96
  export function setSettings(data: Partial<Settings>): void {
85
97
  const s = getSettings();
86
- writeJSON(settingsFile(), { ...s, ...data });
98
+ const merged = { ...s, ...data };
99
+ writeJSON(settingsFile(), merged);
100
+ const agentId = readJSON<AgentState>(stateFile(), {}).agent_id;
101
+ if (agentId) backupWrite(agentId, "settings.json", merged);
87
102
  }
88
103
 
89
104
  // ── Agent state ─────────────────────────────────────────────────────
@@ -100,17 +115,34 @@ export type AgentState = {
100
115
  // ── Sessions ────────────────────────────────────────────────────────
101
116
 
102
117
  export function getSessions(): SessionsMap {
103
- return readJSON<SessionsMap>(sessionsFile(), {});
118
+ const sessions = readJSON<SessionsMap>(sessionsFile(), {});
119
+ if (Object.keys(sessions).length === 0) {
120
+ const agentId = getState().agent_id;
121
+ if (agentId) {
122
+ const backup = backupRead<SessionsMap>(agentId, "sessions.json", {});
123
+ if (Object.keys(backup).length > 0) {
124
+ writeJSON(sessionsFile(), backup);
125
+ return backup;
126
+ }
127
+ }
128
+ }
129
+ return sessions;
104
130
  }
105
131
 
106
132
  export function getSession(id: string): LocalSession | null {
107
133
  return getSessions()[id] ?? null;
108
134
  }
109
135
 
136
+ function writeSessions(sessions: SessionsMap): void {
137
+ writeJSON(sessionsFile(), sessions);
138
+ const agentId = readJSON<AgentState>(stateFile(), {}).agent_id;
139
+ if (agentId) backupWrite(agentId, "sessions.json", sessions);
140
+ }
141
+
110
142
  export function upsertSession(id: string, data: Partial<LocalSession>): LocalSession {
111
143
  const sessions = getSessions();
112
144
  sessions[id] = { ...(sessions[id] ?? { id, messages: [], unread: 0 }), ...data, id };
113
- writeJSON(sessionsFile(), sessions);
145
+ writeSessions(sessions);
114
146
  return sessions[id];
115
147
  }
116
148
 
@@ -126,26 +158,91 @@ export function addMessage(sessionId: string, msg: LocalMessage): void {
126
158
  sessions[sessionId].unread = (sessions[sessionId].unread ?? 0) + 1;
127
159
  }
128
160
  sessions[sessionId].updated_at = Math.floor(Date.now() / 1000);
129
- writeJSON(sessionsFile(), sessions);
161
+ writeSessions(sessions);
130
162
  }
131
163
 
132
164
  export function markRead(sessionId: string): void {
133
165
  const sessions = getSessions();
134
166
  if (sessions[sessionId]) {
135
167
  sessions[sessionId].unread = 0;
136
- writeJSON(sessionsFile(), sessions);
168
+ writeSessions(sessions);
169
+ }
170
+ }
171
+
172
+ // ── Backup (survives plugin/OpenClaw reinstall) ─────────────────────
173
+ // Backup layout:
174
+ // ~/.clawsocial/
175
+ // last_active ← agent_id of most recently used account
176
+ // <agent_id>/
177
+ // credentials.json
178
+ // sessions.json
179
+ // settings.json
180
+ // contacts.json
181
+
182
+ const BACKUP_ROOT = path.join(os.homedir(), ".clawsocial");
183
+
184
+ function backupDir(agentId: string): string {
185
+ return path.join(BACKUP_ROOT, agentId);
186
+ }
187
+
188
+ function backupWrite(agentId: string, name: string, data: unknown): void {
189
+ try {
190
+ const dir = backupDir(agentId);
191
+ fs.mkdirSync(dir, { recursive: true });
192
+ writeJSON(path.join(dir, name), data);
193
+ // Update last_active marker
194
+ fs.writeFileSync(path.join(BACKUP_ROOT, "last_active"), agentId);
195
+ } catch {
196
+ // best-effort backup, don't fail if write fails
197
+ }
198
+ }
199
+
200
+ function backupRead<T>(agentId: string, name: string, fallback: T): T {
201
+ try {
202
+ return readJSON<T>(path.join(backupDir(agentId), name), fallback);
203
+ } catch {
204
+ return fallback;
205
+ }
206
+ }
207
+
208
+ function getLastActiveAgentId(): string | null {
209
+ try {
210
+ return fs.readFileSync(path.join(BACKUP_ROOT, "last_active"), "utf8").trim() || null;
211
+ } catch {
212
+ return null;
137
213
  }
138
214
  }
139
215
 
140
216
  // ── Agent state ─────────────────────────────────────────────────────
141
217
 
142
218
  export function getState(): AgentState {
143
- return readJSON<AgentState>(stateFile(), {});
219
+ const state = readJSON<AgentState>(stateFile(), {});
220
+ if (!state.agent_id || !state.api_key) {
221
+ // Try restoring from backup using last_active agent
222
+ const lastId = getLastActiveAgentId();
223
+ if (lastId) {
224
+ const backup = backupRead<AgentState>(lastId, "credentials.json", {});
225
+ if (backup.agent_id && backup.api_key) {
226
+ writeJSON(stateFile(), backup);
227
+ return backup;
228
+ }
229
+ }
230
+ }
231
+ return state;
144
232
  }
145
233
 
146
234
  export function setState(data: Partial<AgentState>): void {
147
235
  const s = getState();
148
- writeJSON(stateFile(), { ...s, ...data });
236
+ const merged = { ...s, ...data };
237
+ writeJSON(stateFile(), merged);
238
+ if (merged.agent_id && merged.api_key) {
239
+ backupWrite(merged.agent_id, "credentials.json", {
240
+ agent_id: merged.agent_id,
241
+ api_key: merged.api_key,
242
+ public_name: merged.public_name,
243
+ lang: merged.lang,
244
+ });
245
+ }
149
246
  }
150
247
 
151
248
  // ── Contacts ─────────────────────────────────────────────────────────
@@ -166,10 +263,20 @@ function contactsFile(): string {
166
263
  export function readContacts(): Contact[] {
167
264
  try {
168
265
  const data = JSON.parse(fs.readFileSync(contactsFile(), "utf8"));
169
- return Array.isArray(data?.contacts) ? data.contacts : [];
266
+ if (Array.isArray(data?.contacts) && data.contacts.length > 0) return data.contacts;
170
267
  } catch {
171
- return [];
268
+ // fall through to backup
269
+ }
270
+ const agentId = getState().agent_id;
271
+ if (agentId) {
272
+ const backup = backupRead<{ contacts?: Contact[] }>(agentId, "contacts.json", {});
273
+ if (Array.isArray(backup?.contacts) && backup.contacts.length > 0) {
274
+ fs.mkdirSync(path.dirname(contactsFile()), { recursive: true });
275
+ fs.writeFileSync(contactsFile(), JSON.stringify({ contacts: backup.contacts }, null, 2));
276
+ return backup.contacts;
277
+ }
172
278
  }
279
+ return [];
173
280
  }
174
281
 
175
282
  export function upsertContact(contact: Omit<Contact, "added_at"> & { added_at?: number }): void {
@@ -181,7 +288,10 @@ export function upsertContact(contact: Omit<Contact, "added_at"> & { added_at?:
181
288
  } else {
182
289
  contacts.push(entry);
183
290
  }
184
- fs.writeFileSync(contactsFile(), JSON.stringify({ contacts }, null, 2));
291
+ const data = { contacts };
292
+ fs.writeFileSync(contactsFile(), JSON.stringify(data, null, 2));
293
+ const agentId = readJSON<AgentState>(stateFile(), {}).agent_id;
294
+ if (agentId) backupWrite(agentId, "contacts.json", data);
185
295
  }
186
296
 
187
297
  export function lookupContactByName(name: string): Contact[] {
@@ -41,7 +41,6 @@ export function createMatchTool(): AnyAgentTool {
41
41
  public_name: c.public_name,
42
42
  topic_tags: c.topic_tags,
43
43
  match_score: Math.round(c.match_score * 100) + "%",
44
- availability: c.availability,
45
44
  completeness: Math.round((c.completeness_score ?? 0.1) * 100) + "%",
46
45
  ...(c.manual_intro ? { manual_intro: c.manual_intro } : {}),
47
46
  ...(c.auto_bio ? { auto_bio: c.auto_bio } : {}),