openclaw-remote 0.2.1 → 0.3.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 +265 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2746,6 +2746,52 @@ async function getHeartbeat(account) {
|
|
|
2746
2746
|
}
|
|
2747
2747
|
);
|
|
2748
2748
|
}
|
|
2749
|
+
async function listRoles(account) {
|
|
2750
|
+
return apiFetch(
|
|
2751
|
+
buildUrl(account, "/api/v1/roles"),
|
|
2752
|
+
{
|
|
2753
|
+
method: "GET",
|
|
2754
|
+
headers: buildHeaders(account)
|
|
2755
|
+
}
|
|
2756
|
+
);
|
|
2757
|
+
}
|
|
2758
|
+
async function listEpics(account) {
|
|
2759
|
+
return apiFetch(
|
|
2760
|
+
buildUrl(account, "/api/v1/epics"),
|
|
2761
|
+
{
|
|
2762
|
+
method: "GET",
|
|
2763
|
+
headers: buildHeaders(account)
|
|
2764
|
+
}
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2767
|
+
async function createEpic(account, epic) {
|
|
2768
|
+
return apiFetch(
|
|
2769
|
+
buildUrl(account, "/api/v1/epics"),
|
|
2770
|
+
{
|
|
2771
|
+
method: "POST",
|
|
2772
|
+
headers: buildHeaders(account),
|
|
2773
|
+
body: JSON.stringify(epic)
|
|
2774
|
+
}
|
|
2775
|
+
);
|
|
2776
|
+
}
|
|
2777
|
+
async function getBoardHealth(account) {
|
|
2778
|
+
return apiFetch(
|
|
2779
|
+
buildUrl(account, "/api/v1/board/health"),
|
|
2780
|
+
{
|
|
2781
|
+
method: "GET",
|
|
2782
|
+
headers: buildHeaders(account)
|
|
2783
|
+
}
|
|
2784
|
+
);
|
|
2785
|
+
}
|
|
2786
|
+
async function getTeam(account) {
|
|
2787
|
+
return apiFetch(
|
|
2788
|
+
buildUrl(account, "/api/v1/team"),
|
|
2789
|
+
{
|
|
2790
|
+
method: "GET",
|
|
2791
|
+
headers: buildHeaders(account)
|
|
2792
|
+
}
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2749
2795
|
|
|
2750
2796
|
// src/realtime-listener.ts
|
|
2751
2797
|
import { createClient } from "@supabase/supabase-js";
|
|
@@ -3205,6 +3251,9 @@ function createRemotePlugin() {
|
|
|
3205
3251
|
}),
|
|
3206
3252
|
assigned_role_id: Type.Optional(
|
|
3207
3253
|
Type.String({ description: "Role ID to assign the task to" })
|
|
3254
|
+
),
|
|
3255
|
+
epic_id: Type.Optional(
|
|
3256
|
+
Type.String({ description: "Epic ID to group the task under" })
|
|
3208
3257
|
)
|
|
3209
3258
|
}),
|
|
3210
3259
|
execute: async (_toolCallId, args) => {
|
|
@@ -3214,7 +3263,8 @@ function createRemotePlugin() {
|
|
|
3214
3263
|
description: args.description,
|
|
3215
3264
|
type: args.type,
|
|
3216
3265
|
priority: args.priority,
|
|
3217
|
-
assigned_role_id: args.assigned_role_id
|
|
3266
|
+
assigned_role_id: args.assigned_role_id,
|
|
3267
|
+
epic_id: args.epic_id
|
|
3218
3268
|
});
|
|
3219
3269
|
if (!result.ok) {
|
|
3220
3270
|
return {
|
|
@@ -3241,7 +3291,7 @@ function createRemotePlugin() {
|
|
|
3241
3291
|
{
|
|
3242
3292
|
name: "remote_update_task",
|
|
3243
3293
|
label: "Update a task on the Remote project board",
|
|
3244
|
-
description: "Update an existing task on the Remote project board. Specify the task_id and any fields to change: status (todo/in_progress/review/done), priority, assigned_to, title, or
|
|
3294
|
+
description: "Update an existing task on the Remote project board. Specify the task_id and any fields to change: status (todo/in_progress/review/done), priority, assigned_to, title, description, type, epic_id, or assigned_role_id.",
|
|
3245
3295
|
parameters: Type.Object({
|
|
3246
3296
|
task_id: Type.String({ description: "ID of the task to update" }),
|
|
3247
3297
|
status: optionalStringEnum(["todo", "in_progress", "review", "done"], {
|
|
@@ -3254,7 +3304,16 @@ function createRemotePlugin() {
|
|
|
3254
3304
|
Type.String({ description: "User or role to assign the task to" })
|
|
3255
3305
|
),
|
|
3256
3306
|
title: Type.Optional(Type.String({ description: "New task title" })),
|
|
3257
|
-
description: Type.Optional(Type.String({ description: "New task description" }))
|
|
3307
|
+
description: Type.Optional(Type.String({ description: "New task description" })),
|
|
3308
|
+
type: optionalStringEnum(["feature", "task", "bug"], {
|
|
3309
|
+
description: "New type: feature, task, or bug"
|
|
3310
|
+
}),
|
|
3311
|
+
epic_id: Type.Optional(
|
|
3312
|
+
Type.String({ description: "Epic ID to group the task under (or null to remove)" })
|
|
3313
|
+
),
|
|
3314
|
+
assigned_role_id: Type.Optional(
|
|
3315
|
+
Type.String({ description: "Role ID to assign the task to" })
|
|
3316
|
+
)
|
|
3258
3317
|
}),
|
|
3259
3318
|
execute: async (_toolCallId, args) => {
|
|
3260
3319
|
const account = resolveAccount(cfg ?? {});
|
|
@@ -3344,27 +3403,205 @@ function createRemotePlugin() {
|
|
|
3344
3403
|
parameters: Type.Object({}),
|
|
3345
3404
|
execute: async (_toolCallId, _args) => {
|
|
3346
3405
|
const account = resolveAccount(cfg ?? {});
|
|
3347
|
-
const result = await
|
|
3406
|
+
const result = await getBoardHealth(account);
|
|
3348
3407
|
if (!result.ok) {
|
|
3408
|
+
const legacyResult = await getHeartbeat(account);
|
|
3409
|
+
if (!legacyResult.ok) {
|
|
3410
|
+
return {
|
|
3411
|
+
content: [{ type: "text", text: `\u274C Failed to get board health: ${result.error}` }],
|
|
3412
|
+
details: { ok: false, error: result.error }
|
|
3413
|
+
};
|
|
3414
|
+
}
|
|
3415
|
+
const h2 = legacyResult.data;
|
|
3416
|
+
const text = [
|
|
3417
|
+
`\u{1F4CA} **Board Health Summary** (legacy)`,
|
|
3418
|
+
"",
|
|
3419
|
+
`- **Pending tasks**: ${h2.pending_tasks}`,
|
|
3420
|
+
`- **In progress**: ${h2.in_progress_tasks}`,
|
|
3421
|
+
`- **Unassigned**: ${h2.unassigned_tasks}`,
|
|
3422
|
+
`- **Activity (24h)**: ${h2.recent_activity_24h}`,
|
|
3423
|
+
`- **My roles**: ${h2.my_roles?.join(", ") || "none"}`
|
|
3424
|
+
].join("\n");
|
|
3349
3425
|
return {
|
|
3350
|
-
content: [{ type: "text", text
|
|
3351
|
-
details: { ok:
|
|
3426
|
+
content: [{ type: "text", text }],
|
|
3427
|
+
details: { ok: true, heartbeat: h2 }
|
|
3352
3428
|
};
|
|
3353
3429
|
}
|
|
3354
3430
|
const h = result.data;
|
|
3355
|
-
const
|
|
3356
|
-
|
|
3431
|
+
const s = h.stats;
|
|
3432
|
+
const lines = [
|
|
3433
|
+
`\u{1F4CA} **Board Health \u2014 ${h.project.name}**`,
|
|
3357
3434
|
"",
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3435
|
+
`**Tasks** (${s.total_tasks} total):`,
|
|
3436
|
+
` todo: ${s.by_status.todo} | in_progress: ${s.by_status.in_progress} | review: ${s.by_status.review} | done: ${s.by_status.done}`,
|
|
3437
|
+
` unassigned: ${s.unassigned}`,
|
|
3438
|
+
"",
|
|
3439
|
+
`**By priority**: urgent: ${s.by_priority.urgent} | high: ${s.by_priority.high} | medium: ${s.by_priority.medium} | low: ${s.by_priority.low}`
|
|
3440
|
+
];
|
|
3441
|
+
if (h.epics.length > 0) {
|
|
3442
|
+
lines.push("", "**Epics**:");
|
|
3443
|
+
for (const e of h.epics) {
|
|
3444
|
+
lines.push(` - ${e.name} (${e.task_count} tasks)`);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
if (h.roles.length > 0) {
|
|
3448
|
+
lines.push("", "**Roles**:");
|
|
3449
|
+
for (const r of h.roles) {
|
|
3450
|
+
const assignee = r.assigned_to ? `${r.assigned_to.type}: ${r.assigned_to.name}` : "vacant";
|
|
3451
|
+
lines.push(` - ${r.name} [${r.category}] \u2192 ${assignee}`);
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
if (h.recent_activity.length > 0) {
|
|
3455
|
+
lines.push("", `**Recent activity** (last ${h.recent_activity.length}):`);
|
|
3456
|
+
for (const a of h.recent_activity.slice(0, 5)) {
|
|
3457
|
+
const meta = a.metadata;
|
|
3458
|
+
lines.push(` - ${a.action}: ${meta?.title || a.target_id} (${a.actor_type})`);
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
return {
|
|
3462
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
3463
|
+
details: { ok: true, health: h }
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3466
|
+
},
|
|
3467
|
+
// 5. remote_list_roles
|
|
3468
|
+
{
|
|
3469
|
+
name: "remote_list_roles",
|
|
3470
|
+
label: "List project roles",
|
|
3471
|
+
description: "List project roles with assignment info. Use to find valid assigned_role_id values for task creation.",
|
|
3472
|
+
parameters: Type.Object({}),
|
|
3473
|
+
execute: async (_toolCallId, _args) => {
|
|
3474
|
+
const account = resolveAccount(cfg ?? {});
|
|
3475
|
+
const result = await listRoles(account);
|
|
3476
|
+
if (!result.ok) {
|
|
3477
|
+
return {
|
|
3478
|
+
content: [{ type: "text", text: `\u274C Failed to list roles: ${result.error}` }],
|
|
3479
|
+
details: { ok: false, error: result.error }
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
const roles = result.data.roles;
|
|
3483
|
+
if (roles.length === 0) {
|
|
3484
|
+
return {
|
|
3485
|
+
content: [{ type: "text", text: "\u{1F4CB} No roles configured for this project." }],
|
|
3486
|
+
details: { ok: true, roles: [] }
|
|
3487
|
+
};
|
|
3488
|
+
}
|
|
3489
|
+
const lines = [`\u{1F465} **${roles.length} role(s):**`, ""];
|
|
3490
|
+
for (const r of roles) {
|
|
3491
|
+
const assignee = r.assigned_to ? `${r.assigned_to.type}: ${r.assigned_to.name}` : "\u{1F534} vacant";
|
|
3492
|
+
const mine = r.is_mine ? " \u2B50" : "";
|
|
3493
|
+
lines.push(`- **${r.name}** [${r.category}] \u2192 ${assignee}${mine} (id: ${r.id})`);
|
|
3494
|
+
}
|
|
3495
|
+
return {
|
|
3496
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
3497
|
+
details: { ok: true, roles }
|
|
3498
|
+
};
|
|
3499
|
+
}
|
|
3500
|
+
},
|
|
3501
|
+
// 6. remote_list_epics
|
|
3502
|
+
{
|
|
3503
|
+
name: "remote_list_epics",
|
|
3504
|
+
label: "List epics",
|
|
3505
|
+
description: "List epics for the project. Use to find valid epic_id values for task grouping.",
|
|
3506
|
+
parameters: Type.Object({}),
|
|
3507
|
+
execute: async (_toolCallId, _args) => {
|
|
3508
|
+
const account = resolveAccount(cfg ?? {});
|
|
3509
|
+
const result = await listEpics(account);
|
|
3510
|
+
if (!result.ok) {
|
|
3511
|
+
return {
|
|
3512
|
+
content: [{ type: "text", text: `\u274C Failed to list epics: ${result.error}` }],
|
|
3513
|
+
details: { ok: false, error: result.error }
|
|
3514
|
+
};
|
|
3515
|
+
}
|
|
3516
|
+
const epics = result.data.epics;
|
|
3517
|
+
if (epics.length === 0) {
|
|
3518
|
+
return {
|
|
3519
|
+
content: [{ type: "text", text: "\u{1F4CB} No epics found. Use `remote_create_epic` to create one." }],
|
|
3520
|
+
details: { ok: true, epics: [] }
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
const lines = [`\u{1F4E6} **${epics.length} epic(s):**`, ""];
|
|
3524
|
+
for (const e of epics) {
|
|
3525
|
+
lines.push(`- **${e.name}** (${e.task_count ?? 0} tasks) (id: ${e.id})`);
|
|
3526
|
+
if (e.description) {
|
|
3527
|
+
const desc = e.description.length > 80 ? e.description.slice(0, 80) + "\u2026" : e.description;
|
|
3528
|
+
lines.push(` ${desc}`);
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
return {
|
|
3532
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
3533
|
+
details: { ok: true, epics }
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
3536
|
+
},
|
|
3537
|
+
// 7. remote_create_epic
|
|
3538
|
+
{
|
|
3539
|
+
name: "remote_create_epic",
|
|
3540
|
+
label: "Create an epic",
|
|
3541
|
+
description: "Create a new epic on the Remote project board. Specify name, and optionally description and color.",
|
|
3542
|
+
parameters: Type.Object({
|
|
3543
|
+
name: Type.String({ description: "Epic name" }),
|
|
3544
|
+
description: Type.Optional(Type.String({ description: "Epic description" })),
|
|
3545
|
+
color: Type.Optional(Type.String({ description: "Epic color (hex, e.g. #6366f1)" }))
|
|
3546
|
+
}),
|
|
3547
|
+
execute: async (_toolCallId, args) => {
|
|
3548
|
+
const account = resolveAccount(cfg ?? {});
|
|
3549
|
+
const result = await createEpic(account, {
|
|
3550
|
+
name: args.name,
|
|
3551
|
+
description: args.description,
|
|
3552
|
+
color: args.color
|
|
3553
|
+
});
|
|
3554
|
+
if (!result.ok) {
|
|
3555
|
+
return {
|
|
3556
|
+
content: [{ type: "text", text: `\u274C Failed to create epic: ${result.error}` }],
|
|
3557
|
+
details: { ok: false, error: result.error }
|
|
3558
|
+
};
|
|
3559
|
+
}
|
|
3560
|
+
const epic = result.data.epic;
|
|
3561
|
+
const text = [
|
|
3562
|
+
`\u2705 Epic created!`,
|
|
3563
|
+
`- **Name**: ${epic.name}`,
|
|
3564
|
+
`- **ID**: ${epic.id}`,
|
|
3565
|
+
epic.color ? `- **Color**: ${epic.color}` : null
|
|
3566
|
+
].filter(Boolean).join("\n");
|
|
3365
3567
|
return {
|
|
3366
3568
|
content: [{ type: "text", text }],
|
|
3367
|
-
details: { ok: true,
|
|
3569
|
+
details: { ok: true, epic }
|
|
3570
|
+
};
|
|
3571
|
+
}
|
|
3572
|
+
},
|
|
3573
|
+
// 8. remote_list_team
|
|
3574
|
+
{
|
|
3575
|
+
name: "remote_list_team",
|
|
3576
|
+
label: "List team members",
|
|
3577
|
+
description: "List all team members (humans and agents) with their roles and @mention handles. Use to find who to tag in comments or assign tasks to.",
|
|
3578
|
+
parameters: Type.Object({}),
|
|
3579
|
+
execute: async (_toolCallId, _args) => {
|
|
3580
|
+
const account = resolveAccount(cfg ?? {});
|
|
3581
|
+
const result = await getTeam(account);
|
|
3582
|
+
if (!result.ok) {
|
|
3583
|
+
return {
|
|
3584
|
+
content: [{ type: "text", text: `\u274C Failed to list team: ${result.error}` }],
|
|
3585
|
+
details: { ok: false, error: result.error }
|
|
3586
|
+
};
|
|
3587
|
+
}
|
|
3588
|
+
const { team, total, humans, agents } = result.data;
|
|
3589
|
+
if (team.length === 0) {
|
|
3590
|
+
return {
|
|
3591
|
+
content: [{ type: "text", text: "\u{1F465} No team members found." }],
|
|
3592
|
+
details: { ok: true, team: [] }
|
|
3593
|
+
};
|
|
3594
|
+
}
|
|
3595
|
+
const lines = [`\u{1F465} **Team** (${total} members: ${humans} human, ${agents} agent)`, ""];
|
|
3596
|
+
for (const m of team) {
|
|
3597
|
+
const roles = m.project_roles.length > 0 ? m.project_roles.map((r) => r.name).join(", ") : "no role";
|
|
3598
|
+
const me = m.is_me ? " \u2B50 (you)" : "";
|
|
3599
|
+
const status = m.type === "agent" && m.status ? ` [${m.status}]` : "";
|
|
3600
|
+
lines.push(`- **${m.name}** (${m.type}${status}) \u2014 ${roles} \u2014 mention: \`${m.mention}\`${me}`);
|
|
3601
|
+
}
|
|
3602
|
+
return {
|
|
3603
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
3604
|
+
details: { ok: true, team, total, humans, agents }
|
|
3368
3605
|
};
|
|
3369
3606
|
}
|
|
3370
3607
|
}
|
|
@@ -3379,10 +3616,14 @@ function createRemotePlugin() {
|
|
|
3379
3616
|
"**Replying**: When you reply to a task notification, your reply is posted as a comment on that task.",
|
|
3380
3617
|
"",
|
|
3381
3618
|
"**Available tools**:",
|
|
3382
|
-
"- `remote_create_task` \u2014 Create a new task (
|
|
3383
|
-
"- `remote_update_task` \u2014 Update a task (
|
|
3619
|
+
"- `remote_create_task` \u2014 Create a new task (title, type, priority, assigned_role_id, epic_id)",
|
|
3620
|
+
"- `remote_update_task` \u2014 Update a task (status, priority, title, description, type, epic_id, assigned_role_id)",
|
|
3384
3621
|
"- `remote_list_tasks` \u2014 List/filter tasks on the board",
|
|
3385
|
-
"- `remote_board_health` \u2014 Get board health stats (
|
|
3622
|
+
"- `remote_board_health` \u2014 Get board health stats (task counts, epics, roles, recent activity)",
|
|
3623
|
+
"- `remote_list_roles` \u2014 List project roles with vacancy info (find valid assigned_role_id values)",
|
|
3624
|
+
"- `remote_list_epics` \u2014 List epics with task counts (find valid epic_id values)",
|
|
3625
|
+
"- `remote_create_epic` \u2014 Create a new epic (name, description, color)",
|
|
3626
|
+
"- `remote_list_team` \u2014 List all team members with roles and @mention handles",
|
|
3386
3627
|
"",
|
|
3387
3628
|
"**Task lifecycle**: todo \u2192 in_progress \u2192 review \u2192 done",
|
|
3388
3629
|
"**Task types**: feature, task, bug",
|
|
@@ -3392,7 +3633,11 @@ function createRemotePlugin() {
|
|
|
3392
3633
|
"- When assigned a task, acknowledge it and move to in_progress",
|
|
3393
3634
|
"- Use comments to communicate progress and blockers",
|
|
3394
3635
|
"- Move tasks to review when ready for review, done when complete",
|
|
3395
|
-
"- Keep task descriptions and comments clear and actionable"
|
|
3636
|
+
"- Keep task descriptions and comments clear and actionable",
|
|
3637
|
+
"- Use `remote_list_roles` before creating tasks to find valid role IDs for assignment",
|
|
3638
|
+
"- Use `remote_list_epics` before creating tasks to find valid epic IDs for grouping",
|
|
3639
|
+
"- Use `remote_list_team` to find teammates and their @mention handles for tagging in comments",
|
|
3640
|
+
"- Tag team members with @name in comments when you need their input or want to delegate"
|
|
3396
3641
|
]
|
|
3397
3642
|
}
|
|
3398
3643
|
};
|