@id-wispera/core 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.
Files changed (154) hide show
  1. package/README.md +268 -0
  2. package/dist/audit.d.ts +68 -0
  3. package/dist/audit.d.ts.map +1 -0
  4. package/dist/audit.js +252 -0
  5. package/dist/audit.js.map +1 -0
  6. package/dist/auth/index.d.ts +8 -0
  7. package/dist/auth/index.d.ts.map +1 -0
  8. package/dist/auth/index.js +8 -0
  9. package/dist/auth/index.js.map +1 -0
  10. package/dist/auth/keychainProvider.d.ts +40 -0
  11. package/dist/auth/keychainProvider.d.ts.map +1 -0
  12. package/dist/auth/keychainProvider.js +98 -0
  13. package/dist/auth/keychainProvider.js.map +1 -0
  14. package/dist/auth/passphraseProvider.d.ts +80 -0
  15. package/dist/auth/passphraseProvider.d.ts.map +1 -0
  16. package/dist/auth/passphraseProvider.js +188 -0
  17. package/dist/auth/passphraseProvider.js.map +1 -0
  18. package/dist/auth/sessionTokenManager.d.ts +106 -0
  19. package/dist/auth/sessionTokenManager.d.ts.map +1 -0
  20. package/dist/auth/sessionTokenManager.js +263 -0
  21. package/dist/auth/sessionTokenManager.js.map +1 -0
  22. package/dist/delegation.d.ts +81 -0
  23. package/dist/delegation.d.ts.map +1 -0
  24. package/dist/delegation.js +299 -0
  25. package/dist/delegation.js.map +1 -0
  26. package/dist/detection.d.ts +35 -0
  27. package/dist/detection.d.ts.map +1 -0
  28. package/dist/detection.js +474 -0
  29. package/dist/detection.js.map +1 -0
  30. package/dist/exec/execManager.d.ts +60 -0
  31. package/dist/exec/execManager.d.ts.map +1 -0
  32. package/dist/exec/execManager.js +226 -0
  33. package/dist/exec/execManager.js.map +1 -0
  34. package/dist/exec/index.d.ts +6 -0
  35. package/dist/exec/index.d.ts.map +1 -0
  36. package/dist/exec/index.js +5 -0
  37. package/dist/exec/index.js.map +1 -0
  38. package/dist/index.d.ts +35 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +98 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/integrations/base.d.ts +64 -0
  43. package/dist/integrations/base.d.ts.map +1 -0
  44. package/dist/integrations/base.js +173 -0
  45. package/dist/integrations/base.js.map +1 -0
  46. package/dist/integrations/envMapping.d.ts +47 -0
  47. package/dist/integrations/envMapping.d.ts.map +1 -0
  48. package/dist/integrations/envMapping.js +174 -0
  49. package/dist/integrations/envMapping.js.map +1 -0
  50. package/dist/integrations/google-a2a.d.ts +48 -0
  51. package/dist/integrations/google-a2a.d.ts.map +1 -0
  52. package/dist/integrations/google-a2a.js +108 -0
  53. package/dist/integrations/google-a2a.js.map +1 -0
  54. package/dist/integrations/index.d.ts +14 -0
  55. package/dist/integrations/index.d.ts.map +1 -0
  56. package/dist/integrations/index.js +14 -0
  57. package/dist/integrations/index.js.map +1 -0
  58. package/dist/integrations/langchain.d.ts +38 -0
  59. package/dist/integrations/langchain.d.ts.map +1 -0
  60. package/dist/integrations/langchain.js +45 -0
  61. package/dist/integrations/langchain.js.map +1 -0
  62. package/dist/integrations/openai-agents.d.ts +76 -0
  63. package/dist/integrations/openai-agents.d.ts.map +1 -0
  64. package/dist/integrations/openai-agents.js +95 -0
  65. package/dist/integrations/openai-agents.js.map +1 -0
  66. package/dist/integrations/slack.d.ts +59 -0
  67. package/dist/integrations/slack.d.ts.map +1 -0
  68. package/dist/integrations/slack.js +113 -0
  69. package/dist/integrations/slack.js.map +1 -0
  70. package/dist/integrations/types.d.ts +107 -0
  71. package/dist/integrations/types.d.ts.map +1 -0
  72. package/dist/integrations/types.js +6 -0
  73. package/dist/integrations/types.js.map +1 -0
  74. package/dist/locations.d.ts +157 -0
  75. package/dist/locations.d.ts.map +1 -0
  76. package/dist/locations.js +733 -0
  77. package/dist/locations.js.map +1 -0
  78. package/dist/passport.d.ts +70 -0
  79. package/dist/passport.d.ts.map +1 -0
  80. package/dist/passport.js +429 -0
  81. package/dist/passport.js.map +1 -0
  82. package/dist/policy.d.ts +80 -0
  83. package/dist/policy.d.ts.map +1 -0
  84. package/dist/policy.js +392 -0
  85. package/dist/policy.js.map +1 -0
  86. package/dist/providers/openclaw.d.ts +80 -0
  87. package/dist/providers/openclaw.d.ts.map +1 -0
  88. package/dist/providers/openclaw.js +712 -0
  89. package/dist/providers/openclaw.js.map +1 -0
  90. package/dist/provisioning/adminPassport.d.ts +51 -0
  91. package/dist/provisioning/adminPassport.d.ts.map +1 -0
  92. package/dist/provisioning/adminPassport.js +101 -0
  93. package/dist/provisioning/adminPassport.js.map +1 -0
  94. package/dist/provisioning/index.d.ts +81 -0
  95. package/dist/provisioning/index.d.ts.map +1 -0
  96. package/dist/provisioning/index.js +141 -0
  97. package/dist/provisioning/index.js.map +1 -0
  98. package/dist/provisioning/provider.d.ts +59 -0
  99. package/dist/provisioning/provider.d.ts.map +1 -0
  100. package/dist/provisioning/provider.js +52 -0
  101. package/dist/provisioning/provider.js.map +1 -0
  102. package/dist/provisioning/providers/anthropic.d.ts +32 -0
  103. package/dist/provisioning/providers/anthropic.d.ts.map +1 -0
  104. package/dist/provisioning/providers/anthropic.js +116 -0
  105. package/dist/provisioning/providers/anthropic.js.map +1 -0
  106. package/dist/provisioning/providers/aws.d.ts +29 -0
  107. package/dist/provisioning/providers/aws.d.ts.map +1 -0
  108. package/dist/provisioning/providers/aws.js +455 -0
  109. package/dist/provisioning/providers/aws.js.map +1 -0
  110. package/dist/provisioning/providers/azure-entra.d.ts +32 -0
  111. package/dist/provisioning/providers/azure-entra.d.ts.map +1 -0
  112. package/dist/provisioning/providers/azure-entra.js +312 -0
  113. package/dist/provisioning/providers/azure-entra.js.map +1 -0
  114. package/dist/provisioning/providers/github.d.ts +24 -0
  115. package/dist/provisioning/providers/github.d.ts.map +1 -0
  116. package/dist/provisioning/providers/github.js +219 -0
  117. package/dist/provisioning/providers/github.js.map +1 -0
  118. package/dist/provisioning/providers/google-cloud.d.ts +34 -0
  119. package/dist/provisioning/providers/google-cloud.d.ts.map +1 -0
  120. package/dist/provisioning/providers/google-cloud.js +366 -0
  121. package/dist/provisioning/providers/google-cloud.js.map +1 -0
  122. package/dist/provisioning/providers/openai.d.ts +29 -0
  123. package/dist/provisioning/providers/openai.d.ts.map +1 -0
  124. package/dist/provisioning/providers/openai.js +263 -0
  125. package/dist/provisioning/providers/openai.js.map +1 -0
  126. package/dist/provisioning/providers/sendgrid.d.ts +27 -0
  127. package/dist/provisioning/providers/sendgrid.d.ts.map +1 -0
  128. package/dist/provisioning/providers/sendgrid.js +186 -0
  129. package/dist/provisioning/providers/sendgrid.js.map +1 -0
  130. package/dist/provisioning/providers/twilio.d.ts +27 -0
  131. package/dist/provisioning/providers/twilio.d.ts.map +1 -0
  132. package/dist/provisioning/providers/twilio.js +194 -0
  133. package/dist/provisioning/providers/twilio.js.map +1 -0
  134. package/dist/provisioning/types.d.ts +274 -0
  135. package/dist/provisioning/types.d.ts.map +1 -0
  136. package/dist/provisioning/types.js +6 -0
  137. package/dist/provisioning/types.js.map +1 -0
  138. package/dist/sharing.d.ts +60 -0
  139. package/dist/sharing.d.ts.map +1 -0
  140. package/dist/sharing.js +305 -0
  141. package/dist/sharing.js.map +1 -0
  142. package/dist/types.d.ts +396 -0
  143. package/dist/types.d.ts.map +1 -0
  144. package/dist/types.js +88 -0
  145. package/dist/types.js.map +1 -0
  146. package/dist/utils.d.ts +45 -0
  147. package/dist/utils.d.ts.map +1 -0
  148. package/dist/utils.js +110 -0
  149. package/dist/utils.js.map +1 -0
  150. package/dist/vault.d.ts +151 -0
  151. package/dist/vault.d.ts.map +1 -0
  152. package/dist/vault.js +499 -0
  153. package/dist/vault.js.map +1 -0
  154. package/package.json +117 -0
@@ -0,0 +1,712 @@
1
+ /**
2
+ * ID Wispera OpenClaw Provider
3
+ * Specialized scanner for OpenClaw credential locations
4
+ */
5
+ import { homedir } from 'os';
6
+ import { join } from 'path';
7
+ import { sortByRisk, getRiskEmoji, getRiskLabel, } from '../locations.js';
8
+ // ============================================================================
9
+ // OpenClaw Path Constants
10
+ // ============================================================================
11
+ export const OPENCLAW_BASE_PATH = join(homedir(), '.openclaw');
12
+ export const OPENCLAW_PATHS = {
13
+ /** Base OpenClaw directory */
14
+ base: OPENCLAW_BASE_PATH,
15
+ /** WhatsApp credentials directory */
16
+ whatsapp: join(OPENCLAW_BASE_PATH, 'credentials', 'whatsapp'),
17
+ /** Main credentials directory */
18
+ credentials: join(OPENCLAW_BASE_PATH, 'credentials'),
19
+ /** Agents directory */
20
+ agents: join(OPENCLAW_BASE_PATH, 'agents'),
21
+ /** Main config file */
22
+ config: join(OPENCLAW_BASE_PATH, 'openclaw.json'),
23
+ /** OAuth tokens */
24
+ oauth: join(OPENCLAW_BASE_PATH, 'credentials', 'oauth.json'),
25
+ };
26
+ // ============================================================================
27
+ // OpenClaw Detection
28
+ // ============================================================================
29
+ /**
30
+ * Check if OpenClaw is installed
31
+ */
32
+ export async function isOpenClawInstalled() {
33
+ const fs = await import('fs/promises');
34
+ try {
35
+ await fs.access(OPENCLAW_BASE_PATH);
36
+ return true;
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ /**
43
+ * Check file permissions and return warning if insecure
44
+ */
45
+ export async function checkFilePermissions(filePath) {
46
+ const fs = await import('fs/promises');
47
+ try {
48
+ const stats = await fs.stat(filePath);
49
+ const mode = stats.mode;
50
+ // Check if world or group readable (not 600 or 700)
51
+ const worldReadable = (mode & 0o004) !== 0;
52
+ const groupReadable = (mode & 0o040) !== 0;
53
+ if (worldReadable) {
54
+ return `File is world-readable (mode: ${(mode & 0o777).toString(8)}). Run: chmod 600 "${filePath}"`;
55
+ }
56
+ if (groupReadable) {
57
+ return `File is group-readable (mode: ${(mode & 0o777).toString(8)}). Run: chmod 600 "${filePath}"`;
58
+ }
59
+ return undefined;
60
+ }
61
+ catch {
62
+ return undefined;
63
+ }
64
+ }
65
+ /**
66
+ * Check directory permissions
67
+ */
68
+ export async function checkDirectoryPermissions(dirPath) {
69
+ const fs = await import('fs/promises');
70
+ try {
71
+ const stats = await fs.stat(dirPath);
72
+ const mode = stats.mode;
73
+ const worldReadable = (mode & 0o004) !== 0;
74
+ const groupReadable = (mode & 0o040) !== 0;
75
+ if (worldReadable || groupReadable) {
76
+ return `Directory has insecure permissions (mode: ${(mode & 0o777).toString(8)}). Run: chmod 700 "${dirPath}"`;
77
+ }
78
+ return undefined;
79
+ }
80
+ catch {
81
+ return undefined;
82
+ }
83
+ }
84
+ /**
85
+ * Scan all OpenClaw credential locations
86
+ */
87
+ export async function scanOpenClaw() {
88
+ const fs = await import('fs/promises');
89
+ const result = {
90
+ installed: false,
91
+ installPath: OPENCLAW_BASE_PATH,
92
+ credentials: [],
93
+ locationResults: [],
94
+ summary: {
95
+ total: 0,
96
+ governed: 0,
97
+ criticalCount: 0,
98
+ highCount: 0,
99
+ mediumCount: 0,
100
+ lowCount: 0,
101
+ },
102
+ permissionWarnings: [],
103
+ unreadablePaths: [],
104
+ };
105
+ // Check if OpenClaw is installed
106
+ result.installed = await isOpenClawInstalled();
107
+ if (!result.installed) {
108
+ return result;
109
+ }
110
+ // Check base directory permissions
111
+ const baseDirWarning = await checkDirectoryPermissions(OPENCLAW_BASE_PATH);
112
+ if (baseDirWarning) {
113
+ result.permissionWarnings.push(baseDirWarning);
114
+ }
115
+ // Scan each location
116
+ await Promise.all([
117
+ scanWhatsAppCredentials(result, fs),
118
+ scanChannelAllowlists(result, fs),
119
+ scanAuthProfiles(result, fs),
120
+ scanOAuthTokens(result, fs),
121
+ scanOpenClawConfig(result, fs),
122
+ ]);
123
+ // Calculate summary
124
+ for (const cred of result.credentials) {
125
+ result.summary.total++;
126
+ switch (cred.classification.riskLevel) {
127
+ case 'critical':
128
+ result.summary.criticalCount++;
129
+ break;
130
+ case 'high':
131
+ result.summary.highCount++;
132
+ break;
133
+ case 'medium':
134
+ result.summary.mediumCount++;
135
+ break;
136
+ case 'low':
137
+ result.summary.lowCount++;
138
+ break;
139
+ }
140
+ }
141
+ // Sort credentials by risk
142
+ result.credentials = sortByRisk(result.credentials);
143
+ return result;
144
+ }
145
+ /**
146
+ * Scan WhatsApp credentials
147
+ */
148
+ async function scanWhatsAppCredentials(result, fs) {
149
+ const locationResult = {
150
+ path: OPENCLAW_PATHS.whatsapp,
151
+ exists: false,
152
+ readable: false,
153
+ credentials: [],
154
+ };
155
+ try {
156
+ await fs.access(OPENCLAW_PATHS.whatsapp);
157
+ locationResult.exists = true;
158
+ const accounts = await fs.readdir(OPENCLAW_PATHS.whatsapp);
159
+ locationResult.readable = true;
160
+ for (const accountId of accounts) {
161
+ const credsPath = join(OPENCLAW_PATHS.whatsapp, accountId, 'creds.json');
162
+ try {
163
+ const content = await fs.readFile(credsPath, 'utf-8');
164
+ const permWarning = await checkFilePermissions(credsPath);
165
+ const cred = parseWhatsAppCreds(credsPath, content, accountId);
166
+ if (cred) {
167
+ if (permWarning) {
168
+ cred.insecurePermissions = true;
169
+ result.permissionWarnings.push(permWarning);
170
+ }
171
+ locationResult.credentials.push(cred);
172
+ result.credentials.push(cred);
173
+ }
174
+ }
175
+ catch (err) {
176
+ if (err.code === 'EACCES') {
177
+ result.unreadablePaths.push({ path: credsPath, error: 'Permission denied' });
178
+ }
179
+ }
180
+ }
181
+ }
182
+ catch (err) {
183
+ if (err.code === 'ENOENT') {
184
+ // Directory doesn't exist, that's fine
185
+ }
186
+ else if (err.code === 'EACCES') {
187
+ locationResult.error = 'Permission denied';
188
+ result.unreadablePaths.push({ path: OPENCLAW_PATHS.whatsapp, error: 'Permission denied' });
189
+ }
190
+ }
191
+ result.locationResults.push(locationResult);
192
+ }
193
+ /**
194
+ * Parse WhatsApp credentials file
195
+ */
196
+ function parseWhatsAppCreds(filePath, content, accountId) {
197
+ try {
198
+ const data = JSON.parse(content);
199
+ const phoneNumber = data.me?.id?.split(':')[0] || accountId;
200
+ const botName = data.me?.name || 'Unknown';
201
+ return {
202
+ provider: 'openclaw',
203
+ filePath,
204
+ classification: {
205
+ name: `WhatsApp Session (${accountId})`,
206
+ credentialType: 'session-keys',
207
+ visaType: 'access',
208
+ riskLevel: 'high',
209
+ platforms: ['openclaw', 'whatsapp'],
210
+ tags: ['openclaw', 'whatsapp', 'session', 'imported'],
211
+ issuingAuthority: 'OpenClaw (self-managed)',
212
+ },
213
+ rawValue: content,
214
+ metadata: {
215
+ accountId,
216
+ phoneNumber,
217
+ botName,
218
+ registrationId: data.registrationId,
219
+ hasNoiseKey: !!data.noiseKey,
220
+ hasSignedIdentityKey: !!data.signedIdentityKey,
221
+ },
222
+ };
223
+ }
224
+ catch {
225
+ return null;
226
+ }
227
+ }
228
+ /**
229
+ * Scan channel allowlists
230
+ */
231
+ async function scanChannelAllowlists(result, fs) {
232
+ const locationResult = {
233
+ path: OPENCLAW_PATHS.credentials,
234
+ exists: false,
235
+ readable: false,
236
+ credentials: [],
237
+ };
238
+ try {
239
+ await fs.access(OPENCLAW_PATHS.credentials);
240
+ locationResult.exists = true;
241
+ const files = await fs.readdir(OPENCLAW_PATHS.credentials);
242
+ locationResult.readable = true;
243
+ for (const file of files) {
244
+ if (file.endsWith('-allowFrom.json')) {
245
+ const filePath = join(OPENCLAW_PATHS.credentials, file);
246
+ try {
247
+ const content = await fs.readFile(filePath, 'utf-8');
248
+ const channel = file.replace('-allowFrom.json', '');
249
+ const cred = parseAllowlist(filePath, content, channel);
250
+ if (cred) {
251
+ const permWarning = await checkFilePermissions(filePath);
252
+ if (permWarning) {
253
+ cred.insecurePermissions = true;
254
+ result.permissionWarnings.push(permWarning);
255
+ }
256
+ locationResult.credentials.push(cred);
257
+ result.credentials.push(cred);
258
+ }
259
+ }
260
+ catch (err) {
261
+ if (err.code === 'EACCES') {
262
+ result.unreadablePaths.push({ path: filePath, error: 'Permission denied' });
263
+ }
264
+ }
265
+ }
266
+ }
267
+ }
268
+ catch (err) {
269
+ if (err.code === 'EACCES') {
270
+ locationResult.error = 'Permission denied';
271
+ result.unreadablePaths.push({ path: OPENCLAW_PATHS.credentials, error: 'Permission denied' });
272
+ }
273
+ }
274
+ result.locationResults.push(locationResult);
275
+ }
276
+ /**
277
+ * Parse channel allowlist file
278
+ */
279
+ function parseAllowlist(filePath, content, channel) {
280
+ try {
281
+ const data = JSON.parse(content);
282
+ const pairedCount = Object.keys(data).length;
283
+ return {
284
+ provider: 'openclaw',
285
+ filePath,
286
+ classification: {
287
+ name: `Pairing: ${channel} (${pairedCount} paired users)`,
288
+ credentialType: 'custom',
289
+ visaType: 'access',
290
+ riskLevel: 'low',
291
+ platforms: ['openclaw', channel],
292
+ tags: ['openclaw', channel, 'allowlist', 'imported'],
293
+ issuingAuthority: 'OpenClaw (self-managed)',
294
+ },
295
+ rawValue: content,
296
+ metadata: {
297
+ channel,
298
+ pairedCount,
299
+ pairedUsers: Object.entries(data).map(([id, info]) => ({
300
+ id,
301
+ name: info.name,
302
+ pairedAt: info.pairedAt,
303
+ })),
304
+ },
305
+ };
306
+ }
307
+ catch {
308
+ return null;
309
+ }
310
+ }
311
+ /**
312
+ * Scan auth profiles (LLM API keys)
313
+ */
314
+ async function scanAuthProfiles(result, fs) {
315
+ const locationResult = {
316
+ path: OPENCLAW_PATHS.agents,
317
+ exists: false,
318
+ readable: false,
319
+ credentials: [],
320
+ };
321
+ try {
322
+ await fs.access(OPENCLAW_PATHS.agents);
323
+ locationResult.exists = true;
324
+ const agents = await fs.readdir(OPENCLAW_PATHS.agents);
325
+ locationResult.readable = true;
326
+ for (const agentId of agents) {
327
+ const authProfilesPath = join(OPENCLAW_PATHS.agents, agentId, 'agent', 'auth-profiles.json');
328
+ try {
329
+ const content = await fs.readFile(authProfilesPath, 'utf-8');
330
+ const creds = parseAuthProfiles(authProfilesPath, content, agentId);
331
+ for (const cred of creds) {
332
+ const permWarning = await checkFilePermissions(authProfilesPath);
333
+ if (permWarning) {
334
+ cred.insecurePermissions = true;
335
+ if (!result.permissionWarnings.includes(permWarning)) {
336
+ result.permissionWarnings.push(permWarning);
337
+ }
338
+ }
339
+ locationResult.credentials.push(cred);
340
+ result.credentials.push(cred);
341
+ }
342
+ }
343
+ catch (err) {
344
+ if (err.code === 'EACCES') {
345
+ result.unreadablePaths.push({ path: authProfilesPath, error: 'Permission denied' });
346
+ }
347
+ // ENOENT is fine - agent may not have auth profiles
348
+ }
349
+ }
350
+ }
351
+ catch (err) {
352
+ if (err.code === 'EACCES') {
353
+ locationResult.error = 'Permission denied';
354
+ result.unreadablePaths.push({ path: OPENCLAW_PATHS.agents, error: 'Permission denied' });
355
+ }
356
+ }
357
+ result.locationResults.push(locationResult);
358
+ }
359
+ /**
360
+ * Parse auth profiles file
361
+ */
362
+ function parseAuthProfiles(filePath, content, agentId) {
363
+ const credentials = [];
364
+ try {
365
+ const data = JSON.parse(content);
366
+ for (const [provider, profile] of Object.entries(data)) {
367
+ if (profile.type === 'api-key' && profile.key) {
368
+ const keyPreview = profile.key.substring(0, 10) + '...' + profile.key.substring(profile.key.length - 4);
369
+ credentials.push({
370
+ provider: 'openclaw',
371
+ filePath,
372
+ classification: {
373
+ name: `${capitalizeFirst(provider)} API Key (${keyPreview})`,
374
+ credentialType: 'api-key',
375
+ visaType: 'privilege',
376
+ riskLevel: 'critical',
377
+ platforms: ['openclaw', 'mcp', provider],
378
+ tags: ['openclaw', provider, 'api-key', 'llm', 'imported'],
379
+ issuingAuthority: 'OpenClaw (self-managed)',
380
+ context: { agentId, model: profile.model },
381
+ },
382
+ rawValue: profile.key,
383
+ metadata: {
384
+ agentId,
385
+ provider,
386
+ model: profile.model,
387
+ keyType: profile.type,
388
+ },
389
+ });
390
+ }
391
+ }
392
+ }
393
+ catch {
394
+ // Invalid JSON
395
+ }
396
+ return credentials;
397
+ }
398
+ /**
399
+ * Scan OAuth tokens
400
+ */
401
+ async function scanOAuthTokens(result, fs) {
402
+ const locationResult = {
403
+ path: OPENCLAW_PATHS.oauth,
404
+ exists: false,
405
+ readable: false,
406
+ credentials: [],
407
+ };
408
+ try {
409
+ const content = await fs.readFile(OPENCLAW_PATHS.oauth, 'utf-8');
410
+ locationResult.exists = true;
411
+ locationResult.readable = true;
412
+ const permWarning = await checkFilePermissions(OPENCLAW_PATHS.oauth);
413
+ const creds = parseOAuthTokens(OPENCLAW_PATHS.oauth, content);
414
+ for (const cred of creds) {
415
+ if (permWarning) {
416
+ cred.insecurePermissions = true;
417
+ if (!result.permissionWarnings.includes(permWarning)) {
418
+ result.permissionWarnings.push(permWarning);
419
+ }
420
+ }
421
+ locationResult.credentials.push(cred);
422
+ result.credentials.push(cred);
423
+ }
424
+ }
425
+ catch (err) {
426
+ if (err.code === 'EACCES') {
427
+ locationResult.error = 'Permission denied';
428
+ result.unreadablePaths.push({ path: OPENCLAW_PATHS.oauth, error: 'Permission denied' });
429
+ }
430
+ // ENOENT is fine
431
+ }
432
+ result.locationResults.push(locationResult);
433
+ }
434
+ /**
435
+ * Parse OAuth tokens file
436
+ */
437
+ function parseOAuthTokens(filePath, content) {
438
+ const credentials = [];
439
+ try {
440
+ const data = JSON.parse(content);
441
+ for (const [provider, tokens] of Object.entries(data)) {
442
+ if (tokens.access_token) {
443
+ const tokenPreview = tokens.access_token.substring(0, 10) + '...';
444
+ const expiresAt = tokens.expires_at ? new Date(tokens.expires_at * 1000) : undefined;
445
+ const isExpired = expiresAt && expiresAt < new Date();
446
+ credentials.push({
447
+ provider: 'openclaw',
448
+ filePath,
449
+ classification: {
450
+ name: `${capitalizeFirst(provider)} OAuth (${tokenPreview}${isExpired ? ' EXPIRED' : ''})`,
451
+ credentialType: 'oauth-token',
452
+ visaType: 'access',
453
+ riskLevel: 'medium',
454
+ platforms: ['openclaw', provider],
455
+ tags: ['openclaw', provider, 'oauth', 'imported', ...(isExpired ? ['expired'] : [])],
456
+ issuingAuthority: 'OpenClaw (self-managed)',
457
+ },
458
+ rawValue: tokens.access_token,
459
+ metadata: {
460
+ provider,
461
+ hasRefreshToken: !!tokens.refresh_token,
462
+ tokenType: tokens.token_type,
463
+ expiresAt: expiresAt?.toISOString(),
464
+ isExpired,
465
+ },
466
+ expiresAt,
467
+ });
468
+ }
469
+ }
470
+ }
471
+ catch {
472
+ // Invalid JSON
473
+ }
474
+ return credentials;
475
+ }
476
+ /**
477
+ * Scan OpenClaw config file
478
+ */
479
+ async function scanOpenClawConfig(result, fs) {
480
+ const locationResult = {
481
+ path: OPENCLAW_PATHS.config,
482
+ exists: false,
483
+ readable: false,
484
+ credentials: [],
485
+ };
486
+ try {
487
+ const content = await fs.readFile(OPENCLAW_PATHS.config, 'utf-8');
488
+ locationResult.exists = true;
489
+ locationResult.readable = true;
490
+ const permWarning = await checkFilePermissions(OPENCLAW_PATHS.config);
491
+ const creds = parseOpenClawConfig(OPENCLAW_PATHS.config, content);
492
+ for (const cred of creds) {
493
+ if (permWarning) {
494
+ cred.insecurePermissions = true;
495
+ if (!result.permissionWarnings.includes(permWarning)) {
496
+ result.permissionWarnings.push(permWarning);
497
+ }
498
+ }
499
+ locationResult.credentials.push(cred);
500
+ result.credentials.push(cred);
501
+ }
502
+ }
503
+ catch (err) {
504
+ if (err.code === 'EACCES') {
505
+ locationResult.error = 'Permission denied';
506
+ result.unreadablePaths.push({ path: OPENCLAW_PATHS.config, error: 'Permission denied' });
507
+ }
508
+ // ENOENT is fine
509
+ }
510
+ result.locationResults.push(locationResult);
511
+ }
512
+ /**
513
+ * Parse OpenClaw config file
514
+ */
515
+ function parseOpenClawConfig(filePath, content) {
516
+ const credentials = [];
517
+ try {
518
+ const data = JSON.parse(content);
519
+ // Parse channel tokens
520
+ if (data.channels) {
521
+ // Telegram
522
+ if (data.channels.telegram?.token) {
523
+ const token = data.channels.telegram.token;
524
+ const tokenPreview = token.substring(0, 7) + '...' + token.substring(token.length - 4);
525
+ credentials.push({
526
+ provider: 'openclaw',
527
+ filePath,
528
+ classification: {
529
+ name: `Telegram Bot Token (${tokenPreview})`,
530
+ credentialType: 'bot-token',
531
+ visaType: 'access',
532
+ riskLevel: 'high',
533
+ platforms: ['openclaw', 'telegram'],
534
+ tags: ['openclaw', 'telegram', 'bot-token', 'imported'],
535
+ issuingAuthority: 'OpenClaw (self-managed)',
536
+ },
537
+ rawValue: token,
538
+ metadata: { channel: 'telegram' },
539
+ });
540
+ }
541
+ // Slack
542
+ if (data.channels.slack) {
543
+ if (data.channels.slack.botToken) {
544
+ const token = data.channels.slack.botToken;
545
+ const tokenPreview = token.substring(0, 10) + '...' + token.substring(token.length - 4);
546
+ credentials.push({
547
+ provider: 'openclaw',
548
+ filePath,
549
+ classification: {
550
+ name: `Slack Bot Token (${tokenPreview})`,
551
+ credentialType: 'bot-token',
552
+ visaType: 'access',
553
+ riskLevel: 'high',
554
+ platforms: ['openclaw', 'slack'],
555
+ tags: ['openclaw', 'slack', 'bot-token', 'imported'],
556
+ issuingAuthority: 'OpenClaw (self-managed)',
557
+ },
558
+ rawValue: token,
559
+ metadata: { channel: 'slack', tokenType: 'bot' },
560
+ });
561
+ }
562
+ if (data.channels.slack.appToken) {
563
+ const token = data.channels.slack.appToken;
564
+ const tokenPreview = token.substring(0, 10) + '...' + token.substring(token.length - 4);
565
+ credentials.push({
566
+ provider: 'openclaw',
567
+ filePath,
568
+ classification: {
569
+ name: `Slack App Token (${tokenPreview})`,
570
+ credentialType: 'bot-token',
571
+ visaType: 'access',
572
+ riskLevel: 'high',
573
+ platforms: ['openclaw', 'slack'],
574
+ tags: ['openclaw', 'slack', 'app-token', 'imported'],
575
+ issuingAuthority: 'OpenClaw (self-managed)',
576
+ },
577
+ rawValue: token,
578
+ metadata: { channel: 'slack', tokenType: 'app' },
579
+ });
580
+ }
581
+ }
582
+ // Discord
583
+ if (data.channels.discord?.token) {
584
+ const token = data.channels.discord.token;
585
+ const tokenPreview = token.substring(0, 10) + '...' + token.substring(token.length - 4);
586
+ credentials.push({
587
+ provider: 'openclaw',
588
+ filePath,
589
+ classification: {
590
+ name: `Discord Bot Token (${tokenPreview})`,
591
+ credentialType: 'bot-token',
592
+ visaType: 'access',
593
+ riskLevel: 'high',
594
+ platforms: ['openclaw', 'discord'],
595
+ tags: ['openclaw', 'discord', 'bot-token', 'imported'],
596
+ issuingAuthority: 'OpenClaw (self-managed)',
597
+ },
598
+ rawValue: token,
599
+ metadata: { channel: 'discord' },
600
+ });
601
+ }
602
+ }
603
+ // Gateway token
604
+ if (data.gateway?.token) {
605
+ const token = data.gateway.token;
606
+ const tokenPreview = token.substring(0, 6) + '...' + token.substring(token.length - 4);
607
+ credentials.push({
608
+ provider: 'openclaw',
609
+ filePath,
610
+ classification: {
611
+ name: `Gateway Token (${tokenPreview})`,
612
+ credentialType: 'api-key',
613
+ visaType: 'privilege',
614
+ riskLevel: 'high',
615
+ platforms: ['openclaw'],
616
+ tags: ['openclaw', 'gateway', 'admin', 'imported'],
617
+ issuingAuthority: 'OpenClaw (self-managed)',
618
+ },
619
+ rawValue: token,
620
+ metadata: { gatewayPort: data.gateway.port },
621
+ });
622
+ }
623
+ }
624
+ catch {
625
+ // Invalid JSON
626
+ }
627
+ return credentials;
628
+ }
629
+ /**
630
+ * Helper to capitalize first letter
631
+ */
632
+ function capitalizeFirst(str) {
633
+ return str.charAt(0).toUpperCase() + str.slice(1);
634
+ }
635
+ // ============================================================================
636
+ // Import Helpers
637
+ // ============================================================================
638
+ /**
639
+ * Convert discovered credential to passport input
640
+ */
641
+ export function toPassportInput(cred, humanOwner, agentId) {
642
+ const delegationChain = [
643
+ {
644
+ from: humanOwner,
645
+ to: 'OpenClaw Instance',
646
+ grantedAt: new Date().toISOString(),
647
+ scope: ['*'],
648
+ },
649
+ ];
650
+ if (agentId || cred.metadata.agentId) {
651
+ delegationChain.push({
652
+ from: 'OpenClaw Instance',
653
+ to: `Agent: ${agentId || cred.metadata.agentId}`,
654
+ grantedAt: new Date().toISOString(),
655
+ scope: ['*'],
656
+ });
657
+ }
658
+ return {
659
+ name: `OpenClaw - ${cred.classification.name}`,
660
+ agentId: (agentId || cred.metadata.agentId),
661
+ credentialType: cred.classification.credentialType,
662
+ credentialValue: cred.rawValue,
663
+ visaType: cred.classification.visaType,
664
+ issuingAuthority: cred.classification.issuingAuthority,
665
+ scope: ['*'],
666
+ platforms: cred.classification.platforms,
667
+ validFrom: new Date().toISOString(),
668
+ validUntil: cred.expiresAt?.toISOString(),
669
+ delegationChain,
670
+ humanOwner,
671
+ tags: cred.classification.tags,
672
+ notes: `Imported from OpenClaw: ${cred.filePath}`,
673
+ };
674
+ }
675
+ // ============================================================================
676
+ // Formatting Helpers (for CLI output)
677
+ // ============================================================================
678
+ /**
679
+ * Format credential for table display
680
+ */
681
+ export function formatCredentialRow(cred) {
682
+ return {
683
+ credential: cred.classification.name,
684
+ type: formatCredentialType(cred.classification.credentialType),
685
+ visaType: formatVisaType(cred.classification.visaType),
686
+ riskLevel: `${getRiskEmoji(cred.classification.riskLevel)} ${getRiskLabel(cred.classification.riskLevel)}`,
687
+ };
688
+ }
689
+ function formatCredentialType(type) {
690
+ const typeMap = {
691
+ 'api-key': 'API Key',
692
+ 'oauth-token': 'OAuth Token',
693
+ 'session-keys': 'Session Keys',
694
+ 'bot-token': 'Bot Token',
695
+ 'private-key': 'Private Key',
696
+ custom: 'Allowlist',
697
+ secret: 'Secret',
698
+ };
699
+ return typeMap[type] || type;
700
+ }
701
+ function formatVisaType(type) {
702
+ const typeMap = {
703
+ access: 'Access Visa',
704
+ privilege: 'Privilege',
705
+ data: 'Data',
706
+ compliance: 'Compliance',
707
+ ecosystem: 'Ecosystem',
708
+ custom: 'Custom',
709
+ };
710
+ return typeMap[type] || type;
711
+ }
712
+ //# sourceMappingURL=openclaw.js.map