@heyamiko/amiko-cli 0.9.7 → 0.9.9-beta.1
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/index.js +430 -50
- package/package.json +1 -1
- package/skills/SKILL.md +5 -1
package/dist/index.js
CHANGED
|
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
function __accessProp(key) {
|
|
9
|
-
return this[key];
|
|
10
|
-
}
|
|
11
|
-
var __toESMCache_node;
|
|
12
|
-
var __toESMCache_esm;
|
|
13
8
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
-
var canCache = mod != null && typeof mod === "object";
|
|
15
|
-
if (canCache) {
|
|
16
|
-
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
-
var cached = cache.get(mod);
|
|
18
|
-
if (cached)
|
|
19
|
-
return cached;
|
|
20
|
-
}
|
|
21
9
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
10
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
11
|
for (let key of __getOwnPropNames(mod))
|
|
24
12
|
if (!__hasOwnProp.call(to, key))
|
|
25
13
|
__defProp(to, key, {
|
|
26
|
-
get:
|
|
14
|
+
get: () => mod[key],
|
|
27
15
|
enumerable: true
|
|
28
16
|
});
|
|
29
|
-
if (canCache)
|
|
30
|
-
cache.set(mod, to);
|
|
31
17
|
return to;
|
|
32
18
|
};
|
|
33
19
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
-
var __returnValue = (v) => v;
|
|
35
|
-
function __exportSetter(name, newValue) {
|
|
36
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
-
}
|
|
38
20
|
var __export = (target, all) => {
|
|
39
21
|
for (var name in all)
|
|
40
22
|
__defProp(target, name, {
|
|
41
23
|
get: all[name],
|
|
42
24
|
enumerable: true,
|
|
43
25
|
configurable: true,
|
|
44
|
-
set:
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
45
27
|
});
|
|
46
28
|
};
|
|
47
29
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -3533,36 +3515,11 @@ var require_emoji_regex = __commonJS((exports, module) => {
|
|
|
3533
3515
|
};
|
|
3534
3516
|
});
|
|
3535
3517
|
|
|
3536
|
-
// node_modules/mppx/dist/
|
|
3537
|
-
var
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
from: () => from
|
|
3542
|
-
});
|
|
3543
|
-
function from(method) {
|
|
3544
|
-
return method;
|
|
3545
|
-
}
|
|
3546
|
-
function toClient(method, options) {
|
|
3547
|
-
const { context, createCredential } = options;
|
|
3548
|
-
return {
|
|
3549
|
-
...method,
|
|
3550
|
-
context,
|
|
3551
|
-
createCredential
|
|
3552
|
-
};
|
|
3553
|
-
}
|
|
3554
|
-
function toServer(method, options) {
|
|
3555
|
-
const { defaults, html, request, respond, transport, verify } = options;
|
|
3556
|
-
return {
|
|
3557
|
-
...method,
|
|
3558
|
-
defaults,
|
|
3559
|
-
html,
|
|
3560
|
-
request,
|
|
3561
|
-
respond,
|
|
3562
|
-
transport,
|
|
3563
|
-
verify
|
|
3564
|
-
};
|
|
3565
|
-
}
|
|
3518
|
+
// node_modules/mppx/dist/BodyDigest.js
|
|
3519
|
+
var init_BodyDigest = () => {};
|
|
3520
|
+
|
|
3521
|
+
// node_modules/mppx/dist/PaymentRequest.js
|
|
3522
|
+
var init_PaymentRequest = () => {};
|
|
3566
3523
|
|
|
3567
3524
|
// node_modules/mppx/node_modules/zod/v4/core/core.js
|
|
3568
3525
|
function $constructor(name, initializer, params) {
|
|
@@ -16647,8 +16604,89 @@ var init_zod = __esm(() => {
|
|
|
16647
16604
|
init_mini();
|
|
16648
16605
|
});
|
|
16649
16606
|
|
|
16607
|
+
// node_modules/mppx/dist/Challenge.js
|
|
16608
|
+
var Schema;
|
|
16609
|
+
var init_Challenge = __esm(() => {
|
|
16610
|
+
init_zod();
|
|
16611
|
+
Schema = object({
|
|
16612
|
+
description: optional(string2()),
|
|
16613
|
+
digest: optional(string2().check(_regex(/^sha-256=/, "Invalid digest format"))),
|
|
16614
|
+
expires: optional(datetime3()),
|
|
16615
|
+
id: string2(),
|
|
16616
|
+
intent: string2(),
|
|
16617
|
+
method: string2(),
|
|
16618
|
+
opaque: optional(record(string2(), string2())),
|
|
16619
|
+
realm: string2(),
|
|
16620
|
+
request: record(string2(), unknown())
|
|
16621
|
+
});
|
|
16622
|
+
});
|
|
16623
|
+
|
|
16624
|
+
// node_modules/mppx/dist/Credential.js
|
|
16625
|
+
var init_Credential = () => {};
|
|
16626
|
+
|
|
16627
|
+
// node_modules/mppx/dist/Errors.js
|
|
16628
|
+
var init_Errors = () => {};
|
|
16629
|
+
|
|
16630
|
+
// node_modules/mppx/dist/Expires.js
|
|
16631
|
+
var init_Expires = () => {};
|
|
16632
|
+
|
|
16633
|
+
// node_modules/mppx/dist/Method.js
|
|
16634
|
+
var exports_Method = {};
|
|
16635
|
+
__export(exports_Method, {
|
|
16636
|
+
toServer: () => toServer,
|
|
16637
|
+
toClient: () => toClient,
|
|
16638
|
+
from: () => from
|
|
16639
|
+
});
|
|
16640
|
+
function from(method) {
|
|
16641
|
+
return method;
|
|
16642
|
+
}
|
|
16643
|
+
function toClient(method, options) {
|
|
16644
|
+
const { context, createCredential } = options;
|
|
16645
|
+
return {
|
|
16646
|
+
...method,
|
|
16647
|
+
context,
|
|
16648
|
+
createCredential
|
|
16649
|
+
};
|
|
16650
|
+
}
|
|
16651
|
+
function toServer(method, options) {
|
|
16652
|
+
const { defaults, html, request, respond, transport, verify } = options;
|
|
16653
|
+
return {
|
|
16654
|
+
...method,
|
|
16655
|
+
defaults,
|
|
16656
|
+
html,
|
|
16657
|
+
request,
|
|
16658
|
+
respond,
|
|
16659
|
+
transport,
|
|
16660
|
+
verify
|
|
16661
|
+
};
|
|
16662
|
+
}
|
|
16663
|
+
|
|
16664
|
+
// node_modules/mppx/dist/Receipt.js
|
|
16665
|
+
var Schema2;
|
|
16666
|
+
var init_Receipt = __esm(() => {
|
|
16667
|
+
init_zod();
|
|
16668
|
+
Schema2 = object({
|
|
16669
|
+
method: string2(),
|
|
16670
|
+
reference: string2(),
|
|
16671
|
+
externalId: optional(string2()),
|
|
16672
|
+
status: literal("success"),
|
|
16673
|
+
timestamp: datetime3()
|
|
16674
|
+
});
|
|
16675
|
+
});
|
|
16676
|
+
|
|
16677
|
+
// node_modules/mppx/dist/Store.js
|
|
16678
|
+
var init_Store = () => {};
|
|
16679
|
+
|
|
16650
16680
|
// node_modules/mppx/dist/index.js
|
|
16651
16681
|
var init_dist = __esm(() => {
|
|
16682
|
+
init_BodyDigest();
|
|
16683
|
+
init_Challenge();
|
|
16684
|
+
init_Credential();
|
|
16685
|
+
init_Errors();
|
|
16686
|
+
init_Expires();
|
|
16687
|
+
init_PaymentRequest();
|
|
16688
|
+
init_Receipt();
|
|
16689
|
+
init_Store();
|
|
16652
16690
|
init_zod();
|
|
16653
16691
|
});
|
|
16654
16692
|
|
|
@@ -21974,6 +22012,347 @@ function registerNotificationsCommand(program2) {
|
|
|
21974
22012
|
});
|
|
21975
22013
|
}
|
|
21976
22014
|
|
|
22015
|
+
// src/commands/memory.ts
|
|
22016
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
22017
|
+
import { dirname, isAbsolute, normalize, resolve as resolve2 } from "node:path";
|
|
22018
|
+
var VALID_CATEGORIES = ["fact", "preference", "pattern", "decision", "context"];
|
|
22019
|
+
var VALID_SOURCES = ["chat", "file", "manual"];
|
|
22020
|
+
var DEFAULT_WORKSPACE = "/data/.openclaw/workspace";
|
|
22021
|
+
var SYNC_BATCH_SIZE = 100;
|
|
22022
|
+
function fail(msg) {
|
|
22023
|
+
console.error(error(msg));
|
|
22024
|
+
process.exit(1);
|
|
22025
|
+
}
|
|
22026
|
+
function parseTags(raw) {
|
|
22027
|
+
if (!raw)
|
|
22028
|
+
return;
|
|
22029
|
+
const tags = raw.split(",").map((t) => t.trim()).filter(Boolean);
|
|
22030
|
+
return tags.length ? tags : undefined;
|
|
22031
|
+
}
|
|
22032
|
+
function fmtMemory(m) {
|
|
22033
|
+
console.log("");
|
|
22034
|
+
console.log(label("ID", m.id));
|
|
22035
|
+
console.log(label("Category", m.category));
|
|
22036
|
+
console.log(label("Source", m.source));
|
|
22037
|
+
if (m.tags && m.tags.length)
|
|
22038
|
+
console.log(label("Tags", m.tags.join(", ")));
|
|
22039
|
+
if (typeof m.score === "number")
|
|
22040
|
+
console.log(label("Score", m.score.toFixed(3)));
|
|
22041
|
+
if (m.created_at)
|
|
22042
|
+
console.log(label("Created", m.created_at.slice(0, 19)));
|
|
22043
|
+
console.log(m.content);
|
|
22044
|
+
}
|
|
22045
|
+
function registerMemoryCommand(program2) {
|
|
22046
|
+
const memory = program2.command("memory").description("Manage cross-agent memories — add, list, search, remove, sync local files");
|
|
22047
|
+
memory.command("add <content>").description("Add a single memory directly").option("--category <cat>", `Category (${VALID_CATEGORIES.join("|")})`, "fact").option("--tags <list>", "Comma-separated tags (max 10)").option("--source <src>", `Source (${VALID_SOURCES.join("|")})`, "manual").option("--twin <twinId>", "Override twin_id (defaults to scopedTwinId)").option("--json", "Output as JSON").action(async (content, opts) => {
|
|
22048
|
+
if (!content || !content.trim())
|
|
22049
|
+
fail("Content is required and cannot be empty.");
|
|
22050
|
+
if (opts.category && !VALID_CATEGORIES.includes(opts.category)) {
|
|
22051
|
+
fail(`Invalid category. Must be one of: ${VALID_CATEGORIES.join(", ")}`);
|
|
22052
|
+
}
|
|
22053
|
+
if (opts.source && !VALID_SOURCES.includes(opts.source)) {
|
|
22054
|
+
fail(`Invalid source. Must be one of: ${VALID_SOURCES.join(", ")}`);
|
|
22055
|
+
}
|
|
22056
|
+
const tags = parseTags(opts.tags);
|
|
22057
|
+
if (tags && tags.length > 10)
|
|
22058
|
+
fail("Max 10 tags.");
|
|
22059
|
+
const auth = resolveAuth();
|
|
22060
|
+
const spinner = opts.json ? null : ora("Saving memory...").start();
|
|
22061
|
+
try {
|
|
22062
|
+
const data = await amikoWebFetch(auth, "/api/memories", {
|
|
22063
|
+
method: "POST",
|
|
22064
|
+
body: {
|
|
22065
|
+
content: content.trim(),
|
|
22066
|
+
category: opts.category,
|
|
22067
|
+
tags,
|
|
22068
|
+
source: opts.source,
|
|
22069
|
+
twin_id: opts.twin
|
|
22070
|
+
}
|
|
22071
|
+
});
|
|
22072
|
+
spinner?.stop();
|
|
22073
|
+
renderOutput(data, (d) => {
|
|
22074
|
+
console.log(success("Memory saved"));
|
|
22075
|
+
fmtMemory(d.memory);
|
|
22076
|
+
}, { json: opts.json });
|
|
22077
|
+
} catch (e) {
|
|
22078
|
+
spinner?.stop();
|
|
22079
|
+
fail(e instanceof Error ? e.message : String(e));
|
|
22080
|
+
}
|
|
22081
|
+
});
|
|
22082
|
+
memory.command("list").description("List the caller's memories (newest first)").option("--limit <n>", "Max results (1–100)", "25").option("--offset <n>", "Pagination offset", "0").option("--category <cat>", `Filter (${VALID_CATEGORIES.join("|")})`).option("--source <src>", `Filter (${VALID_SOURCES.join("|")})`).option("--json", "Output as JSON").action(async (opts) => {
|
|
22083
|
+
if (opts.category && !VALID_CATEGORIES.includes(opts.category)) {
|
|
22084
|
+
fail(`Invalid category. Must be one of: ${VALID_CATEGORIES.join(", ")}`);
|
|
22085
|
+
}
|
|
22086
|
+
if (opts.source && !VALID_SOURCES.includes(opts.source)) {
|
|
22087
|
+
fail(`Invalid source. Must be one of: ${VALID_SOURCES.join(", ")}`);
|
|
22088
|
+
}
|
|
22089
|
+
const auth = resolveAuth();
|
|
22090
|
+
const spinner = opts.json ? null : ora("Loading memories...").start();
|
|
22091
|
+
try {
|
|
22092
|
+
const data = await amikoWebFetch(auth, "/api/memories", {
|
|
22093
|
+
query: {
|
|
22094
|
+
limit: opts.limit,
|
|
22095
|
+
offset: opts.offset,
|
|
22096
|
+
category: opts.category,
|
|
22097
|
+
source: opts.source
|
|
22098
|
+
}
|
|
22099
|
+
});
|
|
22100
|
+
spinner?.stop();
|
|
22101
|
+
renderOutput(data, (d) => {
|
|
22102
|
+
if (!d.items.length) {
|
|
22103
|
+
console.log(dim("No memories."));
|
|
22104
|
+
return;
|
|
22105
|
+
}
|
|
22106
|
+
console.log(heading(`Memories — ${d.items.length} of ${d.total}`));
|
|
22107
|
+
for (const m of d.items)
|
|
22108
|
+
fmtMemory(m);
|
|
22109
|
+
if (d.has_more) {
|
|
22110
|
+
const next = Number(opts.offset || "0") + d.items.length;
|
|
22111
|
+
console.log("");
|
|
22112
|
+
console.log(dim(`More available — pass --offset ${next}`));
|
|
22113
|
+
}
|
|
22114
|
+
}, { json: opts.json });
|
|
22115
|
+
} catch (e) {
|
|
22116
|
+
spinner?.stop();
|
|
22117
|
+
fail(e instanceof Error ? e.message : String(e));
|
|
22118
|
+
}
|
|
22119
|
+
});
|
|
22120
|
+
memory.command("search <query>").description("Hybrid search across the caller's memories").option("--limit <n>", "Max results (1–100)", "25").option("--category <cat>", `Filter (${VALID_CATEGORIES.join("|")})`).option("--json", "Output as JSON").action(async (query, opts) => {
|
|
22121
|
+
if (!query || !query.trim())
|
|
22122
|
+
fail("Query is required.");
|
|
22123
|
+
if (opts.category && !VALID_CATEGORIES.includes(opts.category)) {
|
|
22124
|
+
fail(`Invalid category. Must be one of: ${VALID_CATEGORIES.join(", ")}`);
|
|
22125
|
+
}
|
|
22126
|
+
const auth = resolveAuth();
|
|
22127
|
+
const spinner = opts.json ? null : ora("Searching...").start();
|
|
22128
|
+
try {
|
|
22129
|
+
const data = await amikoWebFetch(auth, "/api/memories", {
|
|
22130
|
+
query: {
|
|
22131
|
+
q: query,
|
|
22132
|
+
limit: opts.limit,
|
|
22133
|
+
category: opts.category
|
|
22134
|
+
}
|
|
22135
|
+
});
|
|
22136
|
+
spinner?.stop();
|
|
22137
|
+
renderOutput(data, (d) => {
|
|
22138
|
+
if (!d.items.length) {
|
|
22139
|
+
console.log(dim("No matches."));
|
|
22140
|
+
return;
|
|
22141
|
+
}
|
|
22142
|
+
console.log(heading(`Matches — ${d.items.length} of ${d.total}`));
|
|
22143
|
+
for (const m of d.items)
|
|
22144
|
+
fmtMemory(m);
|
|
22145
|
+
}, { json: opts.json });
|
|
22146
|
+
} catch (e) {
|
|
22147
|
+
spinner?.stop();
|
|
22148
|
+
fail(e instanceof Error ? e.message : String(e));
|
|
22149
|
+
}
|
|
22150
|
+
});
|
|
22151
|
+
memory.command("rm <id>").description("Soft-delete a memory by id").option("--json", "Output as JSON").action(async (id, opts) => {
|
|
22152
|
+
if (!id)
|
|
22153
|
+
fail("Memory id is required.");
|
|
22154
|
+
const auth = resolveAuth();
|
|
22155
|
+
const spinner = opts.json ? null : ora("Deleting...").start();
|
|
22156
|
+
try {
|
|
22157
|
+
const data = await amikoWebFetch(auth, `/api/memories/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
22158
|
+
spinner?.stop();
|
|
22159
|
+
renderOutput(data, () => console.log(success(`Deleted memory ${id}`)), { json: opts.json });
|
|
22160
|
+
} catch (e) {
|
|
22161
|
+
spinner?.stop();
|
|
22162
|
+
fail(e instanceof Error ? e.message : String(e));
|
|
22163
|
+
}
|
|
22164
|
+
});
|
|
22165
|
+
memory.command("status").description("Show counts of memories and synced memory files").option("--json", "Output as JSON").action(async (opts) => {
|
|
22166
|
+
const auth = resolveAuth();
|
|
22167
|
+
const spinner = opts.json ? null : ora("Loading status...").start();
|
|
22168
|
+
try {
|
|
22169
|
+
const [memories, files] = await Promise.all([
|
|
22170
|
+
amikoWebFetch(auth, "/api/memories", {
|
|
22171
|
+
query: { limit: "1" }
|
|
22172
|
+
}),
|
|
22173
|
+
amikoWebFetch(auth, "/api/memory-files", { query: { limit: "1" } })
|
|
22174
|
+
]);
|
|
22175
|
+
spinner?.stop();
|
|
22176
|
+
const fileTotal = files.total ?? files.items?.length ?? 0;
|
|
22177
|
+
renderOutput({ memories: memories.total, memory_files: fileTotal }, (d) => {
|
|
22178
|
+
console.log(heading("Memory status"));
|
|
22179
|
+
console.log(label("Memories", String(d.memories)));
|
|
22180
|
+
console.log(label("Memory files", String(d.memory_files)));
|
|
22181
|
+
}, { json: opts.json });
|
|
22182
|
+
} catch (e) {
|
|
22183
|
+
spinner?.stop();
|
|
22184
|
+
fail(e instanceof Error ? e.message : String(e));
|
|
22185
|
+
}
|
|
22186
|
+
});
|
|
22187
|
+
memory.command("sync").description("Sync MEMORY.md and referenced files to the platform (one-way: local → server)").option("--path <dir>", `Workspace dir containing MEMORY.md (default ${DEFAULT_WORKSPACE})`).option("--dry-run", "Print what would be synced without uploading").option("--prune", "Delete server-side memory files that no longer exist locally").option("--json", "Output as JSON").action(async (opts) => {
|
|
22188
|
+
const root = resolve2(opts.path || DEFAULT_WORKSPACE);
|
|
22189
|
+
const indexPath = resolve2(root, "MEMORY.md");
|
|
22190
|
+
if (!existsSync3(indexPath)) {
|
|
22191
|
+
fail(`MEMORY.md not found at ${indexPath}`);
|
|
22192
|
+
}
|
|
22193
|
+
const collected = collectMemoryFiles(root, indexPath);
|
|
22194
|
+
if (collected.errors.length && !opts.json) {
|
|
22195
|
+
for (const e of collected.errors)
|
|
22196
|
+
console.error(error(e));
|
|
22197
|
+
}
|
|
22198
|
+
const files = collected.files;
|
|
22199
|
+
if (opts.dryRun) {
|
|
22200
|
+
renderOutput({ root, files: files.map((f) => f.path), prune: !!opts.prune }, (d) => {
|
|
22201
|
+
console.log(heading(`Dry run — ${d.files.length} file(s)`));
|
|
22202
|
+
for (const p of d.files)
|
|
22203
|
+
console.log(` ${p}`);
|
|
22204
|
+
if (d.prune)
|
|
22205
|
+
console.log(dim("--prune would remove server files not listed above."));
|
|
22206
|
+
}, { json: opts.json });
|
|
22207
|
+
return;
|
|
22208
|
+
}
|
|
22209
|
+
if (!files.length) {
|
|
22210
|
+
renderOutput({ synced: 0, changed: 0, errors: 0, extracted: 0 }, () => console.log(dim("Nothing to sync.")), { json: opts.json });
|
|
22211
|
+
return;
|
|
22212
|
+
}
|
|
22213
|
+
const auth = resolveAuth();
|
|
22214
|
+
const spinner = opts.json ? null : ora(`Syncing ${files.length} file(s)...`).start();
|
|
22215
|
+
try {
|
|
22216
|
+
const allResults = [];
|
|
22217
|
+
for (let i = 0;i < files.length; i += SYNC_BATCH_SIZE) {
|
|
22218
|
+
const chunk = files.slice(i, i + SYNC_BATCH_SIZE);
|
|
22219
|
+
const data = await amikoWebFetch(auth, "/api/memory-files/bulk", {
|
|
22220
|
+
method: "POST",
|
|
22221
|
+
body: { files: chunk }
|
|
22222
|
+
});
|
|
22223
|
+
allResults.push(...data.results);
|
|
22224
|
+
}
|
|
22225
|
+
let pruned = 0;
|
|
22226
|
+
const pruneErrors = [];
|
|
22227
|
+
if (opts.prune) {
|
|
22228
|
+
const localPaths = new Set(files.map((f) => f.path));
|
|
22229
|
+
const remote = await fetchAllMemoryFiles(auth);
|
|
22230
|
+
const stale = remote.filter((f) => !localPaths.has(f.path));
|
|
22231
|
+
for (const f of stale) {
|
|
22232
|
+
try {
|
|
22233
|
+
await amikoWebFetch(auth, `/api/memory-files/${encodeURIComponent(f.id)}`, { method: "DELETE" });
|
|
22234
|
+
pruned++;
|
|
22235
|
+
} catch (e) {
|
|
22236
|
+
pruneErrors.push(`${f.path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
22237
|
+
}
|
|
22238
|
+
}
|
|
22239
|
+
}
|
|
22240
|
+
spinner?.stop();
|
|
22241
|
+
const changed = allResults.filter((r) => r.changed).length;
|
|
22242
|
+
const errors2 = allResults.filter((r) => r.error).length;
|
|
22243
|
+
const extracted = allResults.reduce((acc, r) => acc + (r.extracted ?? 0), 0);
|
|
22244
|
+
renderOutput({
|
|
22245
|
+
synced: allResults.length,
|
|
22246
|
+
changed,
|
|
22247
|
+
errors: errors2,
|
|
22248
|
+
extracted,
|
|
22249
|
+
pruned,
|
|
22250
|
+
prune_errors: pruneErrors,
|
|
22251
|
+
results: allResults
|
|
22252
|
+
}, (d) => {
|
|
22253
|
+
console.log(success(`${d.synced} file(s) synced (${d.changed} changed, ${d.errors} errors), ${d.extracted} memories extracted`));
|
|
22254
|
+
for (const r of allResults) {
|
|
22255
|
+
if (r.error) {
|
|
22256
|
+
console.log(error(`${r.path}: ${r.error}`));
|
|
22257
|
+
} else if (r.changed) {
|
|
22258
|
+
console.log(` ${r.path} ${dim(`→ ${r.extracted ?? 0} extracted`)}`);
|
|
22259
|
+
} else {
|
|
22260
|
+
console.log(` ${r.path} ${dim("(unchanged)")}`);
|
|
22261
|
+
}
|
|
22262
|
+
}
|
|
22263
|
+
if (opts.prune) {
|
|
22264
|
+
console.log("");
|
|
22265
|
+
console.log(label("Pruned", String(d.pruned)));
|
|
22266
|
+
for (const e of pruneErrors)
|
|
22267
|
+
console.log(error(e));
|
|
22268
|
+
}
|
|
22269
|
+
}, { json: opts.json });
|
|
22270
|
+
} catch (e) {
|
|
22271
|
+
spinner?.stop();
|
|
22272
|
+
fail(e instanceof Error ? e.message : String(e));
|
|
22273
|
+
}
|
|
22274
|
+
});
|
|
22275
|
+
}
|
|
22276
|
+
function collectMemoryFiles(root, indexPath) {
|
|
22277
|
+
const errors2 = [];
|
|
22278
|
+
const seen = new Set;
|
|
22279
|
+
const files = [];
|
|
22280
|
+
const indexContent = safeRead(indexPath, errors2);
|
|
22281
|
+
if (indexContent === null)
|
|
22282
|
+
return { files, errors: errors2 };
|
|
22283
|
+
pushFile(files, seen, indexPath, indexContent, root);
|
|
22284
|
+
const indexDir = dirname(indexPath);
|
|
22285
|
+
for (const ref of extractMarkdownLinks(indexContent)) {
|
|
22286
|
+
if (isExternal(ref))
|
|
22287
|
+
continue;
|
|
22288
|
+
const abs = normalize(isAbsolute(ref) ? ref : resolve2(indexDir, ref));
|
|
22289
|
+
if (!abs.startsWith(root)) {
|
|
22290
|
+
errors2.push(`Skipping ${ref}: outside workspace ${root}`);
|
|
22291
|
+
continue;
|
|
22292
|
+
}
|
|
22293
|
+
if (!existsSync3(abs)) {
|
|
22294
|
+
errors2.push(`Referenced file missing: ${ref}`);
|
|
22295
|
+
continue;
|
|
22296
|
+
}
|
|
22297
|
+
const st = statSync(abs);
|
|
22298
|
+
if (!st.isFile())
|
|
22299
|
+
continue;
|
|
22300
|
+
const content = safeRead(abs, errors2);
|
|
22301
|
+
if (content === null)
|
|
22302
|
+
continue;
|
|
22303
|
+
pushFile(files, seen, abs, content, root);
|
|
22304
|
+
}
|
|
22305
|
+
return { files, errors: errors2 };
|
|
22306
|
+
}
|
|
22307
|
+
function pushFile(files, seen, abs, content, root) {
|
|
22308
|
+
const key = relativeKey(abs, root);
|
|
22309
|
+
if (seen.has(key))
|
|
22310
|
+
return;
|
|
22311
|
+
seen.add(key);
|
|
22312
|
+
files.push({ path: key, content });
|
|
22313
|
+
}
|
|
22314
|
+
function relativeKey(abs, root) {
|
|
22315
|
+
const rel = abs.startsWith(root) ? abs.slice(root.length) : abs;
|
|
22316
|
+
return rel.replace(/^\/+/, "");
|
|
22317
|
+
}
|
|
22318
|
+
function safeRead(path2, errors2) {
|
|
22319
|
+
try {
|
|
22320
|
+
return readFileSync2(path2, "utf-8");
|
|
22321
|
+
} catch (e) {
|
|
22322
|
+
errors2.push(`Failed to read ${path2}: ${e instanceof Error ? e.message : String(e)}`);
|
|
22323
|
+
return null;
|
|
22324
|
+
}
|
|
22325
|
+
}
|
|
22326
|
+
function extractMarkdownLinks(md) {
|
|
22327
|
+
const out = [];
|
|
22328
|
+
const re = /\[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
22329
|
+
let m;
|
|
22330
|
+
while ((m = re.exec(md)) !== null) {
|
|
22331
|
+
const target = m[1].split("#")[0];
|
|
22332
|
+
if (target)
|
|
22333
|
+
out.push(target);
|
|
22334
|
+
}
|
|
22335
|
+
return out;
|
|
22336
|
+
}
|
|
22337
|
+
function isExternal(ref) {
|
|
22338
|
+
return /^[a-z][a-z0-9+.-]*:/i.test(ref) || ref.startsWith("//");
|
|
22339
|
+
}
|
|
22340
|
+
async function fetchAllMemoryFiles(auth) {
|
|
22341
|
+
const all = [];
|
|
22342
|
+
let offset = 0;
|
|
22343
|
+
const limit = 100;
|
|
22344
|
+
for (;; ) {
|
|
22345
|
+
const data = await amikoWebFetch(auth, "/api/memory-files", {
|
|
22346
|
+
query: { limit: String(limit), offset: String(offset) }
|
|
22347
|
+
});
|
|
22348
|
+
all.push(...data.items ?? []);
|
|
22349
|
+
if (!data.has_more || !data.items?.length)
|
|
22350
|
+
break;
|
|
22351
|
+
offset += data.items.length;
|
|
22352
|
+
}
|
|
22353
|
+
return all;
|
|
22354
|
+
}
|
|
22355
|
+
|
|
21977
22356
|
// src/commands/wallets.ts
|
|
21978
22357
|
function formatBalance(b) {
|
|
21979
22358
|
const n = Number(b.amount) / 10 ** b.decimals;
|
|
@@ -22217,4 +22596,5 @@ registerReviewCommand(review);
|
|
|
22217
22596
|
registerComposioCommand(composio);
|
|
22218
22597
|
registerConversationCommand(conversation);
|
|
22219
22598
|
registerNotificationsCommand(notifications);
|
|
22599
|
+
registerMemoryCommand(program2);
|
|
22220
22600
|
program2.parse();
|
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: amiko-cli
|
|
3
|
-
description: The Amiko CLI lets an agent act on the Amiko platform end-to-end — manage Solana/Base wallets (create, swap via Jupiter, bridge USDC via Across, transfer tokens to external addresses), top up and spend credits, call paid MPP marketplace services (X/Twitter search, image generation, Amazon product search, TTS/STT, AI chat, SFX/music), manage the twin's identity and RAG documents, voice and avatar, social graph (friends, posts, comments, feed), and Composio OAuth connections. Auth is automatic when run from the agent's workspace folder; payments are platform-custodied (no keys on disk). Use this skill whenever the user asks to do anything that would show up in their Amiko account or cost AMIKO/credits.
|
|
3
|
+
description: The Amiko CLI lets an agent act on the Amiko platform end-to-end — read and send platform DMs with other users (conversations list/find/read/send, the right tool when the owner asks "did X message me?" or "check my chat history with Y" — NOT the built-in sessions_list/sessions_history, which only see the agent's own local sessions), read platform notifications (friend requests, mentions, system alerts), manage Solana/Base wallets (create, swap via Jupiter, bridge USDC via Across, transfer tokens to external addresses), top up and spend credits, call paid MPP marketplace services (X/Twitter search, image generation, Amazon product search, TTS/STT, AI chat, SFX/music), manage the twin's identity and RAG documents, voice and avatar, social graph (friends, posts, comments, feed), and Composio OAuth connections. Auth is automatic when run from the agent's workspace folder; payments are platform-custodied (no keys on disk). Use this skill whenever the user asks about their Amiko inbox/DMs/notifications or to do anything that would show up in their Amiko account or cost AMIKO/credits.
|
|
4
4
|
homepage: https://platform.heyamiko.com
|
|
5
5
|
metadata: {"openclaw":{"emoji":"🤖","requires":{"bins":["node"]}}}
|
|
6
6
|
---
|
|
@@ -264,6 +264,10 @@ amiko review reject <commentId> --yes # destructive — delete a draft
|
|
|
264
264
|
|
|
265
265
|
## Conversations
|
|
266
266
|
|
|
267
|
+
> **Default platform = Amiko.** When the owner asks about messages, DMs, chats, notifications, or "anything new" **without naming a platform** (no "on WeChat", "on Telegram", "on Slack", etc.), assume they mean the Amiko platform and answer with `amiko conversation` / `amiko notifications`. Do **not** ask "which platform?" — just use the Amiko CLI. Only ask for clarification if the owner explicitly mentions a non-Amiko channel or the question is genuinely ambiguous across multiple connected channels.
|
|
268
|
+
>
|
|
269
|
+
> **When the owner asks about chat history with another person** ("did X message me?", "check my chat with Y", "anyone DM me this week?"), use `amiko conversation` — these are platform DMs between the owner and other Amiko users, stored in amiko-web's database. Do **not** use the built-in `sessions_list` / `sessions_history` tools for this: those read the agent's *own* past chat sessions with the owner (local openclaw memory), not platform DMs with third parties, and will return nothing useful. Typical flow: `amiko users search "<name>"` → `amiko conversation find --user-id <id>` → `amiko conversation read --id <conversationId>`. To scan everyone who messaged recently, start with `amiko conversation list` (sorted by last activity) or `amiko notifications list --unread`.
|
|
270
|
+
|
|
267
271
|
```bash
|
|
268
272
|
amiko conversation list # all your conversations (default: active)
|
|
269
273
|
amiko conversation list --archived all --limit 100 # include archived
|