@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roy-task",
3
- "version": "0.6.0",
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 { SQLiteTaskStore } from "@ai-setting/roy-agent-core/env/task/storage";
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
- const store = new SQLiteTaskStore(dbPath);
63
- const backend = new TaskStoreBackend(createSQLiteTaskPort(store));
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 listener = (deps.createListener ?? createEventListener)({
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: options.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
- }, 5 * 60 * 1000);
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} ` + `reply=${result.replyMessageId ?? "(no id)"} chars=${result.text.length}` + ` task=${result.taskId ?? "(none)"}${card}`;
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} ` + `session=${result.plan.sessionId} chat=${result.plan.chatId} prompt=${result.plan.prompt.length}chars`;
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) ` + `toolCalls=${result.toolCalls}${taskSuffix(result)}${card}`;
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.14";
3768
+ var VERSION = "0.7.16";
3615
3769
  async function main(argv) {
3616
3770
  const parsed = parseArgs(argv);
3617
3771
  switch (parsed.command) {