@ai-setting/roy-task-plugin 0.7.14 → 0.7.16
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/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/dist/claude-plugin/mcp/bridge.mjs +33 -3
- package/dist/cli.js +162 -8
- package/dist/index.js +223 -218
- package/dist/index.js.map +22 -22
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roy-task",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Roy — task-as-first-class-citizen harness for Claude Code. Ships the roy agent, slash commands for the task lifecycle, and an MCP server exposing the task tools natively.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "dongzhaokun",
|
|
@@ -17,8 +17,12 @@ import { createTaskMcpServer } from "@ai-setting/roy-agent-core/env/task/mcp";
|
|
|
17
17
|
import {
|
|
18
18
|
TaskStoreBackend,
|
|
19
19
|
createSQLiteTaskPort,
|
|
20
|
+
createRemoteTaskPortFromStore,
|
|
20
21
|
} from "@ai-setting/roy-agent-core/env/task/backend";
|
|
21
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
SQLiteTaskStore,
|
|
24
|
+
RemoteTaskStore,
|
|
25
|
+
} from "@ai-setting/roy-agent-core/env/task/storage";
|
|
22
26
|
import { homedir } from "node:os";
|
|
23
27
|
import { join } from "node:path";
|
|
24
28
|
import { createNotifyDispatcher, taskIdFromResult } from "./task-notify-hooks.mjs";
|
|
@@ -59,8 +63,34 @@ export function defaultTaskDbPath() {
|
|
|
59
63
|
* @param {{ cardNotifier?: object, decisionPort?: object, goalCheckTimeoutMs?: number }} deps test seams
|
|
60
64
|
*/
|
|
61
65
|
export async function createServer(dbPath = defaultTaskDbPath(), deps = {}) {
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
// Task #5569 — remote mode: when ROY_TASK_REMOTE_ENDPOINT is set,
|
|
67
|
+
// talk to the resident task-service over core's /store protocol
|
|
68
|
+
// instead of a local SQLite file. Same backend, same tools.
|
|
69
|
+
// The env var reaches this process because the claude runner
|
|
70
|
+
// spawns the MCP server with the daemon's full environment
|
|
71
|
+
// (mcp-notify.ts documents the inheritance).
|
|
72
|
+
let store, port;
|
|
73
|
+
const remoteEndpoint = process.env.ROY_TASK_REMOTE_ENDPOINT;
|
|
74
|
+
if (remoteEndpoint) {
|
|
75
|
+
const { readFile } = await import("node:fs/promises");
|
|
76
|
+
let authToken = process.env.ROY_TASK_REMOTE_TOKEN;
|
|
77
|
+
if (!authToken) {
|
|
78
|
+
try {
|
|
79
|
+
const auth = JSON.parse(
|
|
80
|
+
await readFile(join(homedir(), ".roy-agent", "auth.json"), "utf-8"),
|
|
81
|
+
);
|
|
82
|
+
authToken = auth.taskService?.authToken;
|
|
83
|
+
} catch {
|
|
84
|
+
// no auth.json — service may accept unauthenticated calls
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
store = new RemoteTaskStore({ endpoint: remoteEndpoint, authToken });
|
|
88
|
+
port = createRemoteTaskPortFromStore(store);
|
|
89
|
+
} else {
|
|
90
|
+
store = new SQLiteTaskStore(dbPath);
|
|
91
|
+
port = createSQLiteTaskPort(store);
|
|
92
|
+
}
|
|
93
|
+
const backend = new TaskStoreBackend(port);
|
|
64
94
|
const server = createTaskMcpServer(backend, {
|
|
65
95
|
name: "roy-task",
|
|
66
96
|
version: "1.0.0",
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,74 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __returnValue = (v) => v;
|
|
5
|
+
function __exportSetter(name, newValue) {
|
|
6
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
7
|
+
}
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, {
|
|
11
|
+
get: all[name],
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
set: __exportSetter.bind(all, name)
|
|
15
|
+
});
|
|
16
|
+
};
|
|
3
17
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
18
|
|
|
19
|
+
// ../../../packages/cli/src/daemon/card-action-ports.ts
|
|
20
|
+
var exports_card_action_ports = {};
|
|
21
|
+
__export(exports_card_action_ports, {
|
|
22
|
+
toCoreTaskShape: () => toCoreTaskShape,
|
|
23
|
+
createTaskComponentAdapter: () => createTaskComponentAdapter
|
|
24
|
+
});
|
|
25
|
+
function toCoreTaskShape(task) {
|
|
26
|
+
const roy = task.roy ?? {};
|
|
27
|
+
const str = (v) => v === undefined || v === null ? undefined : String(v);
|
|
28
|
+
const pick = (key) => {
|
|
29
|
+
const top = task[key];
|
|
30
|
+
if (top !== undefined && top !== null)
|
|
31
|
+
return str(top);
|
|
32
|
+
return str(roy[key]);
|
|
33
|
+
};
|
|
34
|
+
const parentTop = task.parent_task_id;
|
|
35
|
+
return {
|
|
36
|
+
id: task.num ?? Number(task.id),
|
|
37
|
+
title: task.title,
|
|
38
|
+
...task.description ? { description: task.description } : {},
|
|
39
|
+
...pick("goals_and_expected_deliverables") !== undefined ? { goals_and_expected_deliverables: pick("goals_and_expected_deliverables") } : {},
|
|
40
|
+
...pick("priority") !== undefined ? { priority: pick("priority") } : {},
|
|
41
|
+
...pick("type") !== undefined ? { type: pick("type") } : {},
|
|
42
|
+
...pick("project_path") !== undefined ? { project_path: pick("project_path") } : {},
|
|
43
|
+
...typeof parentTop === "number" ? { parent_task_id: parentTop } : typeof roy.parent_task_id === "number" ? { parent_task_id: roy.parent_task_id } : { parent_task_id: null },
|
|
44
|
+
...pick("current_status") !== undefined ? { current_status: pick("current_status") } : {},
|
|
45
|
+
...pick("context") !== undefined ? { context: pick("context") } : {},
|
|
46
|
+
...task.session_id ? { session_id: task.session_id } : {}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function resolveByNum(store, num) {
|
|
50
|
+
if (typeof store.getTaskByNum === "function") {
|
|
51
|
+
return store.getTaskByNum(num);
|
|
52
|
+
}
|
|
53
|
+
const res = await store.listTasks({ limit: 1000 });
|
|
54
|
+
return res.tasks.find((t) => t.num === num || t.num === undefined && t.id === num) ?? null;
|
|
55
|
+
}
|
|
56
|
+
function createTaskComponentAdapter(store) {
|
|
57
|
+
return {
|
|
58
|
+
getTask: async (id) => {
|
|
59
|
+
const t = await resolveByNum(store, id);
|
|
60
|
+
return t ? toCoreTaskShape(t) : undefined;
|
|
61
|
+
},
|
|
62
|
+
updateTask: async (id, options) => {
|
|
63
|
+
const t = await resolveByNum(store, id);
|
|
64
|
+
if (!t)
|
|
65
|
+
throw new Error(`task #${id} not found`);
|
|
66
|
+
const isCoreShape = t.num === undefined;
|
|
67
|
+
return store.updateTask(t.id, options.context === undefined ? {} : isCoreShape ? { context: options.context } : { roy: { context: options.context } });
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
5
72
|
// ../../../packages/cli/src/daemon/runners/types.ts
|
|
6
73
|
var RUNTIMES = Object.freeze(["opencode", "claude", "codex", "cursor"]);
|
|
7
74
|
var DEFAULT_RUNTIME = "opencode";
|
|
@@ -1461,7 +1528,8 @@ var VALUE_FLAGS = new Set([
|
|
|
1461
1528
|
"--codex-sandbox",
|
|
1462
1529
|
"--cursor-model",
|
|
1463
1530
|
"--max-concurrency",
|
|
1464
|
-
"--timeout-ms"
|
|
1531
|
+
"--timeout-ms",
|
|
1532
|
+
"--task-store"
|
|
1465
1533
|
]);
|
|
1466
1534
|
var SUBCOMMANDS = new Set(["serve", "status", "help", "version"]);
|
|
1467
1535
|
function parseArgs(argv) {
|
|
@@ -1640,6 +1708,13 @@ function parseServe(argv) {
|
|
|
1640
1708
|
case "--no-card":
|
|
1641
1709
|
serve.cardEnabled = false;
|
|
1642
1710
|
break;
|
|
1711
|
+
case "--task-store": {
|
|
1712
|
+
const v = take();
|
|
1713
|
+
if (v instanceof Error)
|
|
1714
|
+
return { command: "unknown", error: v.message };
|
|
1715
|
+
serve.taskStoreEndpoint = v;
|
|
1716
|
+
break;
|
|
1717
|
+
}
|
|
1643
1718
|
default:
|
|
1644
1719
|
return { command: "unknown", error: `unknown flag "${flag}"` };
|
|
1645
1720
|
}
|
|
@@ -2552,6 +2627,11 @@ function createEventListener(config) {
|
|
|
2552
2627
|
profile: config.profile,
|
|
2553
2628
|
eventKeys
|
|
2554
2629
|
});
|
|
2630
|
+
if (config.taskComponent) {
|
|
2631
|
+
instance.setEnvironment?.({
|
|
2632
|
+
getComponent: (name) => name === "task" ? config.taskComponent : undefined
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2555
2635
|
let callback;
|
|
2556
2636
|
instance.onEvent((event) => {
|
|
2557
2637
|
const typed = event;
|
|
@@ -2577,6 +2657,14 @@ function createEventListener(config) {
|
|
|
2577
2657
|
plannedCommands() {
|
|
2578
2658
|
const profileFlag = config.profile ? `--profile ${config.profile} ` : "";
|
|
2579
2659
|
return eventKeys.map((k) => `lark-cli ${profileFlag}event consume ${k} --as bot`);
|
|
2660
|
+
},
|
|
2661
|
+
setTaskComponent(component) {
|
|
2662
|
+
instance.setEnvironment?.({
|
|
2663
|
+
getComponent: (name) => name === "task" ? component : undefined
|
|
2664
|
+
});
|
|
2665
|
+
},
|
|
2666
|
+
hasCardActionPorts() {
|
|
2667
|
+
return instance.hasCardActionPorts?.() ?? false;
|
|
2580
2668
|
}
|
|
2581
2669
|
};
|
|
2582
2670
|
}
|
|
@@ -3338,6 +3426,38 @@ function getTraceRuntime(options) {
|
|
|
3338
3426
|
return runtime;
|
|
3339
3427
|
}
|
|
3340
3428
|
|
|
3429
|
+
// ../../../packages/cli/src/daemon/es-config.ts
|
|
3430
|
+
import { resolveEsFilePath } from "@ai-setting/roy-agent-core/env/event-source";
|
|
3431
|
+
async function readEventSourcesFile(configPath) {
|
|
3432
|
+
try {
|
|
3433
|
+
const { readFile } = await import("node:fs/promises");
|
|
3434
|
+
const raw = await readFile(resolveEsFilePath(configPath), "utf-8");
|
|
3435
|
+
const parsed = JSON.parse(raw);
|
|
3436
|
+
return Array.isArray(parsed.sources) ? parsed.sources : [];
|
|
3437
|
+
} catch {
|
|
3438
|
+
return [];
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
async function resolveListenConfig(opts) {
|
|
3442
|
+
const sources = await readEventSourcesFile(opts.configPath);
|
|
3443
|
+
const larkSources = sources.filter((s) => s.type === "lark-cli" && s.enabled !== false);
|
|
3444
|
+
const profile = opts.profile ?? larkSources.find((s) => s.profile)?.profile;
|
|
3445
|
+
if (opts.eventKeys.length > 0) {
|
|
3446
|
+
return { profile, eventKeys: [...opts.eventKeys], matchedSourceIds: [] };
|
|
3447
|
+
}
|
|
3448
|
+
const matched = larkSources.filter((s) => s.profile === profile && Array.isArray(s.eventKeys));
|
|
3449
|
+
const keys = new Set;
|
|
3450
|
+
for (const s of matched) {
|
|
3451
|
+
for (const k of s.eventKeys ?? [])
|
|
3452
|
+
keys.add(k);
|
|
3453
|
+
}
|
|
3454
|
+
return {
|
|
3455
|
+
profile,
|
|
3456
|
+
eventKeys: [...keys],
|
|
3457
|
+
matchedSourceIds: matched.map((s) => s.id ?? "(unnamed)")
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3341
3461
|
// ../../../packages/cli/src/cmd/serve.ts
|
|
3342
3462
|
function serve(options, deps = {}) {
|
|
3343
3463
|
return startServe(options, deps);
|
|
@@ -3368,10 +3488,44 @@ async function startServe(options, deps) {
|
|
|
3368
3488
|
agent: options.agent,
|
|
3369
3489
|
runtime: options.runtime
|
|
3370
3490
|
});
|
|
3371
|
-
const
|
|
3491
|
+
const explicitKeys = options.eventKeys.length === 1 && options.eventKeys[0] === DEFAULT_EVENT_KEY ? [] : options.eventKeys;
|
|
3492
|
+
const listen = await resolveListenConfig({
|
|
3372
3493
|
profile: options.profile,
|
|
3373
|
-
eventKeys:
|
|
3494
|
+
eventKeys: explicitKeys
|
|
3495
|
+
});
|
|
3496
|
+
if (listen.matchedSourceIds.length > 0) {
|
|
3497
|
+
log(`[roy-task] event-sources.json: listening ${JSON.stringify(listen.eventKeys)} ` + `(profile=${listen.profile}, sources: ${listen.matchedSourceIds.join(", ")})`);
|
|
3498
|
+
}
|
|
3499
|
+
const listener = (deps.createListener ?? createEventListener)({
|
|
3500
|
+
profile: listen.profile,
|
|
3501
|
+
eventKeys: listen.eventKeys
|
|
3374
3502
|
});
|
|
3503
|
+
if (options.taskStoreEndpoint) {
|
|
3504
|
+
try {
|
|
3505
|
+
const { readFile } = await import("node:fs/promises");
|
|
3506
|
+
const { homedir } = await import("node:os");
|
|
3507
|
+
const { join: join4 } = await import("node:path");
|
|
3508
|
+
const { createTaskPortFromConfig } = await import("@ai-setting/roy-agent-core/env/task/backend");
|
|
3509
|
+
const { createTaskComponentAdapter: createTaskComponentAdapter2 } = await Promise.resolve().then(() => exports_card_action_ports);
|
|
3510
|
+
let authToken;
|
|
3511
|
+
try {
|
|
3512
|
+
const auth = JSON.parse(await readFile(join4(homedir(), ".roy-agent", "auth.json"), "utf-8"));
|
|
3513
|
+
authToken = auth.taskService?.authToken;
|
|
3514
|
+
} catch {}
|
|
3515
|
+
const wired = await createTaskPortFromConfig({
|
|
3516
|
+
get: (key) => ({
|
|
3517
|
+
"task.storage.type": "remote",
|
|
3518
|
+
"task.storage.endpoint": options.taskStoreEndpoint,
|
|
3519
|
+
"task.storage.authToken": authToken
|
|
3520
|
+
})[key]
|
|
3521
|
+
});
|
|
3522
|
+
const store = wired.port;
|
|
3523
|
+
listener.setTaskComponent(createTaskComponentAdapter2(store));
|
|
3524
|
+
log(`[roy-task] card-action wired to ${options.taskStoreEndpoint}`);
|
|
3525
|
+
} catch (err) {
|
|
3526
|
+
log(`[roy-task] card-action wiring failed: ${errMsg5(err)} — card.action.trigger events will be skipped`);
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3375
3529
|
let eventCount = 0;
|
|
3376
3530
|
let stopping;
|
|
3377
3531
|
let resolveDone;
|
|
@@ -3395,7 +3549,7 @@ async function startServe(options, deps) {
|
|
|
3395
3549
|
};
|
|
3396
3550
|
const sweeper = setInterval(() => {
|
|
3397
3551
|
deduper.sweep();
|
|
3398
|
-
},
|
|
3552
|
+
}, 300000);
|
|
3399
3553
|
if (typeof sweeper.unref === "function")
|
|
3400
3554
|
sweeper.unref();
|
|
3401
3555
|
listener.onEvent(async (event) => {
|
|
@@ -3469,13 +3623,13 @@ function formatResult(label, result) {
|
|
|
3469
3623
|
const card = cardSuffix(result);
|
|
3470
3624
|
switch (result.status) {
|
|
3471
3625
|
case "dispatched":
|
|
3472
|
-
return `[roy-task] ${label} dispatched chat=${result.plan.chatId} session=${result.plan.sessionId}
|
|
3626
|
+
return `[roy-task] ${label} dispatched chat=${result.plan.chatId} session=${result.plan.sessionId} reply=${result.replyMessageId ?? "(no id)"} chars=${result.text.length} task=${result.taskId ?? "(none)"}${card}`;
|
|
3473
3627
|
case "dry-run":
|
|
3474
|
-
return `[roy-task] ${label} DRY-RUN would run agent=${result.plan.agent}
|
|
3628
|
+
return `[roy-task] ${label} DRY-RUN would run agent=${result.plan.agent} session=${result.plan.sessionId} chat=${result.plan.chatId} prompt=${result.plan.prompt.length}chars`;
|
|
3475
3629
|
case "duplicate":
|
|
3476
3630
|
return `[roy-task] ${label} duplicate dropped key=${result.key ?? "(unknown)"}`;
|
|
3477
3631
|
case "no-output":
|
|
3478
|
-
return `[roy-task] ${label} no-output (agent produced no text; not replying)
|
|
3632
|
+
return `[roy-task] ${label} no-output (agent produced no text; not replying) toolCalls=${result.toolCalls}${taskSuffix(result)}${card}`;
|
|
3479
3633
|
case "no-reply":
|
|
3480
3634
|
return `[roy-task] ${label} no-reply (reply disabled) chars=${result.text.length}${taskSuffix(result)}${card}`;
|
|
3481
3635
|
case "agent-error":
|
|
@@ -3611,7 +3765,7 @@ function formatStatus(report) {
|
|
|
3611
3765
|
}
|
|
3612
3766
|
|
|
3613
3767
|
// ../../../packages/cli/src/main.ts
|
|
3614
|
-
var VERSION = "0.7.
|
|
3768
|
+
var VERSION = "0.7.16";
|
|
3615
3769
|
async function main(argv) {
|
|
3616
3770
|
const parsed = parseArgs(argv);
|
|
3617
3771
|
switch (parsed.command) {
|