@aight-cool/aight-utils 0.1.20 → 0.1.22
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/hooks/aight-bootstrap/HOOK.md +20 -0
- package/dist/hooks/aight-bootstrap/handler.d.ts +19 -0
- package/dist/hooks/aight-bootstrap/handler.js +16 -266
- package/dist/hooks/aight-bootstrap/handler.js.map +1 -1
- package/dist/index.d.ts +42 -0
- package/dist/index.js +62 -3799
- package/dist/index.js.map +1 -1
- package/dist/src/bootstrap.d.ts +6 -0
- package/dist/src/bootstrap.js +265 -0
- package/dist/src/bootstrap.js.map +1 -0
- package/dist/src/config.d.ts +18 -0
- package/dist/src/config.js +100 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/defaults.d.ts +4 -0
- package/dist/src/defaults.js +5 -0
- package/dist/src/defaults.js.map +1 -0
- package/dist/src/groups.d.ts +10 -0
- package/dist/src/groups.js +59 -0
- package/dist/src/groups.js.map +1 -0
- package/dist/src/health.d.ts +8 -0
- package/dist/src/health.js +58 -0
- package/dist/src/health.js.map +1 -0
- package/dist/src/items.d.ts +51 -0
- package/dist/src/items.js +208 -0
- package/dist/src/items.js.map +1 -0
- package/dist/src/notif-prefs.d.ts +26 -0
- package/dist/src/notif-prefs.js +93 -0
- package/dist/src/notif-prefs.js.map +1 -0
- package/dist/src/push-hook.d.ts +5 -0
- package/dist/src/push-hook.js +117 -0
- package/dist/src/push-hook.js.map +1 -0
- package/dist/src/push-net.d.ts +17 -0
- package/dist/src/push-net.js +61 -0
- package/dist/src/push-net.js.map +1 -0
- package/dist/src/push-store.d.ts +15 -0
- package/dist/src/push-store.js +45 -0
- package/dist/src/push-store.js.map +1 -0
- package/dist/src/push.d.ts +16 -0
- package/dist/src/push.js +86 -0
- package/dist/src/push.js.map +1 -0
- package/dist/src/reminders.d.ts +6 -0
- package/dist/src/reminders.js +75 -0
- package/dist/src/reminders.js.map +1 -0
- package/dist/src/version.d.ts +8 -0
- package/dist/src/version.js +52 -0
- package/dist/src/version.js.map +1 -0
- package/openclaw.plugin.json +7 -1
- package/package.json +2 -2
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Items Store — aight.items.list, aight.items.upsert, aight.items.delete + aight_item tool
|
|
3
|
+
*/
|
|
4
|
+
import { Type } from "@sinclair/typebox";
|
|
5
|
+
import * as fs from "node:fs";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
import * as os from "node:os";
|
|
8
|
+
// ── Types ──
|
|
9
|
+
export const ItemType = Type.Union([
|
|
10
|
+
Type.Literal("trigger"),
|
|
11
|
+
Type.Literal("item"),
|
|
12
|
+
Type.Literal("process"),
|
|
13
|
+
]);
|
|
14
|
+
export const ItemStatus = Type.Union([
|
|
15
|
+
Type.Literal("active"),
|
|
16
|
+
Type.Literal("done"),
|
|
17
|
+
Type.Literal("fired"),
|
|
18
|
+
Type.Literal("cancelled"),
|
|
19
|
+
Type.Literal("deleted"),
|
|
20
|
+
]);
|
|
21
|
+
export const ItemSchema = Type.Object({
|
|
22
|
+
id: Type.String({ description: "Unique item ID" }),
|
|
23
|
+
type: ItemType,
|
|
24
|
+
title: Type.String({ description: "Display title" }),
|
|
25
|
+
status: Type.Optional(ItemStatus),
|
|
26
|
+
labels: Type.Optional(Type.Array(Type.String())),
|
|
27
|
+
scheduledFor: Type.Optional(Type.String({ description: "ISO 8601 datetime for triggers" })),
|
|
28
|
+
description: Type.Optional(Type.String()),
|
|
29
|
+
url: Type.Optional(Type.String({ description: "Related URL (PR, issue, etc.)" })),
|
|
30
|
+
metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
31
|
+
createdAt: Type.Optional(Type.String()),
|
|
32
|
+
updatedAt: Type.Optional(Type.String()),
|
|
33
|
+
});
|
|
34
|
+
// ── Store ──
|
|
35
|
+
const STORE_DIR = path.join(os.homedir(), ".openclaw", "aight");
|
|
36
|
+
const STORE_FILE = path.join(STORE_DIR, "items.json");
|
|
37
|
+
export function loadItems() {
|
|
38
|
+
try {
|
|
39
|
+
if (!fs.existsSync(STORE_FILE))
|
|
40
|
+
return [];
|
|
41
|
+
const raw = fs.readFileSync(STORE_FILE, "utf-8");
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function saveItems(items) {
|
|
50
|
+
fs.mkdirSync(STORE_DIR, { recursive: true });
|
|
51
|
+
fs.writeFileSync(STORE_FILE, JSON.stringify(items, null, 2), { encoding: "utf-8", mode: 0o600 });
|
|
52
|
+
}
|
|
53
|
+
const MAX_TITLE_LEN = 500;
|
|
54
|
+
const MAX_DESC_LEN = 5000;
|
|
55
|
+
const MAX_ITEMS = 10000;
|
|
56
|
+
export function upsertItem(item) {
|
|
57
|
+
if (item.title && item.title.length > MAX_TITLE_LEN) {
|
|
58
|
+
throw new Error(`title exceeds max length of ${MAX_TITLE_LEN}`);
|
|
59
|
+
}
|
|
60
|
+
if (item.description && item.description.length > MAX_DESC_LEN) {
|
|
61
|
+
throw new Error(`description exceeds max length of ${MAX_DESC_LEN}`);
|
|
62
|
+
}
|
|
63
|
+
const items = loadItems();
|
|
64
|
+
const now = new Date().toISOString();
|
|
65
|
+
const idx = items.findIndex((i) => i.id === item.id);
|
|
66
|
+
if (idx < 0 && items.length >= MAX_ITEMS) {
|
|
67
|
+
throw new Error(`item store full (max ${MAX_ITEMS})`);
|
|
68
|
+
}
|
|
69
|
+
const merged = {
|
|
70
|
+
...item,
|
|
71
|
+
status: item.status ?? "active",
|
|
72
|
+
updatedAt: now,
|
|
73
|
+
createdAt: idx >= 0 ? items[idx].createdAt : now,
|
|
74
|
+
};
|
|
75
|
+
if (idx >= 0) {
|
|
76
|
+
items[idx] = merged;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
items.push(merged);
|
|
80
|
+
}
|
|
81
|
+
saveItems(items);
|
|
82
|
+
return merged;
|
|
83
|
+
}
|
|
84
|
+
export function deleteItem(id) {
|
|
85
|
+
const items = loadItems();
|
|
86
|
+
const idx = items.findIndex((i) => i.id === id);
|
|
87
|
+
if (idx < 0)
|
|
88
|
+
return false;
|
|
89
|
+
items[idx] = { ...items[idx], status: "deleted", updatedAt: new Date().toISOString() };
|
|
90
|
+
saveItems(items);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
export function listItems(filters = {}) {
|
|
94
|
+
let items = loadItems().filter((i) => i.status !== "deleted");
|
|
95
|
+
if (filters.type) {
|
|
96
|
+
items = items.filter((i) => i.type === filters.type);
|
|
97
|
+
}
|
|
98
|
+
if (filters.status) {
|
|
99
|
+
items = items.filter((i) => i.status === filters.status);
|
|
100
|
+
}
|
|
101
|
+
if (filters.labels && filters.labels.length > 0) {
|
|
102
|
+
items = items.filter((i) => filters.labels.some((l) => i.labels?.includes(l)));
|
|
103
|
+
}
|
|
104
|
+
if (filters.from) {
|
|
105
|
+
const fromTs = new Date(filters.from).getTime();
|
|
106
|
+
items = items.filter((i) => {
|
|
107
|
+
if (!i.scheduledFor)
|
|
108
|
+
return true;
|
|
109
|
+
return new Date(i.scheduledFor).getTime() >= fromTs;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (filters.to) {
|
|
113
|
+
const toTs = new Date(filters.to).getTime();
|
|
114
|
+
items = items.filter((i) => {
|
|
115
|
+
if (!i.scheduledFor)
|
|
116
|
+
return true;
|
|
117
|
+
return new Date(i.scheduledFor).getTime() <= toTs;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return items;
|
|
121
|
+
}
|
|
122
|
+
// ── Tool schema ──
|
|
123
|
+
export const AightItemToolParams = Type.Object({
|
|
124
|
+
action: Type.Union([Type.Literal("create"), Type.Literal("update"), Type.Literal("delete")]),
|
|
125
|
+
item: Type.Optional(ItemSchema),
|
|
126
|
+
id: Type.Optional(Type.String({ description: "Item ID (for delete)" })),
|
|
127
|
+
});
|
|
128
|
+
// ── Registration ──
|
|
129
|
+
/** Strip heavy fields for list responses — detail is fetched via aight.items.get */
|
|
130
|
+
function toLightItem(item) {
|
|
131
|
+
const { description: _d, metadata: _m, url: _u, ...light } = item;
|
|
132
|
+
return light;
|
|
133
|
+
}
|
|
134
|
+
export function registerItems(api) {
|
|
135
|
+
api.registerGatewayMethod("aight.items.list", ({ params, respond }) => {
|
|
136
|
+
const filters = params && typeof params === "object" ? params : {};
|
|
137
|
+
respond(true, { items: listItems(filters).map(toLightItem) });
|
|
138
|
+
});
|
|
139
|
+
api.registerGatewayMethod("aight.items.get", ({ params, respond }) => {
|
|
140
|
+
const id = typeof params?.id === "string" ? params.id : "";
|
|
141
|
+
if (!id) {
|
|
142
|
+
respond(false, { error: "id required" });
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const all = loadItems();
|
|
146
|
+
const item = all.find((i) => i.id === id);
|
|
147
|
+
if (!item || item.status === "deleted") {
|
|
148
|
+
respond(false, { error: "item not found" });
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
respond(true, { item });
|
|
152
|
+
});
|
|
153
|
+
api.registerGatewayMethod("aight.items.upsert", ({ params, respond }) => {
|
|
154
|
+
if (!params ||
|
|
155
|
+
typeof params !== "object" ||
|
|
156
|
+
!("id" in params) ||
|
|
157
|
+
!("type" in params) ||
|
|
158
|
+
!("title" in params)) {
|
|
159
|
+
respond(false, { error: "item must have id, type, and title" });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const item = upsertItem(params);
|
|
163
|
+
respond(true, { item });
|
|
164
|
+
});
|
|
165
|
+
api.registerGatewayMethod("aight.items.delete", ({ params, respond }) => {
|
|
166
|
+
const id = typeof params?.id === "string" ? params.id : "";
|
|
167
|
+
if (!id) {
|
|
168
|
+
respond(false, { error: "id required" });
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const ok = deleteItem(id);
|
|
172
|
+
respond(true, { ok, id });
|
|
173
|
+
});
|
|
174
|
+
api.registerTool({
|
|
175
|
+
name: "aight_item",
|
|
176
|
+
label: "Aight Item",
|
|
177
|
+
description: "Create, update, or delete a structured item in the Aight Today view. " +
|
|
178
|
+
"Use for reminders, tasks, events, deadlines, and process tracking. " +
|
|
179
|
+
"Parse natural language dates/times before calling (e.g. 'tomorrow at 3pm' → ISO 8601).",
|
|
180
|
+
parameters: AightItemToolParams,
|
|
181
|
+
async execute(_toolCallId, params) {
|
|
182
|
+
const json = (payload) => ({
|
|
183
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
184
|
+
details: payload,
|
|
185
|
+
});
|
|
186
|
+
try {
|
|
187
|
+
if (params.action === "delete") {
|
|
188
|
+
const id = params.id ?? params.item?.id;
|
|
189
|
+
if (!id)
|
|
190
|
+
throw new Error("id required for delete");
|
|
191
|
+
const ok = deleteItem(id);
|
|
192
|
+
return json({ ok, id });
|
|
193
|
+
}
|
|
194
|
+
if (!params.item)
|
|
195
|
+
throw new Error("item required for create/update");
|
|
196
|
+
if (!params.item.id || !params.item.type || !params.item.title) {
|
|
197
|
+
throw new Error("item must have id, type, and title");
|
|
198
|
+
}
|
|
199
|
+
const item = upsertItem(params.item);
|
|
200
|
+
return json({ action: params.action, item });
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=items.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"items.js","sourceRoot":"","sources":["../../src/items.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification Preferences — gateway-side storage for push suppression.
|
|
3
|
+
*
|
|
4
|
+
* The app syncs notification preferences here via RPC. The push hook
|
|
5
|
+
* checks these prefs before sending to the relay — if a category is
|
|
6
|
+
* muted, the push never leaves the user's machine.
|
|
7
|
+
*
|
|
8
|
+
* RPC methods:
|
|
9
|
+
* aight.notif.setPrefs — update preferences
|
|
10
|
+
* aight.notif.getPrefs — read current preferences
|
|
11
|
+
*/
|
|
12
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
13
|
+
export interface NotifPrefs {
|
|
14
|
+
globalMute: boolean;
|
|
15
|
+
muteUntil: string | null;
|
|
16
|
+
agentReplies: boolean;
|
|
17
|
+
groupChat: boolean;
|
|
18
|
+
cron: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function loadNotifPrefs(): NotifPrefs;
|
|
21
|
+
export declare function classifySessionKey(sessionKey: string): "agentReplies" | "groupChat" | "cron";
|
|
22
|
+
/**
|
|
23
|
+
* Check if a push for this sessionKey should be sent.
|
|
24
|
+
*/
|
|
25
|
+
export declare function shouldSendPush(sessionKey: string): boolean;
|
|
26
|
+
export declare function registerNotifPrefsRPC(api: OpenClawPluginApi): void;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification Preferences — gateway-side storage for push suppression.
|
|
3
|
+
*
|
|
4
|
+
* The app syncs notification preferences here via RPC. The push hook
|
|
5
|
+
* checks these prefs before sending to the relay — if a category is
|
|
6
|
+
* muted, the push never leaves the user's machine.
|
|
7
|
+
*
|
|
8
|
+
* RPC methods:
|
|
9
|
+
* aight.notif.setPrefs — update preferences
|
|
10
|
+
* aight.notif.getPrefs — read current preferences
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import * as os from "node:os";
|
|
15
|
+
const DEFAULTS = {
|
|
16
|
+
globalMute: false,
|
|
17
|
+
muteUntil: null,
|
|
18
|
+
agentReplies: true,
|
|
19
|
+
groupChat: true,
|
|
20
|
+
cron: false,
|
|
21
|
+
};
|
|
22
|
+
// ── Storage ──
|
|
23
|
+
const PREFS_DIR = path.join(os.homedir(), ".openclaw", "aight");
|
|
24
|
+
const PREFS_FILE = path.join(PREFS_DIR, "notif-prefs.json");
|
|
25
|
+
export function loadNotifPrefs() {
|
|
26
|
+
try {
|
|
27
|
+
if (!fs.existsSync(PREFS_FILE))
|
|
28
|
+
return { ...DEFAULTS };
|
|
29
|
+
const raw = fs.readFileSync(PREFS_FILE, "utf-8");
|
|
30
|
+
const parsed = JSON.parse(raw);
|
|
31
|
+
const prefs = { ...DEFAULTS, ...parsed };
|
|
32
|
+
// Normalize expired timed mute in memory so getPrefs returns coherent
|
|
33
|
+
// state to the app. No write — disk is reconciled on the next setPrefs.
|
|
34
|
+
if (prefs.globalMute && prefs.muteUntil && Date.now() >= new Date(prefs.muteUntil).getTime()) {
|
|
35
|
+
prefs.globalMute = false;
|
|
36
|
+
prefs.muteUntil = null;
|
|
37
|
+
}
|
|
38
|
+
return prefs;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return { ...DEFAULTS };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function saveNotifPrefs(prefs) {
|
|
45
|
+
fs.mkdirSync(PREFS_DIR, { recursive: true, mode: 0o700 });
|
|
46
|
+
fs.writeFileSync(PREFS_FILE, JSON.stringify(prefs, null, 2), "utf-8");
|
|
47
|
+
}
|
|
48
|
+
// ── Classification (matches app-side logic) ──
|
|
49
|
+
export function classifySessionKey(sessionKey) {
|
|
50
|
+
if (sessionKey.includes(":cron:"))
|
|
51
|
+
return "cron";
|
|
52
|
+
if (sessionKey.includes(":group-chat:"))
|
|
53
|
+
return "groupChat";
|
|
54
|
+
return "agentReplies";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if a push for this sessionKey should be sent.
|
|
58
|
+
*/
|
|
59
|
+
export function shouldSendPush(sessionKey) {
|
|
60
|
+
if (!sessionKey)
|
|
61
|
+
return true; // fail open
|
|
62
|
+
const prefs = loadNotifPrefs();
|
|
63
|
+
// Global mute (loadNotifPrefs already normalizes expired timed mutes)
|
|
64
|
+
if (prefs.globalMute)
|
|
65
|
+
return false;
|
|
66
|
+
// Category check
|
|
67
|
+
const category = classifySessionKey(sessionKey);
|
|
68
|
+
return prefs[category];
|
|
69
|
+
}
|
|
70
|
+
// ── RPC Registration ──
|
|
71
|
+
export function registerNotifPrefsRPC(api) {
|
|
72
|
+
api.registerGatewayMethod("aight.notif.getPrefs", ({ respond }) => {
|
|
73
|
+
respond(true, loadNotifPrefs());
|
|
74
|
+
});
|
|
75
|
+
api.registerGatewayMethod("aight.notif.setPrefs", ({ params, respond }) => {
|
|
76
|
+
const update = (params ?? {});
|
|
77
|
+
const current = loadNotifPrefs();
|
|
78
|
+
if (typeof update.globalMute === "boolean")
|
|
79
|
+
current.globalMute = update.globalMute;
|
|
80
|
+
if (typeof update.agentReplies === "boolean")
|
|
81
|
+
current.agentReplies = update.agentReplies;
|
|
82
|
+
if (typeof update.groupChat === "boolean")
|
|
83
|
+
current.groupChat = update.groupChat;
|
|
84
|
+
if (typeof update.cron === "boolean")
|
|
85
|
+
current.cron = update.cron;
|
|
86
|
+
if (update.muteUntil !== undefined)
|
|
87
|
+
current.muteUntil = update.muteUntil;
|
|
88
|
+
saveNotifPrefs(current);
|
|
89
|
+
api.logger.info(`[aight-utils] Notification prefs updated: ${JSON.stringify(current)}`);
|
|
90
|
+
respond(true, current);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=notif-prefs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notif-prefs.js","sourceRoot":"","sources":["../../src/notif-prefs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push notification hook — sends push on agent_end.
|
|
3
|
+
*/
|
|
4
|
+
import { getPluginConfig } from "./config.js";
|
|
5
|
+
import { loadTokens, unregisterToken } from "./push-store.js";
|
|
6
|
+
import { sendPush } from "./push-net.js";
|
|
7
|
+
import { loadGroupName } from "./groups.js";
|
|
8
|
+
import { shouldSendPush } from "./notif-prefs.js";
|
|
9
|
+
const HIDDEN_SUFFIXES = [
|
|
10
|
+
":aight-config",
|
|
11
|
+
":aight-pentest",
|
|
12
|
+
":speak",
|
|
13
|
+
":structured_content",
|
|
14
|
+
":main",
|
|
15
|
+
"security-fix",
|
|
16
|
+
"skill-scan",
|
|
17
|
+
];
|
|
18
|
+
const HIDDEN_SUBSTRINGS = ["subagent", "security-audit", "_skill-audit-", "_ensure-skill-defender"];
|
|
19
|
+
const META_RESPONSES = ["NO_REPLY", "REPLY_SKIP", "ANNOUNCE_SKIP", "HEARTBEAT_OK"];
|
|
20
|
+
const STALE_TOKEN_ERRORS = ["baddevicetoken", "unregistered", "devicetokennotfortopic", "expired"];
|
|
21
|
+
export function registerPushHook(api) {
|
|
22
|
+
try {
|
|
23
|
+
api.on("agent_end", async (event, ctx) => {
|
|
24
|
+
const sessionKey = ctx.sessionKey ?? "";
|
|
25
|
+
api.logger.info(`[aight-utils] agent_end fired session=${sessionKey} agent=${ctx.agentId}`);
|
|
26
|
+
const tokens = loadTokens();
|
|
27
|
+
if (tokens.length === 0)
|
|
28
|
+
return;
|
|
29
|
+
// Skip hidden/internal sessions — no push notifications for config,
|
|
30
|
+
// security, voice, sub-agent, or other background sessions.
|
|
31
|
+
if (HIDDEN_SUFFIXES.some((s) => sessionKey.endsWith(s)) ||
|
|
32
|
+
HIDDEN_SUBSTRINGS.some((s) => sessionKey.includes(s))) {
|
|
33
|
+
api.logger.info(`[aight-utils] Skipping hidden session push: ${sessionKey}`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Notification preference gate — bail before expensive preview extraction
|
|
37
|
+
if (!shouldSendPush(sessionKey)) {
|
|
38
|
+
api.logger.info(`[aight-utils] Push suppressed by notification prefs: ${sessionKey}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const msgs = event.messages ?? [];
|
|
42
|
+
// Extract last assistant message - try string and array content formats
|
|
43
|
+
let preview = "";
|
|
44
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
45
|
+
const m = msgs[i];
|
|
46
|
+
if (m.role === "assistant") {
|
|
47
|
+
if (typeof m.content === "string" && m.content.trim()) {
|
|
48
|
+
preview = m.content.slice(0, 200);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(m.content)) {
|
|
52
|
+
const textBlock = m.content.find((b) => b.type === "text" && typeof b.text === "string");
|
|
53
|
+
if (textBlock) {
|
|
54
|
+
preview = textBlock.text.slice(0, 200);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!preview) {
|
|
61
|
+
api.logger.info(`[aight-utils] No preview found, skipping push`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (META_RESPONSES.includes(preview.trim())) {
|
|
65
|
+
api.logger.info(`[aight-utils] Skipping meta response: ${preview.trim()}`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const freshConfig = getPluginConfig(api);
|
|
69
|
+
// Resolve display name from gateway config agent list
|
|
70
|
+
const agentId = ctx.agentId ?? "agent";
|
|
71
|
+
const agents = api.config?.agents?.list ?? [];
|
|
72
|
+
const agent = agents.find((a) => a.id === agentId);
|
|
73
|
+
const displayName = agent?.name ?? agent?.identity?.name ?? agentId;
|
|
74
|
+
// Resolve group chat name for push subtitle (WhatsApp-style layout)
|
|
75
|
+
let pushSubtitle;
|
|
76
|
+
if (sessionKey.includes(":group-chat:")) {
|
|
77
|
+
const groupId = sessionKey.split(":group-chat:")[1];
|
|
78
|
+
if (groupId) {
|
|
79
|
+
const groupName = loadGroupName(api, groupId);
|
|
80
|
+
if (groupName) {
|
|
81
|
+
pushSubtitle = groupName;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const cleanBody = preview.trim().replace(/\n+/g, " ").trim();
|
|
86
|
+
const pushPayload = {
|
|
87
|
+
title: displayName.trim(),
|
|
88
|
+
subtitle: pushSubtitle,
|
|
89
|
+
body: cleanBody,
|
|
90
|
+
data: { sessionKey, agentId },
|
|
91
|
+
};
|
|
92
|
+
for (const device of tokens) {
|
|
93
|
+
if (!device.sendKey)
|
|
94
|
+
continue;
|
|
95
|
+
try {
|
|
96
|
+
const pushResult = await sendPush(device, pushPayload, freshConfig);
|
|
97
|
+
api.logger.info(`[aight-utils] Push sent: session=${sessionKey} device=${device.deviceId} ok=${pushResult.ok}${pushResult.error ? ` error=${pushResult.error}` : ""}`);
|
|
98
|
+
// Auto-prune stale tokens — if the relay rejects the token, remove it
|
|
99
|
+
if (!pushResult.ok && pushResult.error) {
|
|
100
|
+
const errLower = pushResult.error.toLowerCase();
|
|
101
|
+
if (STALE_TOKEN_ERRORS.some((e) => errLower.includes(e))) {
|
|
102
|
+
api.logger.info(`[aight-utils] Pruning stale device token: ${device.deviceId}`);
|
|
103
|
+
unregisterToken(device.deviceId);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
api.logger.warn(`[aight-utils] Push failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
api.logger.error(`[aight-utils] Failed to register push hook: ${err instanceof Error ? err.message : String(err)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=push-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-hook.js","sourceRoot":"","sources":["../../src/push-hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push relay network calls — fetch only, no filesystem access.
|
|
3
|
+
*/
|
|
4
|
+
import type { DeviceToken } from "./push-store.js";
|
|
5
|
+
import type { AightConfig } from "./config.js";
|
|
6
|
+
export declare function obtainSendKey(relayUrl: string, pushToken: string): Promise<string>;
|
|
7
|
+
export interface PushPayload {
|
|
8
|
+
title?: string;
|
|
9
|
+
subtitle?: string;
|
|
10
|
+
body?: string;
|
|
11
|
+
data?: Record<string, unknown>;
|
|
12
|
+
silent?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function sendPush(device: DeviceToken, payload: PushPayload, config: AightConfig): Promise<{
|
|
15
|
+
ok: boolean;
|
|
16
|
+
error?: string;
|
|
17
|
+
}>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push relay network calls — fetch only, no filesystem access.
|
|
3
|
+
*/
|
|
4
|
+
import { DEFAULT_RELAY_URL, DEFAULT_PUSH_MODE } from "./defaults.js";
|
|
5
|
+
// ── Relay Registration (get sendKey) ──
|
|
6
|
+
export async function obtainSendKey(relayUrl, pushToken) {
|
|
7
|
+
const res = await fetch(`${relayUrl}/register`, {
|
|
8
|
+
method: "POST",
|
|
9
|
+
headers: { "Content-Type": "application/json" },
|
|
10
|
+
body: JSON.stringify({ token: pushToken }),
|
|
11
|
+
});
|
|
12
|
+
if (!res.ok) {
|
|
13
|
+
const text = await res.text();
|
|
14
|
+
throw new Error(`Relay /register returned ${res.status}: ${text}`);
|
|
15
|
+
}
|
|
16
|
+
const data = (await res.json());
|
|
17
|
+
if (!data.sendKey)
|
|
18
|
+
throw new Error("Relay did not return a sendKey");
|
|
19
|
+
return data.sendKey;
|
|
20
|
+
}
|
|
21
|
+
export async function sendPush(device, payload, config) {
|
|
22
|
+
if (!device.sendKey) {
|
|
23
|
+
return {
|
|
24
|
+
ok: false,
|
|
25
|
+
error: `No sendKey for device ${device.deviceId} — re-register to obtain one`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const relayUrl = config.push?.relayUrl ?? DEFAULT_RELAY_URL;
|
|
29
|
+
const mode = config.push?.mode ?? DEFAULT_PUSH_MODE;
|
|
30
|
+
const pushBody = {
|
|
31
|
+
token: device.pushToken,
|
|
32
|
+
sendKey: device.sendKey,
|
|
33
|
+
platform: device.platform,
|
|
34
|
+
sandbox: device.sandbox ?? false,
|
|
35
|
+
};
|
|
36
|
+
if (mode === "rich" && !payload.silent) {
|
|
37
|
+
pushBody.title = payload.title;
|
|
38
|
+
if (payload.subtitle)
|
|
39
|
+
pushBody.subtitle = payload.subtitle;
|
|
40
|
+
pushBody.body = payload.body;
|
|
41
|
+
}
|
|
42
|
+
if (payload.data) {
|
|
43
|
+
pushBody.data = payload.data;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const res = await fetch(`${relayUrl}/send`, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: { "Content-Type": "application/json" },
|
|
49
|
+
body: JSON.stringify(pushBody),
|
|
50
|
+
});
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
const text = await res.text();
|
|
53
|
+
return { ok: false, error: `Relay returned ${res.status}: ${text}` };
|
|
54
|
+
}
|
|
55
|
+
return { ok: true };
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=push-net.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-net.js","sourceRoot":"","sources":["../../src/push-net.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push device token persistence — file-based storage only, no network.
|
|
3
|
+
*/
|
|
4
|
+
export interface DeviceToken {
|
|
5
|
+
deviceId: string;
|
|
6
|
+
pushToken: string;
|
|
7
|
+
platform: "ios" | "android";
|
|
8
|
+
sandbox?: boolean;
|
|
9
|
+
sendKey?: string;
|
|
10
|
+
registeredAt: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function loadTokens(): DeviceToken[];
|
|
13
|
+
export declare function saveTokens(tokens: DeviceToken[]): void;
|
|
14
|
+
export declare function registerToken(token: DeviceToken): void;
|
|
15
|
+
export declare function unregisterToken(deviceId: string): boolean;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push device token persistence — file-based storage only, no network.
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import * as os from "node:os";
|
|
7
|
+
// ── Store ──
|
|
8
|
+
const TOKEN_DIR = path.join(os.homedir(), ".openclaw", "aight");
|
|
9
|
+
const TOKEN_FILE = path.join(TOKEN_DIR, "devices.json");
|
|
10
|
+
export function loadTokens() {
|
|
11
|
+
try {
|
|
12
|
+
if (!fs.existsSync(TOKEN_FILE))
|
|
13
|
+
return [];
|
|
14
|
+
const raw = fs.readFileSync(TOKEN_FILE, "utf-8");
|
|
15
|
+
const parsed = JSON.parse(raw);
|
|
16
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function saveTokens(tokens) {
|
|
23
|
+
fs.mkdirSync(TOKEN_DIR, { recursive: true, mode: 0o700 });
|
|
24
|
+
fs.writeFileSync(TOKEN_FILE, JSON.stringify(tokens, null, 2), { encoding: "utf-8", mode: 0o600 });
|
|
25
|
+
}
|
|
26
|
+
export function registerToken(token) {
|
|
27
|
+
const tokens = loadTokens();
|
|
28
|
+
const idx = tokens.findIndex((t) => t.deviceId === token.deviceId);
|
|
29
|
+
if (idx >= 0) {
|
|
30
|
+
tokens[idx] = token;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
tokens.push(token);
|
|
34
|
+
}
|
|
35
|
+
saveTokens(tokens);
|
|
36
|
+
}
|
|
37
|
+
export function unregisterToken(deviceId) {
|
|
38
|
+
const tokens = loadTokens();
|
|
39
|
+
const filtered = tokens.filter((t) => t.deviceId !== deviceId);
|
|
40
|
+
if (filtered.length === tokens.length)
|
|
41
|
+
return false;
|
|
42
|
+
saveTokens(filtered);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=push-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-store.js","sourceRoot":"","sources":["../../src/push-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAa9B,cAAc;AAEd,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAChE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;AAExD,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAqB;IAC9C,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACpG,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAkB;IAC9C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAC;IACnE,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IACD,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IAC/D,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACpD,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC"}
|