@inetafrica/open-claudia 2.1.1 → 2.2.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/core/actions.js +100 -2
- package/core/handlers.js +24 -7
- package/package.json +1 -1
package/core/actions.js
CHANGED
|
@@ -10,6 +10,9 @@ const { currentState, resetSettings, resetSessionUsage, saveState } = require(".
|
|
|
10
10
|
const { runClaude, getActiveSessionId } = require("./runner");
|
|
11
11
|
const { listProjects, projectKeyboard, workspacePath } = require("./projects");
|
|
12
12
|
const { isChatOwner, approveAuthRequest, denyAuthRequest, authRequestLabel } = require("./access");
|
|
13
|
+
const accessStore = require("./access");
|
|
14
|
+
const peopleStore = require("./people");
|
|
15
|
+
const { vault } = require("./vault-store");
|
|
13
16
|
const intros = require("./intros");
|
|
14
17
|
const introFlow = require("./intro-flow");
|
|
15
18
|
const { finishOnboarding } = require("./onboarding");
|
|
@@ -27,8 +30,53 @@ async function handleAction(envelope) {
|
|
|
27
30
|
const state = currentState();
|
|
28
31
|
|
|
29
32
|
if (d.startsWith("auth:")) {
|
|
30
|
-
if (!isChatOwner(envelope.channelId)) return send("Owner only — auth
|
|
31
|
-
const
|
|
33
|
+
if (!isChatOwner(envelope.channelId)) return send("Owner only — auth controls are restricted.");
|
|
34
|
+
const parts = d.split(":");
|
|
35
|
+
const action = parts[1];
|
|
36
|
+
|
|
37
|
+
if (action === "list") {
|
|
38
|
+
const raw = accessStore.listAuthorizedRaw();
|
|
39
|
+
const lines = ["Authorized chats:"];
|
|
40
|
+
for (const a of raw.authorized) {
|
|
41
|
+
const person = peopleStore.findByHandle("telegram", a.chatId) || peopleStore.findByHandle("kazee", a.chatId);
|
|
42
|
+
lines.push(` ${a.chatId}${a.isOwner ? " (owner)" : ""} ${a.name || a.username || ""}${person ? " → " + person.name : " → (no person)"}`);
|
|
43
|
+
}
|
|
44
|
+
if (raw.authorized.length === 0) lines.push(" (none)");
|
|
45
|
+
if (raw.pending.length > 0) {
|
|
46
|
+
lines.push("", "Pending:");
|
|
47
|
+
for (const p of raw.pending) lines.push(` ${p.chatId} ${p.name || p.username || ""}`);
|
|
48
|
+
}
|
|
49
|
+
return send(lines.join("\n"));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (action === "revoke-pick") {
|
|
53
|
+
const raw = accessStore.listAuthorizedRaw();
|
|
54
|
+
const rows = [];
|
|
55
|
+
for (const a of raw.authorized) {
|
|
56
|
+
if (a.isOwner) continue;
|
|
57
|
+
const person = peopleStore.findByHandle("telegram", a.chatId) || peopleStore.findByHandle("kazee", a.chatId);
|
|
58
|
+
const label = `${a.chatId} ${person ? person.name : (a.name || a.username || "")}`.trim();
|
|
59
|
+
rows.push([{ text: `Revoke ${label}`.slice(0, 60), callback_data: `auth:revoke:${a.chatId}` }]);
|
|
60
|
+
}
|
|
61
|
+
if (rows.length === 0) return send("No revocable chats (only owner is authorized).");
|
|
62
|
+
return send("Pick a chat to revoke:", { keyboard: { inline_keyboard: rows } });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (action === "revoke") {
|
|
66
|
+
const chatId = parts.slice(2).join(":");
|
|
67
|
+
if (!chatId) return;
|
|
68
|
+
const person = peopleStore.findByHandle("telegram", chatId) || peopleStore.findByHandle("kazee", chatId);
|
|
69
|
+
if (person && person.isOwner) return send("Refusing to revoke owner chat.");
|
|
70
|
+
if (person) {
|
|
71
|
+
const detected = (person.handles || []).find((h) => String(h.channelId) === String(chatId));
|
|
72
|
+
if (detected) peopleStore.unlinkHandle(person.id, { adapter: detected.adapter, channelId: detected.channelId });
|
|
73
|
+
}
|
|
74
|
+
const r = accessStore.revokeChat(chatId);
|
|
75
|
+
if (!r.ok) return send(`Revoke failed: ${r.reason}`);
|
|
76
|
+
return send(`Revoked ${chatId}${person ? ` (was ${person.name})` : ""}.`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const chatId = parts[2];
|
|
32
80
|
if (!chatId || !["approve", "deny"].includes(action)) return;
|
|
33
81
|
const result = action === "approve" ? approveAuthRequest(chatId) : denyAuthRequest(chatId);
|
|
34
82
|
|
|
@@ -54,6 +102,56 @@ async function handleAction(envelope) {
|
|
|
54
102
|
return;
|
|
55
103
|
}
|
|
56
104
|
|
|
105
|
+
if (d.startsWith("vault:")) {
|
|
106
|
+
const action = d.slice("vault:".length);
|
|
107
|
+
if (action === "lock") {
|
|
108
|
+
vault.lock();
|
|
109
|
+
return send("Vault locked.");
|
|
110
|
+
}
|
|
111
|
+
if (action === "hint") {
|
|
112
|
+
return send([
|
|
113
|
+
"Vault usage:",
|
|
114
|
+
" /vault set <name> <value> — save a credential (your message is deleted)",
|
|
115
|
+
" /vault remove <name> — delete a credential",
|
|
116
|
+
" /vault — list (unlocks if needed)",
|
|
117
|
+
" /vault lock — lock now",
|
|
118
|
+
].join("\n"));
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (d.startsWith("people:")) {
|
|
124
|
+
const rest = d.slice("people:".length);
|
|
125
|
+
if (rest === "hint") {
|
|
126
|
+
return send([
|
|
127
|
+
"People usage:",
|
|
128
|
+
" /people show <name> — details",
|
|
129
|
+
" /people note <name> <text> — add a note",
|
|
130
|
+
" /people add <name> — owner: add person",
|
|
131
|
+
" /people link <name> <adapter> <channel> — owner: link a chat",
|
|
132
|
+
" /people unlink <name> <adapter> <channel> — owner: unlink",
|
|
133
|
+
" /people set-primary <name> <adapter> <channel>",
|
|
134
|
+
" /people remove <name> — owner",
|
|
135
|
+
].join("\n"));
|
|
136
|
+
}
|
|
137
|
+
if (rest.startsWith("show:")) {
|
|
138
|
+
const id = rest.slice("show:".length);
|
|
139
|
+
const person = peopleStore.findById(id);
|
|
140
|
+
if (!person) return send(`Not found: ${id}`);
|
|
141
|
+
const handles = (person.handles || []).map((h) => ` ${h.adapter}:${h.channelId}${person.primaryChannel && person.primaryChannel.adapter === h.adapter && String(person.primaryChannel.channelId) === String(h.channelId) ? " (primary)" : ""}`).join("\n");
|
|
142
|
+
const notes = (person.notes || []).slice(-5).map((n) => ` - [${(n.at || "").slice(0, 10)}] ${n.text}`).join("\n");
|
|
143
|
+
return send([
|
|
144
|
+
`${person.name}${person.isOwner ? " (owner)" : ""} id=${person.id}`,
|
|
145
|
+
person.bio ? `Bio: ${person.bio}` : null,
|
|
146
|
+
"Handles:",
|
|
147
|
+
handles || " (none)",
|
|
148
|
+
notes ? "Notes:" : null,
|
|
149
|
+
notes || null,
|
|
150
|
+
].filter((x) => x !== null && x !== "").join("\n"));
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
57
155
|
if (d.startsWith("intro:")) {
|
|
58
156
|
if (!isChatOwner(envelope.channelId)) return send("Owner only — intro approvals are restricted.");
|
|
59
157
|
const [, action, introId] = d.split(":");
|
package/core/handlers.js
CHANGED
|
@@ -170,8 +170,15 @@ register({
|
|
|
170
170
|
return send("Usage: /auth | /auth list | /auth revoke <chatId>");
|
|
171
171
|
}
|
|
172
172
|
if (authorized(env)) {
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
if (ownerEnv(env)) {
|
|
174
|
+
return send("You're already authorized.", {
|
|
175
|
+
keyboard: { inline_keyboard: [[
|
|
176
|
+
{ text: "List authorized", callback_data: "auth:list" },
|
|
177
|
+
{ text: "Revoke…", callback_data: "auth:revoke-pick" },
|
|
178
|
+
]] },
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return send("You're already authorized.");
|
|
175
182
|
}
|
|
176
183
|
if (!hasOwner()) {
|
|
177
184
|
bootstrapOwner({
|
|
@@ -881,8 +888,12 @@ register({
|
|
|
881
888
|
if (vault.isUnlocked()) {
|
|
882
889
|
const entries = vault.list();
|
|
883
890
|
const keys = Object.keys(entries);
|
|
884
|
-
|
|
885
|
-
|
|
891
|
+
const kb = { inline_keyboard: [[
|
|
892
|
+
{ text: "Lock now", callback_data: "vault:lock" },
|
|
893
|
+
{ text: "How to set/remove", callback_data: "vault:hint" },
|
|
894
|
+
]] };
|
|
895
|
+
if (keys.length === 0) await send("Vault is unlocked but empty.\n\nUse /vault set <name> <value>", { keyboard: kb });
|
|
896
|
+
else await send("Vault (unlocked):\n\n" + keys.map((k) => `${k}: ${entries[k]}`).join("\n") + "\n\nLocks automatically in 5 min.", { keyboard: kb });
|
|
886
897
|
} else {
|
|
887
898
|
state.pendingVaultUnlock = true;
|
|
888
899
|
state.pendingVaultAction = { type: "list" };
|
|
@@ -1052,11 +1063,17 @@ register({
|
|
|
1052
1063
|
if (!authorized(env)) return;
|
|
1053
1064
|
if (!tail || tail === "list") {
|
|
1054
1065
|
const all = peopleStore.list();
|
|
1055
|
-
if (all.length === 0)
|
|
1066
|
+
if (all.length === 0) {
|
|
1067
|
+
return send("No people yet.", ownerEnv(env) ? {
|
|
1068
|
+
keyboard: { inline_keyboard: [[{ text: "How to add/link", callback_data: "people:hint" }]] },
|
|
1069
|
+
} : undefined);
|
|
1070
|
+
}
|
|
1056
1071
|
const lines = ["Team:"];
|
|
1057
1072
|
for (const p of all) lines.push(` ${formatPersonShort(p)}`);
|
|
1058
|
-
|
|
1059
|
-
|
|
1073
|
+
const rows = [];
|
|
1074
|
+
for (const p of all) rows.push([{ text: `Show ${p.name}`, callback_data: `people:show:${p.id}` }]);
|
|
1075
|
+
if (ownerEnv(env)) rows.push([{ text: "How to note/link/add", callback_data: "people:hint" }]);
|
|
1076
|
+
return send(lines.join("\n"), { keyboard: { inline_keyboard: rows } });
|
|
1060
1077
|
}
|
|
1061
1078
|
const showMatch = tail.match(/^show\s+(.+)$/i);
|
|
1062
1079
|
if (showMatch) {
|
package/package.json
CHANGED