pawmode 1.5.0 → 1.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/README.md +15 -3
- package/dist/dashboard-server-B_5-1h9U.js +1201 -0
- package/dist/{dashboard-server-BLUIYHCS.js → dashboard-server-mp29nUYj.js} +1 -1
- package/dist/index.js +3585 -2252
- package/dist/permissions-CoaVX2ZM.js +3 -0
- package/dist/{scheduler-DppXPNqK.js → scheduler-B4_ayyUF.js} +1 -2
- package/dist/{scheduler-DAmd0GzB.js → scheduler-DOWqpB2d.js} +81 -209
- package/dist/{skills-CUY0swcW.js → skills-DwMXaN3R.js} +1 -1
- package/package.json +2 -4
- package/dist/dashboard-server-BiH4JLbM.js +0 -707
- package/dist/permissions-AJXigU7k.js +0 -3
- /package/dist/{permissions-BlGEHCXO.js → permissions-BHOAvP8i.js} +0 -0
- /package/dist/{skills-CMqq9k1-.js → skills-CJ_pyPlv.js} +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { addJob, canRunWithinBudget, getJob, getTodaysCost, installSystemJob, listJobs, parseHumanSchedule, readCostTracker, readScheduleConfig, recordCost, removeJob, removeSystemJob, runJob, toggleJob, writeScheduleConfig } from "./scheduler-
|
|
2
|
-
import "./skills-CMqq9k1-.js";
|
|
1
|
+
import { addJob, canRunWithinBudget, getJob, getTodaysCost, installSystemJob, listJobs, parseHumanSchedule, readCostTracker, readScheduleConfig, recordCost, removeJob, removeSystemJob, runJob, toggleJob, writeScheduleConfig } from "./scheduler-DOWqpB2d.js";
|
|
3
2
|
|
|
4
3
|
export { writeScheduleConfig };
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import { listInstalledSkills } from "./skills-CMqq9k1-.js";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import * as os$2 from "node:os";
|
|
4
|
-
import * as os$1 from "node:os";
|
|
5
2
|
import chalk from "chalk";
|
|
6
3
|
import { execSync } from "node:child_process";
|
|
4
|
+
import * as os$2 from "node:os";
|
|
5
|
+
import * as os$1 from "node:os";
|
|
7
6
|
import * as fs$2 from "node:fs";
|
|
8
7
|
import * as fs$1 from "node:fs";
|
|
9
8
|
import * as path$2 from "node:path";
|
|
10
9
|
import * as path$1 from "node:path";
|
|
11
|
-
import { Bot } from "grammy";
|
|
12
|
-
import { hydrate } from "@grammyjs/hydrate";
|
|
13
|
-
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
14
10
|
import * as crypto from "node:crypto";
|
|
11
|
+
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
15
12
|
|
|
16
13
|
//#region src/core/branding.ts
|
|
17
14
|
const accent = chalk.hex("#b4783c");
|
|
@@ -173,23 +170,48 @@ function showPuppyDisclaimer() {
|
|
|
173
170
|
|
|
174
171
|
//#endregion
|
|
175
172
|
//#region src/core/telegram.ts
|
|
176
|
-
const
|
|
177
|
-
const
|
|
173
|
+
const PLUGIN_SPEC = "telegram@claude-plugins-official";
|
|
174
|
+
const CHANNEL_DIR = path$2.join(os$2.homedir(), ".claude", "channels", "telegram");
|
|
175
|
+
const ENV_FILE = path$2.join(CHANNEL_DIR, ".env");
|
|
176
|
+
const ACCESS_FILE = path$2.join(CHANNEL_DIR, "access.json");
|
|
178
177
|
function writeTelegramConfig(config) {
|
|
179
|
-
fs$2.mkdirSync(
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
fs$2.mkdirSync(CHANNEL_DIR, {
|
|
179
|
+
recursive: true,
|
|
180
|
+
mode: 448
|
|
181
|
+
});
|
|
182
|
+
fs$2.writeFileSync(ENV_FILE, `TELEGRAM_BOT_TOKEN=${config.botToken}\n`, { mode: 384 });
|
|
183
|
+
const access = {
|
|
184
|
+
dmPolicy: "allowlist",
|
|
185
|
+
allowFrom: config.allowedUserIds,
|
|
186
|
+
groups: {},
|
|
187
|
+
pending: {},
|
|
188
|
+
mentionPatterns: ["paw"]
|
|
189
|
+
};
|
|
190
|
+
fs$2.writeFileSync(ACCESS_FILE, JSON.stringify(access, null, 2), { mode: 384 });
|
|
182
191
|
}
|
|
183
192
|
function readTelegramConfig() {
|
|
184
193
|
try {
|
|
185
|
-
const
|
|
186
|
-
|
|
194
|
+
const env = fs$2.readFileSync(ENV_FILE, "utf-8");
|
|
195
|
+
const token = env.match(/TELEGRAM_BOT_TOKEN=(.+)/)?.[1]?.trim();
|
|
196
|
+
if (!token) return null;
|
|
197
|
+
let allowedUserIds = [];
|
|
198
|
+
try {
|
|
199
|
+
const access = JSON.parse(fs$2.readFileSync(ACCESS_FILE, "utf-8"));
|
|
200
|
+
if (Array.isArray(access.allowFrom)) allowedUserIds = access.allowFrom.map(String);
|
|
201
|
+
} catch {}
|
|
202
|
+
return {
|
|
203
|
+
botToken: token,
|
|
204
|
+
allowedUserIds,
|
|
205
|
+
workspaceDir: os$2.homedir(),
|
|
206
|
+
model: "sonnet",
|
|
207
|
+
skills: []
|
|
208
|
+
};
|
|
187
209
|
} catch {
|
|
188
210
|
return null;
|
|
189
211
|
}
|
|
190
212
|
}
|
|
191
213
|
function telegramConfigExists() {
|
|
192
|
-
return fs$2.existsSync(
|
|
214
|
+
return fs$2.existsSync(ENV_FILE);
|
|
193
215
|
}
|
|
194
216
|
async function telegramQuestionnaire() {
|
|
195
217
|
p.log.info(dim("Let's set up your Telegram bot! You'll need:"));
|
|
@@ -224,205 +246,55 @@ async function telegramQuestionnaire() {
|
|
|
224
246
|
skills: []
|
|
225
247
|
};
|
|
226
248
|
}
|
|
227
|
-
|
|
228
|
-
const MODEL_MAP$1 = {
|
|
229
|
-
sonnet: "claude-sonnet-4-5-20250514",
|
|
230
|
-
opus: "claude-opus-4-6",
|
|
231
|
-
haiku: "claude-haiku-4-5-20251001"
|
|
232
|
-
};
|
|
233
|
-
function getModelId(shortName) {
|
|
234
|
-
return MODEL_MAP$1[shortName] || MODEL_MAP$1.sonnet;
|
|
235
|
-
}
|
|
236
|
-
async function startTelegramBot(config) {
|
|
237
|
-
const bot = new Bot(config.botToken);
|
|
238
|
-
bot.use(hydrate());
|
|
239
|
-
const allowedIds = new Set(config.allowedUserIds.map(Number));
|
|
240
|
-
let currentModel = config.model || "sonnet";
|
|
241
|
-
bot.use(async (ctx, next) => {
|
|
242
|
-
if (!ctx.from || !allowedIds.has(ctx.from.id)) {
|
|
243
|
-
await ctx.reply("Woof! I don't know you. Unauthorized. 🐾");
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
await next();
|
|
247
|
-
});
|
|
248
|
-
const installedSkills = listInstalledSkills();
|
|
249
|
-
const skillCommands = installedSkills.filter((id) => id !== "core" && id !== "memory").map((id) => ({
|
|
250
|
-
command: id,
|
|
251
|
-
description: `Use the ${id} skill`
|
|
252
|
-
}));
|
|
253
|
-
const allCommands = [
|
|
254
|
-
{
|
|
255
|
-
command: "start",
|
|
256
|
-
description: "Start the bot"
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
command: "model",
|
|
260
|
-
description: "Switch Claude model (sonnet/opus/haiku)"
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
command: "skills",
|
|
264
|
-
description: "List installed skills"
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
command: "stop",
|
|
268
|
-
description: "Cancel current operation"
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
command: "clear",
|
|
272
|
-
description: "Reset conversation"
|
|
273
|
-
},
|
|
274
|
-
...skillCommands
|
|
275
|
-
];
|
|
249
|
+
function telegramPluginInstalled() {
|
|
276
250
|
try {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
Model: \`${currentModel}\`\nSkills: ${skills.length > 0 ? skills.map((s) => `/${s}`).join(", ") : "none"}\n\nJust send me a message or use a /command!`, { parse_mode: "Markdown" });
|
|
285
|
-
});
|
|
286
|
-
bot.command("model", async (ctx) => {
|
|
287
|
-
const arg = ctx.match?.trim().toLowerCase();
|
|
288
|
-
if (!arg || ![
|
|
289
|
-
"sonnet",
|
|
290
|
-
"opus",
|
|
291
|
-
"haiku"
|
|
292
|
-
].includes(arg)) {
|
|
293
|
-
await ctx.reply(`Current model: \`${currentModel}\`\n\nSwitch with:\n/model sonnet\n/model opus\n/model haiku`, { parse_mode: "Markdown" });
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
currentModel = arg;
|
|
297
|
-
config.model = arg;
|
|
298
|
-
writeTelegramConfig(config);
|
|
299
|
-
await ctx.reply(`Model switched to \`${currentModel}\` 🐾`, { parse_mode: "Markdown" });
|
|
300
|
-
});
|
|
301
|
-
bot.command("skills", async (ctx) => {
|
|
302
|
-
const skills = installedSkills.filter((id) => id !== "core" && id !== "memory");
|
|
303
|
-
if (skills.length === 0) {
|
|
304
|
-
await ctx.reply("No skills installed yet. Run `openpaw setup` first! 🐾");
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
const list = skills.map((s) => `• /${s}`).join("\n");
|
|
308
|
-
await ctx.reply(`*Installed skills:*\n\n${list}`, { parse_mode: "Markdown" });
|
|
309
|
-
});
|
|
310
|
-
bot.command("stop", async (ctx) => {
|
|
311
|
-
const userId = ctx.from.id;
|
|
312
|
-
const session = sessions.get(userId);
|
|
313
|
-
if (session?.controller) {
|
|
314
|
-
session.controller.abort();
|
|
315
|
-
sessions.delete(userId);
|
|
316
|
-
await ctx.reply("Operation cancelled. 🐾");
|
|
317
|
-
} else await ctx.reply("Nothing running right now. 🐾");
|
|
318
|
-
});
|
|
319
|
-
bot.command("clear", async (ctx) => {
|
|
320
|
-
const userId = ctx.from.id;
|
|
321
|
-
sessions.delete(userId);
|
|
322
|
-
await ctx.reply("Conversation cleared! Fresh start. 🐾");
|
|
323
|
-
});
|
|
324
|
-
for (const skillId of installedSkills) {
|
|
325
|
-
if (skillId === "core" || skillId === "memory") continue;
|
|
326
|
-
bot.command(skillId, async (ctx) => {
|
|
327
|
-
const args = ctx.match || "";
|
|
328
|
-
const prompt = args ? `Use the c-${skillId} skill: ${args}` : `What can the c-${skillId} skill do? Give a brief overview.`;
|
|
329
|
-
await handleClaudeMessage(ctx, prompt, currentModel, config);
|
|
251
|
+
const out = execSync("claude plugin list", {
|
|
252
|
+
encoding: "utf-8",
|
|
253
|
+
stdio: [
|
|
254
|
+
"ignore",
|
|
255
|
+
"pipe",
|
|
256
|
+
"ignore"
|
|
257
|
+
]
|
|
330
258
|
});
|
|
259
|
+
if (out.includes("telegram@claude-plugins-official")) return true;
|
|
260
|
+
} catch {}
|
|
261
|
+
return fs$2.existsSync(path$2.join(os$2.homedir(), ".claude", "plugins", "cache", "claude-plugins-official", "telegram"));
|
|
262
|
+
}
|
|
263
|
+
/** Install the official Telegram plugin via the Claude Code CLI. */
|
|
264
|
+
function installTelegramPlugin() {
|
|
265
|
+
try {
|
|
266
|
+
execSync(`claude plugin install ${PLUGIN_SPEC}`, { stdio: "ignore" });
|
|
267
|
+
return telegramPluginInstalled();
|
|
268
|
+
} catch {
|
|
269
|
+
return false;
|
|
331
270
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
bot.catch((err) => {
|
|
336
|
-
console.error("Bot error:", err.message || err);
|
|
337
|
-
});
|
|
338
|
-
process.on("SIGINT", () => {
|
|
339
|
-
console.log("\nShutting down gracefully... 🐾");
|
|
340
|
-
bot.stop();
|
|
341
|
-
process.exit(0);
|
|
342
|
-
});
|
|
343
|
-
process.on("SIGTERM", () => {
|
|
344
|
-
bot.stop();
|
|
345
|
-
process.exit(0);
|
|
346
|
-
});
|
|
347
|
-
console.log("");
|
|
348
|
-
console.log(` 🐾 ${bold("OpenPaw Telegram Bridge")}`);
|
|
349
|
-
console.log(` Model: ${accent(currentModel)}`);
|
|
350
|
-
console.log(` Skills: ${accent(String(installedSkills.length))}`);
|
|
351
|
-
console.log(` Workspace: ${dim(config.workspaceDir)}`);
|
|
352
|
-
console.log(` Allowed users: ${dim(config.allowedUserIds.join(", "))}`);
|
|
353
|
-
console.log("");
|
|
354
|
-
console.log(dim(" Listening for messages... (Ctrl+C to stop)"));
|
|
355
|
-
console.log("");
|
|
356
|
-
await bot.start();
|
|
357
|
-
}
|
|
358
|
-
async function handleClaudeMessage(ctx, prompt, model, config) {
|
|
359
|
-
const userId = ctx.from.id;
|
|
360
|
-
const existing = sessions.get(userId);
|
|
361
|
-
if (existing?.controller) existing.controller.abort();
|
|
362
|
-
const controller = new AbortController();
|
|
363
|
-
const session = sessions.get(userId) || {};
|
|
364
|
-
session.controller = controller;
|
|
365
|
-
sessions.set(userId, session);
|
|
366
|
-
const statusMsg = await ctx.reply("Thinking... 🐾");
|
|
367
|
-
let fullText = "";
|
|
368
|
-
let lastEditTime = 0;
|
|
369
|
-
const EDIT_INTERVAL = 1500;
|
|
271
|
+
}
|
|
272
|
+
/** Enable the plugin (idempotent; ignored if already enabled). */
|
|
273
|
+
function enableTelegramPlugin() {
|
|
370
274
|
try {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const text = msgContent.content.filter((block) => block.type === "text").map((block) => block.text || "").join("");
|
|
389
|
-
if (text) {
|
|
390
|
-
fullText = text;
|
|
391
|
-
const now = Date.now();
|
|
392
|
-
if (now - lastEditTime > EDIT_INTERVAL) {
|
|
393
|
-
lastEditTime = now;
|
|
394
|
-
const truncated = fullText.length > 4e3 ? `${fullText.slice(0, 4e3)}...` : fullText;
|
|
395
|
-
try {
|
|
396
|
-
await statusMsg.editText(truncated);
|
|
397
|
-
} catch {}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
if (message.type === "result") {
|
|
402
|
-
const result = message.result;
|
|
403
|
-
if (result) fullText = result;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
if (fullText) {
|
|
407
|
-
const truncated = fullText.length > 4e3 ? `${fullText.slice(0, 4e3)}...` : fullText;
|
|
408
|
-
try {
|
|
409
|
-
await statusMsg.editText(truncated);
|
|
410
|
-
} catch {
|
|
411
|
-
await ctx.reply(truncated);
|
|
412
|
-
}
|
|
413
|
-
} else await statusMsg.editText("Done! (no text output) 🐾");
|
|
414
|
-
} catch (err) {
|
|
415
|
-
const errorMsg = err instanceof Error ? err.message : "Unknown error";
|
|
416
|
-
if (errorMsg.includes("abort") || controller.signal.aborted) return;
|
|
417
|
-
try {
|
|
418
|
-
await statusMsg.editText(`Woof, something went wrong: ${errorMsg.slice(0, 200)} 🐾`);
|
|
419
|
-
} catch {
|
|
420
|
-
await ctx.reply(`Woof, something went wrong: ${errorMsg.slice(0, 200)} 🐾`);
|
|
421
|
-
}
|
|
422
|
-
} finally {
|
|
423
|
-
session.controller = void 0;
|
|
424
|
-
sessions.set(userId, session);
|
|
275
|
+
execSync(`claude plugin enable ${PLUGIN_SPEC}`, { stdio: "ignore" });
|
|
276
|
+
} catch {}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Ensure the official plugin is installed, configured, and enabled.
|
|
280
|
+
* Returns a human-readable status for the wizard/CLI to print.
|
|
281
|
+
*/
|
|
282
|
+
function ensureTelegramReady() {
|
|
283
|
+
if (!telegramConfigExists()) return {
|
|
284
|
+
ok: false,
|
|
285
|
+
message: "No Telegram config yet — run `openpaw telegram setup`."
|
|
286
|
+
};
|
|
287
|
+
if (!telegramPluginInstalled()) {
|
|
288
|
+
if (!installTelegramPlugin()) return {
|
|
289
|
+
ok: false,
|
|
290
|
+
message: `Couldn't auto-install the plugin. Install it once with:\n claude plugin install ${PLUGIN_SPEC}`
|
|
291
|
+
};
|
|
425
292
|
}
|
|
293
|
+
enableTelegramPlugin();
|
|
294
|
+
return {
|
|
295
|
+
ok: true,
|
|
296
|
+
message: "Telegram bridge ready — it runs inside Claude Code."
|
|
297
|
+
};
|
|
426
298
|
}
|
|
427
299
|
|
|
428
300
|
//#endregion
|
|
@@ -885,4 +757,4 @@ async function runJob(jobId) {
|
|
|
885
757
|
}
|
|
886
758
|
|
|
887
759
|
//#endregion
|
|
888
|
-
export { accent, addJob, bold, canRunWithinBudget, dim, getJob, getTodaysCost, installSystemJob, listJobs, parseHumanSchedule, pawPulse, pawStep, readCostTracker, readScheduleConfig, readTelegramConfig, recordCost, removeJob, removeSystemJob, runJob, showBanner, showMini, showPuppyDisclaimer,
|
|
760
|
+
export { accent, addJob, bold, canRunWithinBudget, dim, ensureTelegramReady, getJob, getTodaysCost, installSystemJob, listJobs, parseHumanSchedule, pawPulse, pawStep, readCostTracker, readScheduleConfig, readTelegramConfig, recordCost, removeJob, removeSystemJob, runJob, showBanner, showMini, showPuppyDisclaimer, subtle, telegramConfigExists, telegramQuestionnaire, toggleJob, writeScheduleConfig, writeTelegramConfig };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { getDefaultSkillsDir, getInstalledSkillPath, getSkillTemplatePath, installSkill, isSkillInstalled, listInstalledSkills, removeSkill } from "./skills-
|
|
1
|
+
import { getDefaultSkillsDir, getInstalledSkillPath, getSkillTemplatePath, installSkill, isSkillInstalled, listInstalledSkills, removeSkill } from "./skills-CJ_pyPlv.js";
|
|
2
2
|
|
|
3
3
|
export { installSkill };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pawmode",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Open-source Personal Assistant Wizard for Claude Code. Turn Claude Code into your PA with CLI tools and skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,11 +43,9 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@anthropic-ai/claude-agent-sdk": "^0.2.63",
|
|
45
45
|
"@clack/prompts": "^0.10.0",
|
|
46
|
-
"@grammyjs/hydrate": "^1.6.0",
|
|
47
46
|
"chalk": "^5.4.1",
|
|
48
47
|
"commander": "^14.0.0",
|
|
49
|
-
"gradient-string": "^3.0.0"
|
|
50
|
-
"grammy": "^1.40.1"
|
|
48
|
+
"gradient-string": "^3.0.0"
|
|
51
49
|
},
|
|
52
50
|
"devDependencies": {
|
|
53
51
|
"@biomejs/biome": "^1.9.0",
|