openclaw-plugin-vt-sentinel 0.8.6 → 0.9.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.
- package/dist/config-manager.d.ts +11 -0
- package/dist/config-manager.js +71 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +176 -5
- package/dist/state-store.d.ts +8 -1
- package/dist/state-store.js +19 -4
- package/dist/status-renderer.d.ts +6 -0
- package/dist/status-renderer.js +19 -0
- package/dist/vt-api.d.ts +13 -1
- package/dist/vt-api.js +13 -6
- package/openclaw.plugin.json +28 -1
- package/package.json +1 -1
- package/skills/vt-sentinel/SKILL.md +23 -0
package/dist/config-manager.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { SensitiveFilePolicy } from './scanner';
|
|
|
2
2
|
export type NotifyLevel = 'all' | 'threats_only' | 'silent';
|
|
3
3
|
export type BlockMode = 'quarantine' | 'block_only' | 'log_only';
|
|
4
4
|
export type PresetName = 'balanced' | 'privacy_first' | 'strict_security';
|
|
5
|
+
export type AgentMetadataMode = 'minimal' | 'enhanced';
|
|
5
6
|
export interface FullConfig {
|
|
6
7
|
apiKey?: string;
|
|
7
8
|
watchDirs: string[];
|
|
@@ -14,6 +15,11 @@ export interface FullConfig {
|
|
|
14
15
|
blockMode: BlockMode;
|
|
15
16
|
showCleanScanLogs: boolean;
|
|
16
17
|
configPreset: PresetName;
|
|
18
|
+
agentDisplayName?: string;
|
|
19
|
+
agentHumanAlias?: string;
|
|
20
|
+
agentBio?: string;
|
|
21
|
+
agentContactEmail?: string;
|
|
22
|
+
agentMetadataMode?: AgentMetadataMode;
|
|
17
23
|
}
|
|
18
24
|
export type ConfigOverrides = Partial<Omit<FullConfig, 'apiKey'>>;
|
|
19
25
|
export interface ConfigDiff {
|
|
@@ -33,6 +39,11 @@ export interface StaticConfig {
|
|
|
33
39
|
blockMode?: BlockMode;
|
|
34
40
|
showCleanScanLogs?: boolean;
|
|
35
41
|
configPreset?: PresetName;
|
|
42
|
+
agentDisplayName?: string;
|
|
43
|
+
agentHumanAlias?: string;
|
|
44
|
+
agentBio?: string;
|
|
45
|
+
agentContactEmail?: string;
|
|
46
|
+
agentMetadataMode?: AgentMetadataMode;
|
|
36
47
|
}
|
|
37
48
|
/**
|
|
38
49
|
* Check if a resolved path is a dangerous filesystem root.
|
package/dist/config-manager.js
CHANGED
|
@@ -91,6 +91,10 @@ const VALID_NOTIFY_LEVELS = new Set(['all', 'threats_only', 'silent']);
|
|
|
91
91
|
const VALID_BLOCK_MODES = new Set(['quarantine', 'block_only', 'log_only']);
|
|
92
92
|
const VALID_PRESETS = new Set(['balanced', 'privacy_first', 'strict_security']);
|
|
93
93
|
const VALID_SENSITIVE_POLICIES = new Set(['ask', 'ask_once', 'always_upload', 'hash_only']);
|
|
94
|
+
const VALID_METADATA_MODES = new Set(['minimal', 'enhanced']);
|
|
95
|
+
const DISPLAY_NAME_RE = /^[a-zA-Z0-9 _-]+$/;
|
|
96
|
+
const HUMAN_ALIAS_RE = /^[a-zA-Z0-9_-]+$/;
|
|
97
|
+
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
94
98
|
/**
|
|
95
99
|
* Check if a resolved path is a dangerous filesystem root.
|
|
96
100
|
* Handles Unix / and any Windows drive letter (C:\, d:/, E:\, etc.) case-insensitively.
|
|
@@ -190,6 +194,51 @@ function validateOverrides(input) {
|
|
|
190
194
|
errors.push(`excludeGlobs must be an array of strings`);
|
|
191
195
|
}
|
|
192
196
|
}
|
|
197
|
+
// --- Agent identity fields ---
|
|
198
|
+
if ('agentDisplayName' in input) {
|
|
199
|
+
const v = input.agentDisplayName;
|
|
200
|
+
if (typeof v === 'string' && v.length >= 1 && v.length <= 50 && DISPLAY_NAME_RE.test(v)) {
|
|
201
|
+
valid.agentDisplayName = v;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
errors.push('agentDisplayName must be 1-50 chars matching [a-zA-Z0-9 _-]');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if ('agentHumanAlias' in input) {
|
|
208
|
+
const v = input.agentHumanAlias;
|
|
209
|
+
if (typeof v === 'string' && v.length >= 1 && v.length <= 50 && HUMAN_ALIAS_RE.test(v)) {
|
|
210
|
+
valid.agentHumanAlias = v;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
errors.push('agentHumanAlias must be 1-50 chars matching [a-zA-Z0-9_-] (no spaces)');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if ('agentBio' in input) {
|
|
217
|
+
const v = input.agentBio;
|
|
218
|
+
if (typeof v === 'string' && v.length >= 1 && v.length <= 200) {
|
|
219
|
+
valid.agentBio = v;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
errors.push('agentBio must be 1-200 chars');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if ('agentContactEmail' in input) {
|
|
226
|
+
const v = input.agentContactEmail;
|
|
227
|
+
if (typeof v === 'string' && v.length <= 100 && EMAIL_RE.test(v)) {
|
|
228
|
+
valid.agentContactEmail = v;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
errors.push('agentContactEmail must be a valid email (max 100 chars)');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if ('agentMetadataMode' in input) {
|
|
235
|
+
if (VALID_METADATA_MODES.has(input.agentMetadataMode)) {
|
|
236
|
+
valid.agentMetadataMode = input.agentMetadataMode;
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
errors.push('agentMetadataMode must be: minimal, enhanced');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
193
242
|
return { valid, errors };
|
|
194
243
|
}
|
|
195
244
|
// --- Glob matcher ---
|
|
@@ -249,6 +298,17 @@ class ConfigManager {
|
|
|
249
298
|
result.blockMode = s.blockMode;
|
|
250
299
|
if (s.showCleanScanLogs !== undefined)
|
|
251
300
|
result.showCleanScanLogs = s.showCleanScanLogs;
|
|
301
|
+
// Identity
|
|
302
|
+
if (s.agentDisplayName !== undefined)
|
|
303
|
+
result.agentDisplayName = s.agentDisplayName;
|
|
304
|
+
if (s.agentHumanAlias !== undefined)
|
|
305
|
+
result.agentHumanAlias = s.agentHumanAlias;
|
|
306
|
+
if (s.agentBio !== undefined)
|
|
307
|
+
result.agentBio = s.agentBio;
|
|
308
|
+
if (s.agentContactEmail !== undefined)
|
|
309
|
+
result.agentContactEmail = s.agentContactEmail;
|
|
310
|
+
if (s.agentMetadataMode !== undefined)
|
|
311
|
+
result.agentMetadataMode = s.agentMetadataMode;
|
|
252
312
|
}
|
|
253
313
|
// Layer 4: Overlay runtime overrides
|
|
254
314
|
const o = this.runtimeOverrides;
|
|
@@ -272,6 +332,17 @@ class ConfigManager {
|
|
|
272
332
|
result.showCleanScanLogs = o.showCleanScanLogs;
|
|
273
333
|
if (o.configPreset !== undefined)
|
|
274
334
|
result.configPreset = o.configPreset;
|
|
335
|
+
// Identity
|
|
336
|
+
if (o.agentDisplayName !== undefined)
|
|
337
|
+
result.agentDisplayName = o.agentDisplayName;
|
|
338
|
+
if (o.agentHumanAlias !== undefined)
|
|
339
|
+
result.agentHumanAlias = o.agentHumanAlias;
|
|
340
|
+
if (o.agentBio !== undefined)
|
|
341
|
+
result.agentBio = o.agentBio;
|
|
342
|
+
if (o.agentContactEmail !== undefined)
|
|
343
|
+
result.agentContactEmail = o.agentContactEmail;
|
|
344
|
+
if (o.agentMetadataMode !== undefined)
|
|
345
|
+
result.agentMetadataMode = o.agentMetadataMode;
|
|
275
346
|
this.effectiveCache = result;
|
|
276
347
|
return result;
|
|
277
348
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -68,9 +68,16 @@ declare function generateUpdateCommands(opts: {
|
|
|
68
68
|
stateDir: string;
|
|
69
69
|
}): string;
|
|
70
70
|
export declare function isSelfPath(filePath: string): boolean;
|
|
71
|
+
declare function generateAgentName(): string;
|
|
72
|
+
declare function buildEnhancedBio(eff: {
|
|
73
|
+
configPreset?: string;
|
|
74
|
+
autoScan?: boolean;
|
|
75
|
+
}): string;
|
|
71
76
|
export default function vtSentinelPlugin(api: PluginApi): void;
|
|
72
77
|
export declare const _generateUpdateCommands: typeof generateUpdateCommands;
|
|
73
78
|
export declare const _fetchLatestVersion: typeof fetchLatestVersion;
|
|
74
79
|
export declare const _getCurrentVersion: typeof getCurrentVersion;
|
|
75
80
|
export declare const _getStateDir: typeof getStateDir;
|
|
81
|
+
export declare const _generateAgentName: typeof generateAgentName;
|
|
82
|
+
export declare const _buildEnhancedBio: typeof buildEnhancedBio;
|
|
76
83
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports._getStateDir = exports._getCurrentVersion = exports._fetchLatestVersion = exports._generateUpdateCommands = void 0;
|
|
39
|
+
exports._buildEnhancedBio = exports._generateAgentName = exports._getStateDir = exports._getCurrentVersion = exports._fetchLatestVersion = exports._generateUpdateCommands = void 0;
|
|
40
40
|
exports.isNewerVersion = isNewerVersion;
|
|
41
41
|
exports.isSelfPath = isSelfPath;
|
|
42
42
|
exports.default = vtSentinelPlugin;
|
|
@@ -231,6 +231,27 @@ function isSelfPath(filePath) {
|
|
|
231
231
|
}
|
|
232
232
|
return normalized.startsWith(SELF_DIR + path.sep) || normalized === SELF_DIR;
|
|
233
233
|
}
|
|
234
|
+
// --- Agent Name Generator ---
|
|
235
|
+
const ADJECTIVES = ['Swift', 'Silent', 'Sharp', 'Bright', 'Steady', 'Bold', 'Keen', 'Quick', 'Iron', 'Steel',
|
|
236
|
+
'Brave', 'Rapid', 'True', 'Deep', 'Clear', 'Calm', 'Noble', 'Dark', 'Wise', 'Frost'];
|
|
237
|
+
const ANIMALS = ['Falcon', 'Wolf', 'Hawk', 'Fox', 'Lynx', 'Bear', 'Eagle', 'Otter', 'Raven', 'Tiger',
|
|
238
|
+
'Shark', 'Viper', 'Puma', 'Crane', 'Owl', 'Cobra', 'Stag', 'Mantis', 'Badger', 'Drake'];
|
|
239
|
+
function generateAgentName() {
|
|
240
|
+
const adj = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
|
|
241
|
+
const animal = ANIMALS[Math.floor(Math.random() * ANIMALS.length)];
|
|
242
|
+
const hex = Math.floor(Math.random() * 0xFFFF).toString(16).padStart(4, '0');
|
|
243
|
+
return `Sentinel-${adj}${animal}-${hex}`;
|
|
244
|
+
}
|
|
245
|
+
function buildEnhancedBio(eff) {
|
|
246
|
+
const osFamily = process.platform === 'darwin' ? 'macos'
|
|
247
|
+
: process.platform === 'win32' ? 'windows' : 'linux';
|
|
248
|
+
const parts = [
|
|
249
|
+
`OpenClaw VT Sentinel on ${osFamily}`,
|
|
250
|
+
`preset ${eff.configPreset || 'balanced'}`,
|
|
251
|
+
eff.autoScan !== false ? 'auto-scan on' : 'auto-scan off',
|
|
252
|
+
];
|
|
253
|
+
return parts.join(', ').slice(0, 200);
|
|
254
|
+
}
|
|
234
255
|
// --- Plugin Entry Point ---
|
|
235
256
|
function vtSentinelPlugin(api) {
|
|
236
257
|
let watcher = null;
|
|
@@ -255,6 +276,50 @@ function vtSentinelPlugin(api) {
|
|
|
255
276
|
catch {
|
|
256
277
|
// Best-effort only.
|
|
257
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* Build agent_version string: pluginVer.oc<openclawVer> (max 20 chars, [a-zA-Z0-9.-]+)
|
|
281
|
+
*/
|
|
282
|
+
function buildAgentVersion() {
|
|
283
|
+
const pluginVer = getCurrentVersion();
|
|
284
|
+
try {
|
|
285
|
+
const meta = api.config?.meta;
|
|
286
|
+
const ocVer = meta?.version || meta?.lastTouchedVersion || '';
|
|
287
|
+
const sanitized = String(ocVer).replace(/[^a-zA-Z0-9.-]/g, '').slice(0, 10);
|
|
288
|
+
if (sanitized)
|
|
289
|
+
return `${pluginVer}.oc${sanitized}`.slice(0, 20);
|
|
290
|
+
}
|
|
291
|
+
catch { }
|
|
292
|
+
return pluginVer.slice(0, 20);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Build registration opts from effective config.
|
|
296
|
+
*/
|
|
297
|
+
function buildRegistrationOpts() {
|
|
298
|
+
const eff = configManager.getEffective();
|
|
299
|
+
// Resolve display name: config > persisted auto-name > generate new
|
|
300
|
+
let displayName = eff.agentDisplayName;
|
|
301
|
+
if (!displayName) {
|
|
302
|
+
let autoName = stateStore.getAutoAgentName();
|
|
303
|
+
if (!autoName) {
|
|
304
|
+
autoName = generateAgentName();
|
|
305
|
+
stateStore.setAutoAgentName(autoName);
|
|
306
|
+
}
|
|
307
|
+
displayName = autoName;
|
|
308
|
+
}
|
|
309
|
+
const regOpts = {
|
|
310
|
+
agentFamily: 'vt-sentinel',
|
|
311
|
+
agentVersion: buildAgentVersion(),
|
|
312
|
+
displayName,
|
|
313
|
+
};
|
|
314
|
+
if (eff.agentHumanAlias)
|
|
315
|
+
regOpts.humanAlias = eff.agentHumanAlias;
|
|
316
|
+
if (eff.agentContactEmail)
|
|
317
|
+
regOpts.contactEmail = eff.agentContactEmail;
|
|
318
|
+
if (eff.agentMetadataMode === 'enhanced') {
|
|
319
|
+
regOpts.defineYourSelf = eff.agentBio || buildEnhancedBio(eff);
|
|
320
|
+
}
|
|
321
|
+
return regOpts;
|
|
322
|
+
}
|
|
258
323
|
/**
|
|
259
324
|
* Ensure scanner is initialized. Handles API key resolution:
|
|
260
325
|
* 1. User-provided apiKey in config or env → standard VT API
|
|
@@ -280,7 +345,7 @@ function vtSentinelPlugin(api) {
|
|
|
280
345
|
let creds = (0, vt_api_1.loadAgentCredentials)();
|
|
281
346
|
if (!creds) {
|
|
282
347
|
try {
|
|
283
|
-
creds = await (0, vt_api_1.registerAgent)();
|
|
348
|
+
creds = await (0, vt_api_1.registerAgent)(buildRegistrationOpts());
|
|
284
349
|
(0, vt_api_1.saveAgentCredentials)(creds);
|
|
285
350
|
api.logger.info(`[VT-Sentinel] Auto-registered agent: ${creds.publicHandle}`);
|
|
286
351
|
}
|
|
@@ -878,6 +943,12 @@ function vtSentinelPlugin(api) {
|
|
|
878
943
|
updateAvailable: latestKnownVersion != null && !updateCheckFailed,
|
|
879
944
|
latestVersion: latestKnownVersion || undefined,
|
|
880
945
|
updateCheckFailed,
|
|
946
|
+
agentIdentity: {
|
|
947
|
+
displayName: eff.agentDisplayName || stateStore.getAutoAgentName() || '(not set)',
|
|
948
|
+
publicHandle: (0, vt_api_1.loadAgentCredentials)()?.publicHandle,
|
|
949
|
+
metadataMode: eff.agentMetadataMode || 'minimal',
|
|
950
|
+
humanAlias: eff.agentHumanAlias,
|
|
951
|
+
},
|
|
881
952
|
}));
|
|
882
953
|
},
|
|
883
954
|
});
|
|
@@ -900,6 +971,11 @@ function vtSentinelPlugin(api) {
|
|
|
900
971
|
excludeGlobs: { type: 'array', items: { type: 'string' }, description: 'Glob patterns for files to skip (e.g., *.log)' },
|
|
901
972
|
blockMode: { type: 'string', enum: ['quarantine', 'block_only', 'log_only'], description: 'How to handle malicious files' },
|
|
902
973
|
showCleanScanLogs: { type: 'boolean', description: 'Log clean scan results' },
|
|
974
|
+
agentDisplayName: { type: 'string', description: 'Display name for VTAI leaderboard (1-50 chars)' },
|
|
975
|
+
agentHumanAlias: { type: 'string', description: 'Human alias (1-50 chars, no spaces)' },
|
|
976
|
+
agentBio: { type: 'string', description: 'Agent description for VTAI (1-200 chars)' },
|
|
977
|
+
agentContactEmail: { type: 'string', description: 'Contact email (optional, privacy-sensitive)' },
|
|
978
|
+
agentMetadataMode: { type: 'string', enum: ['minimal', 'enhanced'], description: 'Metadata sent during registration: minimal=display_name only, enhanced=adds OS/preset info' },
|
|
903
979
|
persist: { type: 'string', enum: ['session', 'state'], description: 'session = until restart, state = saved to disk (default: state)' },
|
|
904
980
|
},
|
|
905
981
|
required: [],
|
|
@@ -967,7 +1043,13 @@ function vtSentinelPlugin(api) {
|
|
|
967
1043
|
if (params.persist !== 'session') {
|
|
968
1044
|
stateStore.persistOverrides(configManager.getRuntimeOverrides());
|
|
969
1045
|
}
|
|
970
|
-
|
|
1046
|
+
let result = (0, status_renderer_1.renderConfigChangeResult)(diff, newConfig);
|
|
1047
|
+
// Hint about re-registration if identity fields changed
|
|
1048
|
+
const identityFields = ['agentDisplayName', 'agentHumanAlias', 'agentBio', 'agentContactEmail', 'agentMetadataMode'];
|
|
1049
|
+
if (diff.changedFields.some(f => identityFields.includes(f))) {
|
|
1050
|
+
result += '\n\nIdentity changes saved. To apply to VTAI leaderboard, use vt_sentinel_re_register { confirm: true }.';
|
|
1051
|
+
}
|
|
1052
|
+
return textResponse(result);
|
|
971
1053
|
},
|
|
972
1054
|
});
|
|
973
1055
|
// --- Tool: vt_sentinel_reset_policy ---
|
|
@@ -1068,6 +1150,93 @@ function vtSentinelPlugin(api) {
|
|
|
1068
1150
|
}));
|
|
1069
1151
|
},
|
|
1070
1152
|
});
|
|
1153
|
+
// --- Tool: vt_sentinel_re_register ---
|
|
1154
|
+
api.registerTool({
|
|
1155
|
+
name: 'vt_sentinel_re_register',
|
|
1156
|
+
description: 'Re-register agent with VTAI to apply identity changes. Creates a new agent identity (new public_handle). Use after changing agentDisplayName or other identity settings.',
|
|
1157
|
+
parameters: {
|
|
1158
|
+
type: 'object',
|
|
1159
|
+
properties: {
|
|
1160
|
+
confirm: { type: 'boolean', description: 'Must be true to proceed (generates new identity)' },
|
|
1161
|
+
},
|
|
1162
|
+
required: [],
|
|
1163
|
+
},
|
|
1164
|
+
execute: async (_ctx, rawParams) => {
|
|
1165
|
+
const params = (typeof rawParams === 'object' && rawParams !== null) ? rawParams : {};
|
|
1166
|
+
if ('confirm' in params && typeof params.confirm !== 'boolean') {
|
|
1167
|
+
return textResponse('Error: confirm must be true or false');
|
|
1168
|
+
}
|
|
1169
|
+
// Check if using user API key (no VTAI registration)
|
|
1170
|
+
const envKey = process.env.VIRUSTOTAL_API_KEY;
|
|
1171
|
+
if (envKey && envKey !== 'vtai-active') {
|
|
1172
|
+
return textResponse('Re-registration not applicable — using user-provided API key (not VTAI).');
|
|
1173
|
+
}
|
|
1174
|
+
const eff = configManager.getEffective();
|
|
1175
|
+
const currentCreds = (0, vt_api_1.loadAgentCredentials)();
|
|
1176
|
+
// Resolve display name for preview
|
|
1177
|
+
let displayName = eff.agentDisplayName;
|
|
1178
|
+
if (!displayName) {
|
|
1179
|
+
displayName = stateStore.getAutoAgentName() || '(will generate new name)';
|
|
1180
|
+
}
|
|
1181
|
+
if (!params.confirm) {
|
|
1182
|
+
// Preview mode
|
|
1183
|
+
const lines = ['Agent re-registration preview:'];
|
|
1184
|
+
lines.push('');
|
|
1185
|
+
if (currentCreds) {
|
|
1186
|
+
lines.push(` Current handle: ${currentCreds.publicHandle}`);
|
|
1187
|
+
lines.push(` Registered at: ${currentCreds.registeredAt}`);
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1190
|
+
lines.push(' No current registration found.');
|
|
1191
|
+
}
|
|
1192
|
+
lines.push('');
|
|
1193
|
+
lines.push(' New registration will use:');
|
|
1194
|
+
lines.push(` display_name: ${displayName}`);
|
|
1195
|
+
if (eff.agentHumanAlias)
|
|
1196
|
+
lines.push(` human_alias: ${eff.agentHumanAlias}`);
|
|
1197
|
+
if (eff.agentMetadataMode === 'enhanced') {
|
|
1198
|
+
lines.push(` define_your_self: ${eff.agentBio || buildEnhancedBio(eff)}`);
|
|
1199
|
+
}
|
|
1200
|
+
if (eff.agentContactEmail)
|
|
1201
|
+
lines.push(` contact_email: (set)`);
|
|
1202
|
+
lines.push('');
|
|
1203
|
+
lines.push('WARNING: This creates a NEW agent identity with a new public_handle.');
|
|
1204
|
+
lines.push('The old handle will remain on the leaderboard as a separate entry.');
|
|
1205
|
+
lines.push('');
|
|
1206
|
+
lines.push('Call with { confirm: true } to proceed.');
|
|
1207
|
+
return textResponse(lines.join('\n'));
|
|
1208
|
+
}
|
|
1209
|
+
// Confirmed — re-register
|
|
1210
|
+
try {
|
|
1211
|
+
// Backup current credentials
|
|
1212
|
+
if (currentCreds) {
|
|
1213
|
+
const backupPath = (0, vt_api_1.getAgentCredentialsPath)() + '.bak';
|
|
1214
|
+
fs.writeFileSync(backupPath, JSON.stringify(currentCreds, null, 2), { mode: 0o600 });
|
|
1215
|
+
}
|
|
1216
|
+
const newCreds = await (0, vt_api_1.registerAgent)(buildRegistrationOpts());
|
|
1217
|
+
(0, vt_api_1.saveAgentCredentials)(newCreds);
|
|
1218
|
+
// Update scanner with new token
|
|
1219
|
+
if (scanner) {
|
|
1220
|
+
const scanEff = configManager.getEffective();
|
|
1221
|
+
scanner = new scanner_1.Scanner(newCreds.agentToken, api.logger, scanEff.maxFileSizeMb, scanEff.sensitiveFilePolicy, true);
|
|
1222
|
+
}
|
|
1223
|
+
const lines = ['Agent re-registered successfully:'];
|
|
1224
|
+
lines.push(` New handle: ${newCreds.publicHandle}`);
|
|
1225
|
+
lines.push(` Display name: ${buildRegistrationOpts().displayName || displayName}`);
|
|
1226
|
+
if (currentCreds) {
|
|
1227
|
+
lines.push(` Previous handle: ${currentCreds.publicHandle} (backup saved)`);
|
|
1228
|
+
}
|
|
1229
|
+
return textResponse(lines.join('\n'));
|
|
1230
|
+
}
|
|
1231
|
+
catch (err) {
|
|
1232
|
+
// Rollback on failure
|
|
1233
|
+
if (currentCreds) {
|
|
1234
|
+
(0, vt_api_1.saveAgentCredentials)(currentCreds);
|
|
1235
|
+
}
|
|
1236
|
+
return textResponse(`Re-registration failed: ${err.message}\nPrevious credentials restored.`);
|
|
1237
|
+
}
|
|
1238
|
+
},
|
|
1239
|
+
});
|
|
1071
1240
|
// --- Hook: auto-scan tool results ---
|
|
1072
1241
|
const handleToolResult = async (event) => {
|
|
1073
1242
|
const s = await ensureScanner();
|
|
@@ -1093,7 +1262,7 @@ function vtSentinelPlugin(api) {
|
|
|
1093
1262
|
'vt_scan_file', 'vt_check_hash', 'vt_upload_consent',
|
|
1094
1263
|
'vt_sentinel_status', 'vt_sentinel_configure',
|
|
1095
1264
|
'vt_sentinel_reset_policy', 'vt_sentinel_help',
|
|
1096
|
-
'vt_sentinel_update',
|
|
1265
|
+
'vt_sentinel_update', 'vt_sentinel_re_register',
|
|
1097
1266
|
],
|
|
1098
1267
|
});
|
|
1099
1268
|
const injected = injectOnboarding(event, onboardingText);
|
|
@@ -1308,7 +1477,7 @@ function vtSentinelPlugin(api) {
|
|
|
1308
1477
|
vtSentinelPlugin._computeAutoWatchDirs = computeAutoWatchDirs;
|
|
1309
1478
|
vtSentinelPlugin._handleWatcherFile = handleWatcherFile;
|
|
1310
1479
|
vtSentinelPlugin._enrichFromContext = enrichFromContext;
|
|
1311
|
-
api.logger.info('[VT-Sentinel] Plugin loaded —
|
|
1480
|
+
api.logger.info('[VT-Sentinel] Plugin loaded — 9 tools + active protection hooks registered (VTAI auto-registration enabled)');
|
|
1312
1481
|
// Non-blocking update check (fire-and-forget)
|
|
1313
1482
|
checkForUpdates(api.logger, {
|
|
1314
1483
|
onNewer: (v) => { latestKnownVersion = v; updateCheckFailed = false; },
|
|
@@ -1375,3 +1544,5 @@ exports._generateUpdateCommands = generateUpdateCommands;
|
|
|
1375
1544
|
exports._fetchLatestVersion = fetchLatestVersion;
|
|
1376
1545
|
exports._getCurrentVersion = getCurrentVersion;
|
|
1377
1546
|
exports._getStateDir = getStateDir;
|
|
1547
|
+
exports._generateAgentName = generateAgentName;
|
|
1548
|
+
exports._buildEnhancedBio = buildEnhancedBio;
|
package/dist/state-store.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { ConfigOverrides } from './config-manager';
|
|
2
|
+
export interface AgentIdentityState {
|
|
3
|
+
autoAgentName?: string;
|
|
4
|
+
}
|
|
2
5
|
export interface PersistedState {
|
|
3
|
-
version:
|
|
6
|
+
version: 2;
|
|
4
7
|
firstRunShown: Record<string, boolean>;
|
|
5
8
|
runtimeOverrides: ConfigOverrides;
|
|
9
|
+
agentIdentity?: AgentIdentityState;
|
|
6
10
|
}
|
|
7
11
|
export declare class StateStore {
|
|
8
12
|
private state;
|
|
@@ -20,6 +24,9 @@ export declare class StateStore {
|
|
|
20
24
|
getPersistedOverrides(): ConfigOverrides;
|
|
21
25
|
persistOverrides(overrides: ConfigOverrides): void;
|
|
22
26
|
clearPersistedOverrides(): void;
|
|
27
|
+
getAgentIdentity(): AgentIdentityState;
|
|
28
|
+
setAutoAgentName(name: string): void;
|
|
29
|
+
getAutoAgentName(): string | undefined;
|
|
23
30
|
private scopeKey;
|
|
24
31
|
private load;
|
|
25
32
|
private save;
|
package/dist/state-store.js
CHANGED
|
@@ -38,9 +38,10 @@ const fs = __importStar(require("fs"));
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const os = __importStar(require("os"));
|
|
40
40
|
const DEFAULT_STATE = {
|
|
41
|
-
version:
|
|
41
|
+
version: 2,
|
|
42
42
|
firstRunShown: {},
|
|
43
43
|
runtimeOverrides: {},
|
|
44
|
+
agentIdentity: {},
|
|
44
45
|
};
|
|
45
46
|
// --- StateStore ---
|
|
46
47
|
class StateStore {
|
|
@@ -75,6 +76,19 @@ class StateStore {
|
|
|
75
76
|
this.state.runtimeOverrides = {};
|
|
76
77
|
this.save();
|
|
77
78
|
}
|
|
79
|
+
// --- Agent Identity ---
|
|
80
|
+
getAgentIdentity() {
|
|
81
|
+
return { ...(this.state.agentIdentity || {}) };
|
|
82
|
+
}
|
|
83
|
+
setAutoAgentName(name) {
|
|
84
|
+
if (!this.state.agentIdentity)
|
|
85
|
+
this.state.agentIdentity = {};
|
|
86
|
+
this.state.agentIdentity.autoAgentName = name;
|
|
87
|
+
this.save();
|
|
88
|
+
}
|
|
89
|
+
getAutoAgentName() {
|
|
90
|
+
return this.state.agentIdentity?.autoAgentName;
|
|
91
|
+
}
|
|
78
92
|
// --- Internals ---
|
|
79
93
|
scopeKey(scope) {
|
|
80
94
|
if (scope?.workspaceDir)
|
|
@@ -86,16 +100,17 @@ class StateStore {
|
|
|
86
100
|
load() {
|
|
87
101
|
try {
|
|
88
102
|
const data = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
|
|
89
|
-
if (data && data.version === 1) {
|
|
103
|
+
if (data && (data.version === 1 || data.version === 2)) {
|
|
90
104
|
return {
|
|
91
|
-
version:
|
|
105
|
+
version: 2,
|
|
92
106
|
firstRunShown: data.firstRunShown || {},
|
|
93
107
|
runtimeOverrides: data.runtimeOverrides || {},
|
|
108
|
+
agentIdentity: data.agentIdentity || {},
|
|
94
109
|
};
|
|
95
110
|
}
|
|
96
111
|
}
|
|
97
112
|
catch { /* missing or corrupt — use defaults */ }
|
|
98
|
-
return { ...DEFAULT_STATE, firstRunShown: {}, runtimeOverrides: {} };
|
|
113
|
+
return { ...DEFAULT_STATE, firstRunShown: {}, runtimeOverrides: {}, agentIdentity: {} };
|
|
99
114
|
}
|
|
100
115
|
save() {
|
|
101
116
|
try {
|
|
@@ -17,6 +17,12 @@ export declare function renderStatus(opts: {
|
|
|
17
17
|
updateAvailable?: boolean;
|
|
18
18
|
latestVersion?: string;
|
|
19
19
|
updateCheckFailed?: boolean;
|
|
20
|
+
agentIdentity?: {
|
|
21
|
+
displayName: string;
|
|
22
|
+
publicHandle?: string;
|
|
23
|
+
metadataMode: string;
|
|
24
|
+
humanAlias?: string;
|
|
25
|
+
};
|
|
20
26
|
}): string;
|
|
21
27
|
export declare function renderPolicyMatrix(config: FullConfig): string;
|
|
22
28
|
export declare function renderHelp(): string;
|
package/dist/status-renderer.js
CHANGED
|
@@ -45,6 +45,19 @@ function renderStatus(opts) {
|
|
|
45
45
|
lines.push(' Update check: last check failed (network error). Use vt_sentinel_update to retry.');
|
|
46
46
|
}
|
|
47
47
|
lines.push('');
|
|
48
|
+
// Agent Identity
|
|
49
|
+
if (opts.agentIdentity) {
|
|
50
|
+
lines.push('Agent Identity:');
|
|
51
|
+
lines.push(` Display name: ${opts.agentIdentity.displayName}`);
|
|
52
|
+
if (opts.agentIdentity.publicHandle) {
|
|
53
|
+
lines.push(` Public handle: ${opts.agentIdentity.publicHandle}`);
|
|
54
|
+
}
|
|
55
|
+
lines.push(` Metadata mode: ${opts.agentIdentity.metadataMode}`);
|
|
56
|
+
if (opts.agentIdentity.humanAlias) {
|
|
57
|
+
lines.push(` Human alias: ${opts.agentIdentity.humanAlias}`);
|
|
58
|
+
}
|
|
59
|
+
lines.push('');
|
|
60
|
+
}
|
|
48
61
|
// Config
|
|
49
62
|
lines.push('Effective Configuration:');
|
|
50
63
|
lines.push(` Preset: ${opts.presetName}`);
|
|
@@ -147,6 +160,12 @@ function renderHelp() {
|
|
|
147
160
|
lines.push(' vt_sentinel_update { confirm: true }');
|
|
148
161
|
lines.push(' Check for updates and get upgrade instructions');
|
|
149
162
|
lines.push('');
|
|
163
|
+
lines.push(' vt_sentinel_configure { agentDisplayName: "MySecurityBot", agentMetadataMode: "enhanced" }');
|
|
164
|
+
lines.push(' Set agent display name and enable enhanced metadata');
|
|
165
|
+
lines.push('');
|
|
166
|
+
lines.push(' vt_sentinel_re_register { confirm: true }');
|
|
167
|
+
lines.push(' Re-register with VTAI to apply identity changes (creates new public_handle)');
|
|
168
|
+
lines.push('');
|
|
150
169
|
lines.push('PRESETS:');
|
|
151
170
|
lines.push(' balanced (default)');
|
|
152
171
|
lines.push(' Ask before uploading sensitive files. Quarantine malicious. Log all scans.');
|
package/dist/vt-api.d.ts
CHANGED
|
@@ -34,11 +34,23 @@ export interface AgentCredentials {
|
|
|
34
34
|
export declare function getAgentCredentialsPath(): string;
|
|
35
35
|
export declare function loadAgentCredentials(): AgentCredentials | null;
|
|
36
36
|
export declare function saveAgentCredentials(creds: AgentCredentials): void;
|
|
37
|
+
/**
|
|
38
|
+
* Options for agent registration with VTAI.
|
|
39
|
+
* All fields optional — sensible defaults applied.
|
|
40
|
+
*/
|
|
41
|
+
export interface RegisterAgentOpts {
|
|
42
|
+
agentFamily?: string;
|
|
43
|
+
agentVersion?: string;
|
|
44
|
+
displayName?: string;
|
|
45
|
+
humanAlias?: string;
|
|
46
|
+
defineYourSelf?: string;
|
|
47
|
+
contactEmail?: string;
|
|
48
|
+
}
|
|
37
49
|
/**
|
|
38
50
|
* Register a new agent with VirusTotal AI API.
|
|
39
51
|
* No authentication required — zero-friction onboarding.
|
|
40
52
|
*/
|
|
41
|
-
export declare function registerAgent(
|
|
53
|
+
export declare function registerAgent(opts?: RegisterAgentOpts): Promise<AgentCredentials>;
|
|
42
54
|
export declare function calculateSHA256(filePath: string): Promise<string>;
|
|
43
55
|
export declare class VTApiClient {
|
|
44
56
|
private apiKey;
|
package/dist/vt-api.js
CHANGED
|
@@ -86,12 +86,19 @@ function saveAgentCredentials(creds) {
|
|
|
86
86
|
* Register a new agent with VirusTotal AI API.
|
|
87
87
|
* No authentication required — zero-friction onboarding.
|
|
88
88
|
*/
|
|
89
|
-
async function registerAgent(
|
|
90
|
-
const
|
|
91
|
-
agent_family: 'vt-sentinel',
|
|
92
|
-
agent_version:
|
|
93
|
-
display_name: 'VT Sentinel',
|
|
94
|
-
}
|
|
89
|
+
async function registerAgent(opts) {
|
|
90
|
+
const body = {
|
|
91
|
+
agent_family: opts?.agentFamily || 'vt-sentinel',
|
|
92
|
+
agent_version: opts?.agentVersion || '0.2.0',
|
|
93
|
+
display_name: opts?.displayName || 'VT Sentinel',
|
|
94
|
+
};
|
|
95
|
+
if (opts?.humanAlias)
|
|
96
|
+
body.human_alias = opts.humanAlias;
|
|
97
|
+
if (opts?.defineYourSelf)
|
|
98
|
+
body.define_your_self = opts.defineYourSelf;
|
|
99
|
+
if (opts?.contactEmail)
|
|
100
|
+
body.contact_email = opts.contactEmail;
|
|
101
|
+
const resp = await axios_1.default.post(`${VTAI_API_URL}/agents/register`, body, { timeout: HTTP_TIMEOUT_MS });
|
|
95
102
|
return {
|
|
96
103
|
agentId: resp.data.agent_id,
|
|
97
104
|
agentToken: resp.data.agent_token,
|
package/openclaw.plugin.json
CHANGED
|
@@ -62,6 +62,28 @@
|
|
|
62
62
|
"enum": ["balanced", "privacy_first", "strict_security"],
|
|
63
63
|
"default": "balanced",
|
|
64
64
|
"description": "Configuration preset. Individual settings override the preset."
|
|
65
|
+
},
|
|
66
|
+
"agentDisplayName": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"description": "Display name for VTAI leaderboard (1-50 chars, [a-zA-Z0-9 _-]+)"
|
|
69
|
+
},
|
|
70
|
+
"agentHumanAlias": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "Human alias for VTAI (1-50 chars, no spaces)"
|
|
73
|
+
},
|
|
74
|
+
"agentBio": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"description": "Agent description for VTAI define_your_self (1-200 chars)"
|
|
77
|
+
},
|
|
78
|
+
"agentContactEmail": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"description": "Contact email for VTAI (optional, privacy-sensitive)"
|
|
81
|
+
},
|
|
82
|
+
"agentMetadataMode": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"enum": ["minimal", "enhanced"],
|
|
85
|
+
"default": "minimal",
|
|
86
|
+
"description": "What metadata to send during registration. minimal = display_name only. enhanced = also sends OS family, preset, and auto-scan status (no personal data)."
|
|
65
87
|
}
|
|
66
88
|
},
|
|
67
89
|
"required": []
|
|
@@ -77,6 +99,11 @@
|
|
|
77
99
|
"excludeGlobs": { "label": "Exclude File Patterns" },
|
|
78
100
|
"blockMode": { "label": "Block Mode" },
|
|
79
101
|
"showCleanScanLogs": { "label": "Show Clean Scan Logs" },
|
|
80
|
-
"configPreset": { "label": "Configuration Preset" }
|
|
102
|
+
"configPreset": { "label": "Configuration Preset" },
|
|
103
|
+
"agentDisplayName": { "label": "Agent Display Name" },
|
|
104
|
+
"agentHumanAlias": { "label": "Human Alias" },
|
|
105
|
+
"agentBio": { "label": "Agent Description" },
|
|
106
|
+
"agentContactEmail": { "label": "Contact Email", "sensitive": true },
|
|
107
|
+
"agentMetadataMode": { "label": "Metadata Mode" }
|
|
81
108
|
}
|
|
82
109
|
}
|
package/package.json
CHANGED
|
@@ -148,6 +148,29 @@ vt_sentinel_update {}
|
|
|
148
148
|
vt_sentinel_update { "confirm": true }
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
+
### `vt_sentinel_re_register` — Re-register Agent Identity
|
|
152
|
+
Re-registers with VTAI using current identity settings. Creates a new `public_handle`.
|
|
153
|
+
Use after changing `agentDisplayName` or other identity settings via `vt_sentinel_configure`.
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
vt_sentinel_re_register {}
|
|
157
|
+
vt_sentinel_re_register { "confirm": true }
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Agent Identity
|
|
161
|
+
|
|
162
|
+
Each VT Sentinel instance registers with VTAI and appears on the agent leaderboard.
|
|
163
|
+
By default, a unique name is auto-generated (e.g., `Sentinel-SwiftFalcon-a3f2`).
|
|
164
|
+
|
|
165
|
+
Configure identity via `vt_sentinel_configure`:
|
|
166
|
+
- `agentDisplayName`: Custom display name for the leaderboard
|
|
167
|
+
- `agentHumanAlias`: Human operator alias (no spaces)
|
|
168
|
+
- `agentBio`: Short description (maps to VTAI `define_your_self`)
|
|
169
|
+
- `agentContactEmail`: Optional contact email
|
|
170
|
+
- `agentMetadataMode`: `minimal` (default, display name only) or `enhanced` (adds OS, preset info — no personal data)
|
|
171
|
+
|
|
172
|
+
After changing identity settings, use `vt_sentinel_re_register { "confirm": true }` to apply.
|
|
173
|
+
|
|
151
174
|
## Active Protection
|
|
152
175
|
|
|
153
176
|
VT Sentinel automatically protects the system in real-time:
|