@mnemom/mnemom 0.7.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/LICENSE +191 -0
- package/README.md +123 -0
- package/dist/commands/agents.d.ts +15 -0
- package/dist/commands/agents.js +303 -0
- package/dist/commands/auth.d.ts +5 -0
- package/dist/commands/auth.js +60 -0
- package/dist/commands/card.d.ts +23 -0
- package/dist/commands/card.js +460 -0
- package/dist/commands/claim.d.ts +1 -0
- package/dist/commands/claim.js +72 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +763 -0
- package/dist/commands/integrity.d.ts +1 -0
- package/dist/commands/integrity.js +49 -0
- package/dist/commands/license.d.ts +3 -0
- package/dist/commands/license.js +163 -0
- package/dist/commands/logs.d.ts +5 -0
- package/dist/commands/logs.js +73 -0
- package/dist/commands/migrate-config.d.ts +2 -0
- package/dist/commands/migrate-config.js +72 -0
- package/dist/commands/policy.d.ts +31 -0
- package/dist/commands/policy.js +543 -0
- package/dist/commands/register.d.ts +6 -0
- package/dist/commands/register.js +362 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +383 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +381 -0
- package/dist/lib/api.d.ts +133 -0
- package/dist/lib/api.js +207 -0
- package/dist/lib/auth.d.ts +60 -0
- package/dist/lib/auth.js +281 -0
- package/dist/lib/config.d.ts +105 -0
- package/dist/lib/config.js +253 -0
- package/dist/lib/format.d.ts +35 -0
- package/dist/lib/format.js +60 -0
- package/dist/lib/model-cache.d.ts +16 -0
- package/dist/lib/model-cache.js +138 -0
- package/dist/lib/models.d.ts +41 -0
- package/dist/lib/models.js +357 -0
- package/dist/lib/openclaw.d.ts +221 -0
- package/dist/lib/openclaw.js +474 -0
- package/dist/lib/prompt.d.ts +26 -0
- package/dist/lib/prompt.js +150 -0
- package/dist/smoltbot-shim.d.ts +2 -0
- package/dist/smoltbot-shim.js +7 -0
- package/package.json +61 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { configExists, getAuthInfo, clearAuthTokens } from "../lib/config.js";
|
|
2
|
+
import { loginWithBrowser, loginWithPassword } from "../lib/auth.js";
|
|
3
|
+
import { fmt } from "../lib/format.js";
|
|
4
|
+
import { askInput } from "../lib/prompt.js";
|
|
5
|
+
export async function loginCommand(options = {}) {
|
|
6
|
+
if (!configExists()) {
|
|
7
|
+
console.log("\n" + fmt.error("smoltbot is not initialized") + "\n");
|
|
8
|
+
console.log("Run `smoltbot init` to get started.\n");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
let tokens;
|
|
13
|
+
if (options.noBrowser) {
|
|
14
|
+
const email = await askInput("Email:");
|
|
15
|
+
if (!email) {
|
|
16
|
+
console.log(fmt.error("Email is required."));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const password = await askInput("Password:", true);
|
|
20
|
+
if (!password) {
|
|
21
|
+
console.log(fmt.error("Password is required."));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
tokens = await loginWithPassword(email, password);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
tokens = await loginWithBrowser();
|
|
28
|
+
}
|
|
29
|
+
console.log();
|
|
30
|
+
console.log(fmt.success("Logged in successfully!"));
|
|
31
|
+
console.log(fmt.label(" Email: ", ` ${tokens.email}`));
|
|
32
|
+
console.log(fmt.label(" User ID:", ` ${tokens.userId}`));
|
|
33
|
+
console.log();
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
37
|
+
console.log("\n" + fmt.error(`Login failed: ${message}`) + "\n");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function logoutCommand() {
|
|
42
|
+
clearAuthTokens();
|
|
43
|
+
console.log(fmt.success("Logged out."));
|
|
44
|
+
}
|
|
45
|
+
export async function whoamiCommand() {
|
|
46
|
+
const auth = getAuthInfo();
|
|
47
|
+
if (!auth) {
|
|
48
|
+
console.log("\nNot logged in. Run `smoltbot login` to authenticate.\n");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const now = Math.floor(Date.now() / 1000);
|
|
52
|
+
const expired = auth.expiresAt <= now;
|
|
53
|
+
const expiresDate = new Date(auth.expiresAt * 1000).toISOString();
|
|
54
|
+
console.log(fmt.header("Auth Status"));
|
|
55
|
+
console.log();
|
|
56
|
+
console.log(fmt.label(" Email: ", ` ${auth.email}`));
|
|
57
|
+
console.log(fmt.label(" User ID:", ` ${auth.userId}`));
|
|
58
|
+
console.log(fmt.label(" Token: ", expired ? " expired" : ` valid until ${expiresDate}`));
|
|
59
|
+
console.log();
|
|
60
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type AlignmentCard } from "../lib/api.js";
|
|
2
|
+
export type CardFormat = "json" | "yaml";
|
|
3
|
+
export interface ParsedCard {
|
|
4
|
+
format: CardFormat;
|
|
5
|
+
parsed: AlignmentCard;
|
|
6
|
+
}
|
|
7
|
+
export declare function parseCardFile(filePath: string): ParsedCard;
|
|
8
|
+
export interface ValidationCheck {
|
|
9
|
+
name: string;
|
|
10
|
+
passed: boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
/** @deprecated Use validateCard instead */
|
|
14
|
+
export declare const validateCardJson: typeof validateCard;
|
|
15
|
+
export declare function validateCard(raw: string): ValidationCheck[];
|
|
16
|
+
/**
|
|
17
|
+
* Validate a pre-parsed card object (used for YAML cards).
|
|
18
|
+
* Re-serializes to JSON so the same checks run identically.
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateCardObject(card: AlignmentCard): ValidationCheck[];
|
|
21
|
+
export declare function cardShowCommand(agentName?: string): Promise<void>;
|
|
22
|
+
export declare function cardPublishCommand(file: string, agentName?: string): Promise<void>;
|
|
23
|
+
export declare function cardValidateCommand(file: string): Promise<void>;
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import { configExists, loadConfig, requireAgent } from "../lib/config.js";
|
|
5
|
+
import { getCard, updateCard, reverifyAgent, } from "../lib/api.js";
|
|
6
|
+
import { requireAuth } from "../lib/auth.js";
|
|
7
|
+
import { fmt } from "../lib/format.js";
|
|
8
|
+
import { askYesNo, isInteractive } from "../lib/prompt.js";
|
|
9
|
+
// Standard AAP values that do not require custom definitions
|
|
10
|
+
const STANDARD_VALUES = new Set([
|
|
11
|
+
"transparency",
|
|
12
|
+
"honesty",
|
|
13
|
+
"safety",
|
|
14
|
+
"privacy",
|
|
15
|
+
"fairness",
|
|
16
|
+
"accountability",
|
|
17
|
+
"beneficence",
|
|
18
|
+
"non-maleficence",
|
|
19
|
+
"autonomy",
|
|
20
|
+
"justice",
|
|
21
|
+
"reliability",
|
|
22
|
+
"security",
|
|
23
|
+
"human_oversight",
|
|
24
|
+
"explainability",
|
|
25
|
+
]);
|
|
26
|
+
function detectFormat(filePath) {
|
|
27
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
28
|
+
if (ext === ".yaml" || ext === ".yml")
|
|
29
|
+
return "yaml";
|
|
30
|
+
return "json";
|
|
31
|
+
}
|
|
32
|
+
export function parseCardFile(filePath) {
|
|
33
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
34
|
+
const format = detectFormat(filePath);
|
|
35
|
+
if (format === "yaml") {
|
|
36
|
+
const parsed = yaml.load(raw);
|
|
37
|
+
if (!parsed || typeof parsed !== "object") {
|
|
38
|
+
throw new Error("YAML did not produce a valid object");
|
|
39
|
+
}
|
|
40
|
+
return { format, parsed };
|
|
41
|
+
}
|
|
42
|
+
return { format, parsed: JSON.parse(raw) };
|
|
43
|
+
}
|
|
44
|
+
/** @deprecated Use validateCard instead */
|
|
45
|
+
export const validateCardJson = validateCard;
|
|
46
|
+
export function validateCard(raw) {
|
|
47
|
+
const checks = [];
|
|
48
|
+
// Check 1: Valid JSON
|
|
49
|
+
let card;
|
|
50
|
+
try {
|
|
51
|
+
card = JSON.parse(raw);
|
|
52
|
+
checks.push({ name: "Valid JSON", passed: true, message: "Parsed successfully" });
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
56
|
+
checks.push({ name: "Valid JSON", passed: false, message: `Parse error: ${msg}` });
|
|
57
|
+
return checks; // Can't continue without valid JSON
|
|
58
|
+
}
|
|
59
|
+
// Check 2: Required blocks
|
|
60
|
+
const requiredBlocks = ["principal", "values", "autonomy_envelope", "audit_commitment"];
|
|
61
|
+
for (const block of requiredBlocks) {
|
|
62
|
+
if (card[block] && typeof card[block] === "object") {
|
|
63
|
+
checks.push({ name: `Block: ${block}`, passed: true, message: "Present" });
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
checks.push({ name: `Block: ${block}`, passed: false, message: "Missing or invalid" });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Check 3: values.declared is non-empty array
|
|
70
|
+
const declared = card.values?.declared;
|
|
71
|
+
if (Array.isArray(declared) && declared.length > 0) {
|
|
72
|
+
checks.push({
|
|
73
|
+
name: "values.declared",
|
|
74
|
+
passed: true,
|
|
75
|
+
message: `${declared.length} value(s) declared`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
checks.push({
|
|
80
|
+
name: "values.declared",
|
|
81
|
+
passed: false,
|
|
82
|
+
message: "Must be a non-empty array",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// Check 4: Custom values have definitions
|
|
86
|
+
if (Array.isArray(declared)) {
|
|
87
|
+
const definitions = card.values?.definitions || {};
|
|
88
|
+
const customValues = declared.filter((v) => !STANDARD_VALUES.has(v));
|
|
89
|
+
const missingDefs = customValues.filter((v) => !definitions[v]);
|
|
90
|
+
if (missingDefs.length === 0) {
|
|
91
|
+
checks.push({
|
|
92
|
+
name: "Custom value definitions",
|
|
93
|
+
passed: true,
|
|
94
|
+
message: customValues.length === 0
|
|
95
|
+
? "No custom values (all standard)"
|
|
96
|
+
: `${customValues.length} custom value(s) defined`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
checks.push({
|
|
101
|
+
name: "Custom value definitions",
|
|
102
|
+
passed: false,
|
|
103
|
+
message: `Missing definitions for: ${missingDefs.join(", ")}`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Check 5: bounded_actions is non-empty array
|
|
108
|
+
const bounded = card.autonomy_envelope?.bounded_actions;
|
|
109
|
+
if (Array.isArray(bounded) && bounded.length > 0) {
|
|
110
|
+
checks.push({
|
|
111
|
+
name: "bounded_actions",
|
|
112
|
+
passed: true,
|
|
113
|
+
message: `${bounded.length} bounded action(s)`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
checks.push({
|
|
118
|
+
name: "bounded_actions",
|
|
119
|
+
passed: false,
|
|
120
|
+
message: "Must be a non-empty array",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// Check 6: escalation_triggers conditions are evaluable
|
|
124
|
+
// Accepts: simple identifiers, tool_matches('...') calls, and logical expressions
|
|
125
|
+
// (comparisons with ==, !=, >, <, and/or, quoted strings, numbers)
|
|
126
|
+
const triggers = card.autonomy_envelope?.escalation_triggers;
|
|
127
|
+
if (Array.isArray(triggers) && triggers.length > 0) {
|
|
128
|
+
const conditionPattern = /^[\w\s\(\)\*\.'",=!><&|]+$/;
|
|
129
|
+
const invalidTriggers = triggers.filter((t) => !t.condition || !conditionPattern.test(t.condition));
|
|
130
|
+
if (invalidTriggers.length === 0) {
|
|
131
|
+
checks.push({
|
|
132
|
+
name: "escalation_triggers",
|
|
133
|
+
passed: true,
|
|
134
|
+
message: `${triggers.length} trigger(s), all valid`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
checks.push({
|
|
139
|
+
name: "escalation_triggers",
|
|
140
|
+
passed: false,
|
|
141
|
+
message: `${invalidTriggers.length} trigger(s) with invalid conditions`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (Array.isArray(triggers) && triggers.length === 0) {
|
|
146
|
+
checks.push({
|
|
147
|
+
name: "escalation_triggers",
|
|
148
|
+
passed: true,
|
|
149
|
+
message: "No triggers defined",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// Check 7: expires_at not already expired
|
|
153
|
+
if (card.expires_at) {
|
|
154
|
+
const expiresDate = new Date(card.expires_at);
|
|
155
|
+
if (isNaN(expiresDate.getTime())) {
|
|
156
|
+
checks.push({
|
|
157
|
+
name: "expires_at",
|
|
158
|
+
passed: false,
|
|
159
|
+
message: "Invalid date format",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else if (expiresDate.getTime() < Date.now()) {
|
|
163
|
+
checks.push({
|
|
164
|
+
name: "expires_at",
|
|
165
|
+
passed: false,
|
|
166
|
+
message: `Already expired: ${card.expires_at}`,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
checks.push({
|
|
171
|
+
name: "expires_at",
|
|
172
|
+
passed: true,
|
|
173
|
+
message: `Valid until ${card.expires_at}`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return checks;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Validate a pre-parsed card object (used for YAML cards).
|
|
181
|
+
* Re-serializes to JSON so the same checks run identically.
|
|
182
|
+
*/
|
|
183
|
+
export function validateCardObject(card) {
|
|
184
|
+
return validateCard(JSON.stringify(card));
|
|
185
|
+
}
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Card display
|
|
188
|
+
// ============================================================================
|
|
189
|
+
function displayCard(cardResponse) {
|
|
190
|
+
const card = cardResponse.card_json;
|
|
191
|
+
console.log(fmt.header("Alignment Card"));
|
|
192
|
+
console.log();
|
|
193
|
+
// Header info
|
|
194
|
+
console.log(fmt.label(" Card ID: ", cardResponse.card_id));
|
|
195
|
+
if (card.version) {
|
|
196
|
+
console.log(fmt.label(" Version: ", card.version));
|
|
197
|
+
}
|
|
198
|
+
if (card.issued_at) {
|
|
199
|
+
console.log(fmt.label(" Issued: ", card.issued_at));
|
|
200
|
+
}
|
|
201
|
+
if (card.expires_at) {
|
|
202
|
+
console.log(fmt.label(" Expires: ", card.expires_at));
|
|
203
|
+
}
|
|
204
|
+
// Principal
|
|
205
|
+
if (card.principal) {
|
|
206
|
+
console.log(fmt.section("Principal"));
|
|
207
|
+
console.log();
|
|
208
|
+
if (card.principal.name) {
|
|
209
|
+
console.log(fmt.label(" Name: ", card.principal.name));
|
|
210
|
+
}
|
|
211
|
+
if (card.principal.type) {
|
|
212
|
+
console.log(fmt.label(" Type: ", card.principal.type));
|
|
213
|
+
}
|
|
214
|
+
if (card.principal.organization) {
|
|
215
|
+
console.log(fmt.label(" Organization:", ` ${card.principal.organization}`));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Values
|
|
219
|
+
if (card.values) {
|
|
220
|
+
console.log(fmt.section("Values"));
|
|
221
|
+
console.log();
|
|
222
|
+
if (Array.isArray(card.values.declared) && card.values.declared.length > 0) {
|
|
223
|
+
const badges = card.values.declared.map((v) => {
|
|
224
|
+
if (STANDARD_VALUES.has(v)) {
|
|
225
|
+
return fmt.badge(v, "cyan");
|
|
226
|
+
}
|
|
227
|
+
return fmt.badge(v, "magenta");
|
|
228
|
+
});
|
|
229
|
+
console.log(` ${badges.join(" ")}`);
|
|
230
|
+
// Show definitions for custom values
|
|
231
|
+
const definitions = card.values.definitions || {};
|
|
232
|
+
const customValues = card.values.declared.filter((v) => !STANDARD_VALUES.has(v));
|
|
233
|
+
if (customValues.length > 0) {
|
|
234
|
+
console.log();
|
|
235
|
+
for (const v of customValues) {
|
|
236
|
+
if (definitions[v]) {
|
|
237
|
+
console.log(fmt.label(` ${v}:`, ` ${definitions[v]}`));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Autonomy envelope
|
|
244
|
+
if (card.autonomy_envelope) {
|
|
245
|
+
console.log(fmt.section("Autonomy Envelope"));
|
|
246
|
+
console.log();
|
|
247
|
+
const bounded = card.autonomy_envelope.bounded_actions;
|
|
248
|
+
if (Array.isArray(bounded) && bounded.length > 0) {
|
|
249
|
+
console.log(" Bounded actions:");
|
|
250
|
+
for (const action of bounded) {
|
|
251
|
+
console.log(` ${fmt.success(action)}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const forbidden = card.autonomy_envelope.forbidden_actions;
|
|
255
|
+
if (Array.isArray(forbidden) && forbidden.length > 0) {
|
|
256
|
+
console.log(" Forbidden actions:");
|
|
257
|
+
for (const action of forbidden) {
|
|
258
|
+
console.log(` ${fmt.error(action)}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const triggers = card.autonomy_envelope.escalation_triggers;
|
|
262
|
+
if (Array.isArray(triggers) && triggers.length > 0) {
|
|
263
|
+
console.log(" Escalation triggers:");
|
|
264
|
+
for (const trigger of triggers) {
|
|
265
|
+
const action = trigger.action ? ` -> ${trigger.action}` : "";
|
|
266
|
+
console.log(` ${fmt.warn(`${trigger.condition}${action}`)}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Audit commitment
|
|
271
|
+
if (card.audit_commitment) {
|
|
272
|
+
console.log(fmt.section("Audit Commitment"));
|
|
273
|
+
console.log();
|
|
274
|
+
if (card.audit_commitment.log_level) {
|
|
275
|
+
console.log(fmt.label(" Log level: ", card.audit_commitment.log_level));
|
|
276
|
+
}
|
|
277
|
+
if (card.audit_commitment.retention_days != null) {
|
|
278
|
+
console.log(fmt.label(" Retention: ", `${card.audit_commitment.retention_days} days`));
|
|
279
|
+
}
|
|
280
|
+
if (card.audit_commitment.access_policy) {
|
|
281
|
+
console.log(fmt.label(" Access policy: ", card.audit_commitment.access_policy));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Extensions
|
|
285
|
+
if (card.extensions && Object.keys(card.extensions).length > 0) {
|
|
286
|
+
console.log(fmt.section("Extensions"));
|
|
287
|
+
console.log();
|
|
288
|
+
console.log(fmt.json(card.extensions));
|
|
289
|
+
}
|
|
290
|
+
console.log();
|
|
291
|
+
}
|
|
292
|
+
// ============================================================================
|
|
293
|
+
// Subcommands
|
|
294
|
+
// ============================================================================
|
|
295
|
+
export async function cardShowCommand(agentName) {
|
|
296
|
+
if (!configExists()) {
|
|
297
|
+
console.log("\n" + fmt.error("smoltbot is not initialized") + "\n");
|
|
298
|
+
console.log("Run `smoltbot init` to get started.\n");
|
|
299
|
+
process.exit(1);
|
|
300
|
+
}
|
|
301
|
+
const config = loadConfig();
|
|
302
|
+
if (!config) {
|
|
303
|
+
console.log("\n" + fmt.error("Failed to load configuration") + "\n");
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
const agent = await requireAgent(agentName);
|
|
307
|
+
if (!agent) {
|
|
308
|
+
console.log("\n" + fmt.error(`Agent not found${agentName ? `: ${agentName}` : ""}`) + "\n");
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
console.log("\nFetching alignment card...\n");
|
|
312
|
+
try {
|
|
313
|
+
const cardResponse = await getCard(agent.agentId);
|
|
314
|
+
if (!cardResponse) {
|
|
315
|
+
console.log(fmt.warn("No custom card -- using default"));
|
|
316
|
+
console.log("\nPublish a custom card with:\n");
|
|
317
|
+
console.log(" smoltbot card publish <file.json>\n");
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
displayCard(cardResponse);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
324
|
+
console.log("\n" + fmt.error(`Failed to fetch card: ${message}`) + "\n");
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
export async function cardPublishCommand(file, agentName) {
|
|
329
|
+
if (!configExists()) {
|
|
330
|
+
console.log("\n" + fmt.error("smoltbot is not initialized") + "\n");
|
|
331
|
+
console.log("Run `smoltbot init` to get started.\n");
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
const config = loadConfig();
|
|
335
|
+
if (!config) {
|
|
336
|
+
console.log("\n" + fmt.error("Failed to load configuration") + "\n");
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
const agent = await requireAgent(agentName);
|
|
340
|
+
if (!agent) {
|
|
341
|
+
console.log("\n" + fmt.error(`Agent not found${agentName ? `: ${agentName}` : ""}`) + "\n");
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
// Resolve file path
|
|
345
|
+
const filePath = path.resolve(file);
|
|
346
|
+
if (!fs.existsSync(filePath)) {
|
|
347
|
+
console.log("\n" + fmt.error(`File not found: ${filePath}`) + "\n");
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
// Parse file (JSON or YAML)
|
|
351
|
+
let parsed;
|
|
352
|
+
let format;
|
|
353
|
+
try {
|
|
354
|
+
const result = parseCardFile(filePath);
|
|
355
|
+
parsed = result.parsed;
|
|
356
|
+
format = result.format;
|
|
357
|
+
}
|
|
358
|
+
catch (e) {
|
|
359
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
360
|
+
console.log("\n" + fmt.error(`Could not parse file: ${msg}`) + "\n");
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
// Validate locally
|
|
364
|
+
const checks = validateCardObject(parsed);
|
|
365
|
+
const allPassed = checks.every((c) => c.passed);
|
|
366
|
+
console.log(fmt.header("Card Validation"));
|
|
367
|
+
console.log(fmt.label(" Format:", ` ${format.toUpperCase()}`));
|
|
368
|
+
console.log();
|
|
369
|
+
for (const check of checks) {
|
|
370
|
+
if (check.passed) {
|
|
371
|
+
console.log(fmt.success(`${check.name}: ${check.message}`));
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
console.log(fmt.error(`${check.name}: ${check.message}`));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
console.log();
|
|
378
|
+
if (!allPassed) {
|
|
379
|
+
console.log(fmt.error("Validation failed. Fix the errors above before publishing.") + "\n");
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
// Require authentication
|
|
383
|
+
await requireAuth();
|
|
384
|
+
// Confirm with user
|
|
385
|
+
if (isInteractive()) {
|
|
386
|
+
const confirm = await askYesNo(`Publish this card for agent ${agent.agentId}?`, false);
|
|
387
|
+
if (!confirm) {
|
|
388
|
+
console.log("\nPublish cancelled.\n");
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// Publish (always sends JSON to API regardless of source format)
|
|
393
|
+
try {
|
|
394
|
+
console.log("\nPublishing card...");
|
|
395
|
+
const result = await updateCard(agent.agentId, parsed);
|
|
396
|
+
console.log(fmt.success(`Card published successfully!`));
|
|
397
|
+
console.log(fmt.label(" Card ID:", ` ${result.card_id}`) + "\n");
|
|
398
|
+
// Trigger re-verification
|
|
399
|
+
try {
|
|
400
|
+
console.log("Triggering re-verification...");
|
|
401
|
+
const reverifyResult = await reverifyAgent(agent.agentId);
|
|
402
|
+
console.log(fmt.success(`Re-verification started (${reverifyResult.reverified} traces queued)`) + "\n");
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
console.log(fmt.warn("Could not trigger re-verification (non-critical)") + "\n");
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
410
|
+
console.log("\n" + fmt.error(`Failed to publish card: ${message}`) + "\n");
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
export async function cardValidateCommand(file) {
|
|
415
|
+
// Resolve file path
|
|
416
|
+
const filePath = path.resolve(file);
|
|
417
|
+
if (!fs.existsSync(filePath)) {
|
|
418
|
+
console.log("\n" + fmt.error(`File not found: ${filePath}`) + "\n");
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
// Parse file (JSON or YAML)
|
|
422
|
+
let parsed;
|
|
423
|
+
let format;
|
|
424
|
+
try {
|
|
425
|
+
const result = parseCardFile(filePath);
|
|
426
|
+
parsed = result.parsed;
|
|
427
|
+
format = result.format;
|
|
428
|
+
}
|
|
429
|
+
catch (e) {
|
|
430
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
431
|
+
console.log("\n" + fmt.error(`Could not parse file: ${msg}`) + "\n");
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
// Validate the parsed object
|
|
435
|
+
const checks = validateCardObject(parsed);
|
|
436
|
+
const allPassed = checks.every((c) => c.passed);
|
|
437
|
+
const passCount = checks.filter((c) => c.passed).length;
|
|
438
|
+
const failCount = checks.filter((c) => !c.passed).length;
|
|
439
|
+
console.log(fmt.header("Card Validation Report"));
|
|
440
|
+
console.log();
|
|
441
|
+
console.log(fmt.label(" File:", ` ${filePath}`));
|
|
442
|
+
console.log(fmt.label(" Format:", ` ${format.toUpperCase()}`));
|
|
443
|
+
console.log();
|
|
444
|
+
for (const check of checks) {
|
|
445
|
+
if (check.passed) {
|
|
446
|
+
console.log(fmt.success(`${check.name}: ${check.message}`));
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
console.log(fmt.error(`${check.name}: ${check.message}`));
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
console.log();
|
|
453
|
+
if (allPassed) {
|
|
454
|
+
console.log(fmt.success(`All ${passCount} checks passed`) + "\n");
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
console.log(fmt.error(`${failCount} check(s) failed, ${passCount} passed`) + "\n");
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function claimCommand(agentName?: string): Promise<void>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import { configExists, loadConfig, getActiveAgent } from "../lib/config.js";
|
|
3
|
+
import { detectOpenClaw } from "../lib/openclaw.js";
|
|
4
|
+
import { claimAgent } from "../lib/api.js";
|
|
5
|
+
import { fmt } from "../lib/format.js";
|
|
6
|
+
const DASHBOARD_URL = "https://mnemom.ai";
|
|
7
|
+
export async function claimCommand(agentName) {
|
|
8
|
+
console.log(fmt.header("smoltbot claim - Link agent to your Mnemom account"));
|
|
9
|
+
console.log();
|
|
10
|
+
// Step 1: Check smoltbot config exists
|
|
11
|
+
if (!configExists()) {
|
|
12
|
+
console.log(fmt.error("smoltbot is not initialized") + "\n");
|
|
13
|
+
console.log("Run `smoltbot init` first.\n");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const config = loadConfig();
|
|
17
|
+
if (!config) {
|
|
18
|
+
console.log(fmt.error("Could not load smoltbot config") + "\n");
|
|
19
|
+
console.log("Run `smoltbot init` to reconfigure.\n");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const agent = getActiveAgent(agentName);
|
|
23
|
+
if (!agent) {
|
|
24
|
+
console.log(fmt.error(`Agent not found${agentName ? `: ${agentName}` : ""}`) + "\n");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
console.log(fmt.label("Agent ID:", ` ${agent.agentId}`) + "\n");
|
|
28
|
+
// Step 2: Read API key from OpenClaw
|
|
29
|
+
const detection = detectOpenClaw();
|
|
30
|
+
if (!detection.hasApiKey || !detection.apiKey) {
|
|
31
|
+
console.log(fmt.error("No API key found") + "\n");
|
|
32
|
+
console.log(detection.error || "Run `openclaw auth` to configure your API key.\n");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
// Step 3: Compute SHA-256 hash proof
|
|
36
|
+
console.log("Computing ownership proof...");
|
|
37
|
+
const hashProof = crypto
|
|
38
|
+
.createHash("sha256")
|
|
39
|
+
.update(detection.apiKey)
|
|
40
|
+
.digest("hex");
|
|
41
|
+
// Step 4: Claim via API
|
|
42
|
+
console.log("Claiming agent...\n");
|
|
43
|
+
try {
|
|
44
|
+
const result = await claimAgent(agent.agentId, hashProof);
|
|
45
|
+
console.log(fmt.success("Agent claimed successfully!") + "\n");
|
|
46
|
+
console.log(` Claimed at: ${new Date(result.claimed_at).toLocaleString()}\n`);
|
|
47
|
+
console.log(fmt.section("Next: Create a Mnemom account to link your agent"));
|
|
48
|
+
console.log(`\n Visit: ${DASHBOARD_URL}/claim/${agent.agentId}\n`);
|
|
49
|
+
console.log(" This lets you manage traces privately and access");
|
|
50
|
+
console.log(" your full transparency dashboard.\n");
|
|
51
|
+
console.log(fmt.label("Dashboard:", ` ${DASHBOARD_URL}/agents/${agent.agentId}`) + "\n");
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
55
|
+
if (message.includes("already been claimed")) {
|
|
56
|
+
console.log("Agent has already been claimed.\n");
|
|
57
|
+
console.log(` Dashboard: ${DASHBOARD_URL}/agents/${agent.agentId}\n`);
|
|
58
|
+
console.log(" If this is your agent, sign in at:");
|
|
59
|
+
console.log(` ${DASHBOARD_URL}/login\n`);
|
|
60
|
+
}
|
|
61
|
+
else if (message.includes("not found")) {
|
|
62
|
+
console.log(fmt.error("Agent not found on server") + "\n");
|
|
63
|
+
console.log(" Your agent will be registered after its first traced API call.");
|
|
64
|
+
console.log(" Make sure you're using a smoltbot model:\n");
|
|
65
|
+
console.log(" openclaw models set smoltbot/<model-id>\n");
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.log(fmt.error(`Claim failed: ${message}`) + "\n");
|
|
69
|
+
}
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|