@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.
@@ -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,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 listener = (deps.createListener ?? createEventListener)({
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: options.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
- }, 5 * 60 * 1000);
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} ` + `reply=${result.replyMessageId ?? "(no id)"} chars=${result.text.length}` + ` task=${result.taskId ?? "(none)"}${card}`;
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} ` + `session=${result.plan.sessionId} chat=${result.plan.chatId} prompt=${result.plan.prompt.length}chars`;
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) ` + `toolCalls=${result.toolCalls}${taskSuffix(result)}${card}`;
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.14";
3767
+ var VERSION = "0.7.15";
3615
3768
  async function main(argv) {
3616
3769
  const parsed = parseArgs(argv);
3617
3770
  switch (parsed.command) {