@ai-setting/roy-task-plugin 0.7.14 → 0.7.15
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 +161 -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,73 @@
|
|
|
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
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function resolveByNum(store, num) {
|
|
49
|
+
if (typeof store.getTaskByNum === "function") {
|
|
50
|
+
return store.getTaskByNum(num);
|
|
51
|
+
}
|
|
52
|
+
const res = await store.listTasks({ limit: 1000 });
|
|
53
|
+
return res.tasks.find((t) => t.num === num || t.num === undefined && t.id === num) ?? null;
|
|
54
|
+
}
|
|
55
|
+
function createTaskComponentAdapter(store) {
|
|
56
|
+
return {
|
|
57
|
+
getTask: async (id) => {
|
|
58
|
+
const t = await resolveByNum(store, id);
|
|
59
|
+
return t ? toCoreTaskShape(t) : undefined;
|
|
60
|
+
},
|
|
61
|
+
updateTask: async (id, options) => {
|
|
62
|
+
const t = await resolveByNum(store, id);
|
|
63
|
+
if (!t)
|
|
64
|
+
throw new Error(`task #${id} not found`);
|
|
65
|
+
const isCoreShape = t.num === undefined;
|
|
66
|
+
return store.updateTask(t.id, options.context === undefined ? {} : isCoreShape ? { context: options.context } : { roy: { context: options.context } });
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
5
71
|
// ../../../packages/cli/src/daemon/runners/types.ts
|
|
6
72
|
var RUNTIMES = Object.freeze(["opencode", "claude", "codex", "cursor"]);
|
|
7
73
|
var DEFAULT_RUNTIME = "opencode";
|
|
@@ -1461,7 +1527,8 @@ var VALUE_FLAGS = new Set([
|
|
|
1461
1527
|
"--codex-sandbox",
|
|
1462
1528
|
"--cursor-model",
|
|
1463
1529
|
"--max-concurrency",
|
|
1464
|
-
"--timeout-ms"
|
|
1530
|
+
"--timeout-ms",
|
|
1531
|
+
"--task-store"
|
|
1465
1532
|
]);
|
|
1466
1533
|
var SUBCOMMANDS = new Set(["serve", "status", "help", "version"]);
|
|
1467
1534
|
function parseArgs(argv) {
|
|
@@ -1640,6 +1707,13 @@ function parseServe(argv) {
|
|
|
1640
1707
|
case "--no-card":
|
|
1641
1708
|
serve.cardEnabled = false;
|
|
1642
1709
|
break;
|
|
1710
|
+
case "--task-store": {
|
|
1711
|
+
const v = take();
|
|
1712
|
+
if (v instanceof Error)
|
|
1713
|
+
return { command: "unknown", error: v.message };
|
|
1714
|
+
serve.taskStoreEndpoint = v;
|
|
1715
|
+
break;
|
|
1716
|
+
}
|
|
1643
1717
|
default:
|
|
1644
1718
|
return { command: "unknown", error: `unknown flag "${flag}"` };
|
|
1645
1719
|
}
|
|
@@ -2552,6 +2626,11 @@ function createEventListener(config) {
|
|
|
2552
2626
|
profile: config.profile,
|
|
2553
2627
|
eventKeys
|
|
2554
2628
|
});
|
|
2629
|
+
if (config.taskComponent) {
|
|
2630
|
+
instance.setEnvironment?.({
|
|
2631
|
+
getComponent: (name) => name === "task" ? config.taskComponent : undefined
|
|
2632
|
+
});
|
|
2633
|
+
}
|
|
2555
2634
|
let callback;
|
|
2556
2635
|
instance.onEvent((event) => {
|
|
2557
2636
|
const typed = event;
|
|
@@ -2577,6 +2656,14 @@ function createEventListener(config) {
|
|
|
2577
2656
|
plannedCommands() {
|
|
2578
2657
|
const profileFlag = config.profile ? `--profile ${config.profile} ` : "";
|
|
2579
2658
|
return eventKeys.map((k) => `lark-cli ${profileFlag}event consume ${k} --as bot`);
|
|
2659
|
+
},
|
|
2660
|
+
setTaskComponent(component) {
|
|
2661
|
+
instance.setEnvironment?.({
|
|
2662
|
+
getComponent: (name) => name === "task" ? component : undefined
|
|
2663
|
+
});
|
|
2664
|
+
},
|
|
2665
|
+
hasCardActionPorts() {
|
|
2666
|
+
return instance.hasCardActionPorts?.() ?? false;
|
|
2580
2667
|
}
|
|
2581
2668
|
};
|
|
2582
2669
|
}
|
|
@@ -3338,6 +3425,38 @@ function getTraceRuntime(options) {
|
|
|
3338
3425
|
return runtime;
|
|
3339
3426
|
}
|
|
3340
3427
|
|
|
3428
|
+
// ../../../packages/cli/src/daemon/es-config.ts
|
|
3429
|
+
import { resolveEsFilePath } from "@ai-setting/roy-agent-core/env/event-source";
|
|
3430
|
+
async function readEventSourcesFile(configPath) {
|
|
3431
|
+
try {
|
|
3432
|
+
const { readFile } = await import("node:fs/promises");
|
|
3433
|
+
const raw = await readFile(resolveEsFilePath(configPath), "utf-8");
|
|
3434
|
+
const parsed = JSON.parse(raw);
|
|
3435
|
+
return Array.isArray(parsed.sources) ? parsed.sources : [];
|
|
3436
|
+
} catch {
|
|
3437
|
+
return [];
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
async function resolveListenConfig(opts) {
|
|
3441
|
+
const sources = await readEventSourcesFile(opts.configPath);
|
|
3442
|
+
const larkSources = sources.filter((s) => s.type === "lark-cli" && s.enabled !== false);
|
|
3443
|
+
const profile = opts.profile ?? larkSources.find((s) => s.profile)?.profile;
|
|
3444
|
+
if (opts.eventKeys.length > 0) {
|
|
3445
|
+
return { profile, eventKeys: [...opts.eventKeys], matchedSourceIds: [] };
|
|
3446
|
+
}
|
|
3447
|
+
const matched = larkSources.filter((s) => s.profile === profile && Array.isArray(s.eventKeys));
|
|
3448
|
+
const keys = new Set;
|
|
3449
|
+
for (const s of matched) {
|
|
3450
|
+
for (const k of s.eventKeys ?? [])
|
|
3451
|
+
keys.add(k);
|
|
3452
|
+
}
|
|
3453
|
+
return {
|
|
3454
|
+
profile,
|
|
3455
|
+
eventKeys: [...keys],
|
|
3456
|
+
matchedSourceIds: matched.map((s) => s.id ?? "(unnamed)")
|
|
3457
|
+
};
|
|
3458
|
+
}
|
|
3459
|
+
|
|
3341
3460
|
// ../../../packages/cli/src/cmd/serve.ts
|
|
3342
3461
|
function serve(options, deps = {}) {
|
|
3343
3462
|
return startServe(options, deps);
|
|
@@ -3368,10 +3487,44 @@ async function startServe(options, deps) {
|
|
|
3368
3487
|
agent: options.agent,
|
|
3369
3488
|
runtime: options.runtime
|
|
3370
3489
|
});
|
|
3371
|
-
const
|
|
3490
|
+
const explicitKeys = options.eventKeys.length === 1 && options.eventKeys[0] === DEFAULT_EVENT_KEY ? [] : options.eventKeys;
|
|
3491
|
+
const listen = await resolveListenConfig({
|
|
3372
3492
|
profile: options.profile,
|
|
3373
|
-
eventKeys:
|
|
3493
|
+
eventKeys: explicitKeys
|
|
3494
|
+
});
|
|
3495
|
+
if (listen.matchedSourceIds.length > 0) {
|
|
3496
|
+
log(`[roy-task] event-sources.json: listening ${JSON.stringify(listen.eventKeys)} ` + `(profile=${listen.profile}, sources: ${listen.matchedSourceIds.join(", ")})`);
|
|
3497
|
+
}
|
|
3498
|
+
const listener = (deps.createListener ?? createEventListener)({
|
|
3499
|
+
profile: listen.profile,
|
|
3500
|
+
eventKeys: listen.eventKeys
|
|
3374
3501
|
});
|
|
3502
|
+
if (options.taskStoreEndpoint) {
|
|
3503
|
+
try {
|
|
3504
|
+
const { readFile } = await import("node:fs/promises");
|
|
3505
|
+
const { homedir } = await import("node:os");
|
|
3506
|
+
const { join: join4 } = await import("node:path");
|
|
3507
|
+
const { createTaskPortFromConfig } = await import("@ai-setting/roy-agent-core/env/task/backend");
|
|
3508
|
+
const { createTaskComponentAdapter: createTaskComponentAdapter2 } = await Promise.resolve().then(() => exports_card_action_ports);
|
|
3509
|
+
let authToken;
|
|
3510
|
+
try {
|
|
3511
|
+
const auth = JSON.parse(await readFile(join4(homedir(), ".roy-agent", "auth.json"), "utf-8"));
|
|
3512
|
+
authToken = auth.taskService?.authToken;
|
|
3513
|
+
} catch {}
|
|
3514
|
+
const wired = await createTaskPortFromConfig({
|
|
3515
|
+
get: (key) => ({
|
|
3516
|
+
"task.storage.type": "remote",
|
|
3517
|
+
"task.storage.endpoint": options.taskStoreEndpoint,
|
|
3518
|
+
"task.storage.authToken": authToken
|
|
3519
|
+
})[key]
|
|
3520
|
+
});
|
|
3521
|
+
const store = wired.port;
|
|
3522
|
+
listener.setTaskComponent(createTaskComponentAdapter2(store));
|
|
3523
|
+
log(`[roy-task] card-action wired to ${options.taskStoreEndpoint}`);
|
|
3524
|
+
} catch (err) {
|
|
3525
|
+
log(`[roy-task] card-action wiring failed: ${errMsg5(err)} — card.action.trigger events will be skipped`);
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3375
3528
|
let eventCount = 0;
|
|
3376
3529
|
let stopping;
|
|
3377
3530
|
let resolveDone;
|
|
@@ -3395,7 +3548,7 @@ async function startServe(options, deps) {
|
|
|
3395
3548
|
};
|
|
3396
3549
|
const sweeper = setInterval(() => {
|
|
3397
3550
|
deduper.sweep();
|
|
3398
|
-
},
|
|
3551
|
+
}, 300000);
|
|
3399
3552
|
if (typeof sweeper.unref === "function")
|
|
3400
3553
|
sweeper.unref();
|
|
3401
3554
|
listener.onEvent(async (event) => {
|
|
@@ -3469,13 +3622,13 @@ function formatResult(label, result) {
|
|
|
3469
3622
|
const card = cardSuffix(result);
|
|
3470
3623
|
switch (result.status) {
|
|
3471
3624
|
case "dispatched":
|
|
3472
|
-
return `[roy-task] ${label} dispatched chat=${result.plan.chatId} session=${result.plan.sessionId}
|
|
3625
|
+
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
3626
|
case "dry-run":
|
|
3474
|
-
return `[roy-task] ${label} DRY-RUN would run agent=${result.plan.agent}
|
|
3627
|
+
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
3628
|
case "duplicate":
|
|
3476
3629
|
return `[roy-task] ${label} duplicate dropped key=${result.key ?? "(unknown)"}`;
|
|
3477
3630
|
case "no-output":
|
|
3478
|
-
return `[roy-task] ${label} no-output (agent produced no text; not replying)
|
|
3631
|
+
return `[roy-task] ${label} no-output (agent produced no text; not replying) toolCalls=${result.toolCalls}${taskSuffix(result)}${card}`;
|
|
3479
3632
|
case "no-reply":
|
|
3480
3633
|
return `[roy-task] ${label} no-reply (reply disabled) chars=${result.text.length}${taskSuffix(result)}${card}`;
|
|
3481
3634
|
case "agent-error":
|
|
@@ -3611,7 +3764,7 @@ function formatStatus(report) {
|
|
|
3611
3764
|
}
|
|
3612
3765
|
|
|
3613
3766
|
// ../../../packages/cli/src/main.ts
|
|
3614
|
-
var VERSION = "0.7.
|
|
3767
|
+
var VERSION = "0.7.15";
|
|
3615
3768
|
async function main(argv) {
|
|
3616
3769
|
const parsed = parseArgs(argv);
|
|
3617
3770
|
switch (parsed.command) {
|