@myagentroam/marctl 0.10.8 → 0.10.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/marctl",
3
- "version": "0.10.8",
3
+ "version": "0.10.10",
4
4
  "description": "MyAgentRoam command-line interface for authorized Node access.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -48,10 +48,19 @@ Usage:
48
48
  {program} current [--json]
49
49
  {program} list [--query <title>] [--parent <session-id>]
50
50
  [--cursor <cursor>] [--limit <1..100>] [--json]
51
- {program} show <session-id> [--json]
52
- {program} config <session-id> [--model <id>] [--effort <value>] [--json]
53
- {program} graph <session-id> [--limit <1..100>] [--json]
54
- {program} history <session-id>
51
+ {program} show [<session-id>] [--json]
52
+ {program} config [<session-id>]
53
+ [--model <id>] [--effort <value>] [--json]
54
+ {program} rename [<session-id>] --title <name> [--json]
55
+ {program} schedule list [--cursor <cursor>] [--limit <1..100>] [--json]
56
+ {program} schedule get [<session-id>] [--json]
57
+ {program} schedule create [<session-id>] --cron <cron>
58
+ (-m <text> | --message-file <file|->) [--json]
59
+ {program} schedule update [<session-id>]
60
+ --expected-revision <revision>
61
+ [--cron <cron>] [-m <text> | --message-file <file|->] [--json]
62
+ {program} graph [<session-id>] [--limit <1..100>] [--json]
63
+ {program} history [<session-id>]
55
64
  [--cursor <cursor>] [--limit <1..100>] [--json]
56
65
 
57
66
  {program} start --runner <runner>
@@ -72,6 +81,10 @@ Usage:
72
81
  --run <run-id> --timeout <30s|5m> [--json]
73
82
 
74
83
  Input:
84
+ show/config/history/graph/rename may omit the ID.
85
+ schedule get/create/update follow the same rule.
86
+ They use the current Session; without one, specify an ID explicitly.
87
+ send/goal/stop/wait always require an explicit Session ID.
75
88
  Runners: mar-agent, codex, claude-code, opencode; actual capabilities apply.
76
89
  -m / --message supplies text; it is mutually exclusive with --message-file.
77
90
  Files use UTF-8; - reads stdin. Repeat --image for PNG/JPEG/GIF/WebP files.
@@ -80,11 +93,29 @@ Input:
80
93
  Config requires model or effort; other fields and permissions are unchanged.
81
94
  Use --effort "" for the Runner default. Active execution is not restarted.
82
95
 
96
+ Titles and scheduled tasks:
97
+ rename uses the existing title update; no parent-tree restriction.
98
+ It does not restart execution. schedule list reads local Workspace tasks.
99
+ schedule get/create/update share MCP task data.
100
+ Schedule text is limited to 20,000 characters; update requires cron or text.
101
+ Other-owner edits create PENDING proposals; scheduled callers are read-only.
102
+ Read the current revision before updating; a version conflict is not retried.
103
+
104
+ Reading JSON:
105
+ show: session.activity, session.model, session.effort,
106
+ session.customTitle / session.runnerTitle, runs[] (id/status),
107
+ queue.items[], goal.status (goal may be null).
108
+ history: segments[].items[]; select kind === "assistant_message",
109
+ read payload.text.
110
+
83
111
  Delivery and waiting:
84
112
  Start/send return a clientMessageId.
85
113
  Send requires exactly one of --steer or --queue, even when idle.
86
- When useful, --steer adds gentle context to an in-progress task.
87
- --queue places input in a subsequent turn.
114
+ --steer prioritizes new context and may switch the current execution.
115
+ Node decides from actual state; reserve it for updates
116
+ affecting current work.
117
+ --queue schedules a subsequent turn; an active Goal can delay it
118
+ until the Goal exits.
88
119
  Example:
89
120
  marctl sessions send <session-id> --steer \\
90
121
  -m "Additional context: focus on the resume path." --json
@@ -233,6 +264,7 @@ or Server connection is required.`
233
264
  sessionWaitTimeout: 'Waiting timed out; the target Run was not stopped.',
234
265
 
235
266
  userContextUnavailable: 'This Node session has no valid marctl user authorization.',
267
+ sessionContextRequired: 'No current Session is available; specify a Session ID explicitly.',
236
268
  serverUrlInvalid: 'MAR_CTL_SERVER_URL is invalid.',
237
269
  serverUrlTransport: 'MAR_CTL_SERVER_URL must use HTTPS or loopback HTTP.',
238
270
  serverUrlOrigin: 'MAR_CTL_SERVER_URL must be an origin without credentials or query data.',
@@ -47,10 +47,19 @@ ${configurationHelp}
47
47
  {program} current [--json]
48
48
  {program} list [--query <title>] [--parent <session-id>]
49
49
  [--cursor <cursor>] [--limit <1..100>] [--json]
50
- {program} show <session-id> [--json]
51
- {program} config <session-id> [--model <id>] [--effort <value>] [--json]
52
- {program} graph <session-id> [--limit <1..100>] [--json]
53
- {program} history <session-id>
50
+ {program} show [<session-id>] [--json]
51
+ {program} config [<session-id>]
52
+ [--model <id>] [--effort <value>] [--json]
53
+ {program} rename [<session-id>] --title <name> [--json]
54
+ {program} schedule list [--cursor <cursor>] [--limit <1..100>] [--json]
55
+ {program} schedule get [<session-id>] [--json]
56
+ {program} schedule create [<session-id>] --cron <cron>
57
+ (-m <text> | --message-file <file|->) [--json]
58
+ {program} schedule update [<session-id>]
59
+ --expected-revision <revision>
60
+ [--cron <cron>] [-m <text> | --message-file <file|->] [--json]
61
+ {program} graph [<session-id>] [--limit <1..100>] [--json]
62
+ {program} history [<session-id>]
54
63
  [--cursor <cursor>] [--limit <1..100>] [--json]
55
64
 
56
65
  {program} start --runner <runner>
@@ -71,6 +80,9 @@ ${configurationHelp}
71
80
  --run <run-id> --timeout <30s|5m> [--json]
72
81
 
73
82
  输入:
83
+ show/config/history/graph/rename 和 schedule get/create/update 可省略 ID,
84
+ 使用当前会话;没有当前会话时请显式指定 ID。
85
+ send/goal/stop/wait 始终要求显式会话 ID。
74
86
  Runner:mar-agent、codex、claude-code、opencode,操作以实际能力为准。
75
87
  -m / --message 直接传文本,与 --message-file 互斥;
76
88
  文件用 UTF-8,- 从 stdin 读取。
@@ -79,11 +91,26 @@ ${configurationHelp}
79
91
  config 至少指定 model 或 effort;未指定字段和权限不变。
80
92
  --effort "" 使用 Runner 默认值,不重启当前执行。
81
93
 
94
+ 标题与定时任务:
95
+ rename 复用现有标题更新,不限制 parent 树、不重启执行。
96
+ schedule list 读取本地 Workspace 任务,get/create/update 与 MCP 共用任务数据。
97
+ 任务正文最多 20,000 字符;update 至少指定 cron 或正文。
98
+ 修改其他用户的任务会生成 PENDING 提议;定时来源只读。
99
+ 更新前读取当前 revision;版本冲突不会自动重试。
100
+
101
+ 读取 JSON:
102
+ show:session.activity、session.model、session.effort、
103
+ session.customTitle / session.runnerTitle、runs[](id/status)、
104
+ queue.items[]、goal.status(goal 可为空)。
105
+ history:segments[].items[] 中按 kind === "assistant_message"
106
+ 筛选,读取 payload.text。
107
+
82
108
  投递与等待:
83
109
  start/send 返回 clientMessageId。
84
110
  send 必须二选一:--steer 或 --queue,空闲时也需指定。
85
- --steer 在必要时为正在进行的任务补充温和的新信息。
86
- --queue 将输入排入后续轮次。
111
+ --steer 优先补充新信息,可能切换当前执行;
112
+ Node 按实际状态裁决,仅用于影响当前工作的必要更新。
113
+ --queue 排入后续轮次;有活动 Goal 时可能等待 Goal 退出才处理。
87
114
  示例:
88
115
  marctl sessions send <session-id> --steer \\
89
116
  -m "Additional context: focus on the resume path." --json
@@ -222,6 +249,7 @@ ${configurationHelp}`,
222
249
  sessionWaitTimeout: '等待超时,未停止目标 Run。',
223
250
 
224
251
  userContextUnavailable: '此 Node 会话没有有效的 marctl 用户授权。',
252
+ sessionContextRequired: '当前上下文没有会话,请显式指定会话 ID。',
225
253
  serverUrlInvalid: 'MAR_CTL_SERVER_URL 无效。',
226
254
  serverUrlTransport: 'MAR_CTL_SERVER_URL 必须使用 HTTPS 或回环 HTTP。',
227
255
  serverUrlOrigin: 'MAR_CTL_SERVER_URL 必须是不含凭据或查询参数的 Origin。',
package/src/sessions.mjs CHANGED
@@ -5,6 +5,16 @@ import { MarctlError } from './config.mjs';
5
5
  import { createTranslator } from './i18n/index.mjs';
6
6
 
7
7
  const terminal = new Set(['SUCCEEDED', 'FAILED', 'CANCELLED', 'INTERRUPTED']);
8
+ const currentSessionActions = new Set([
9
+ 'show',
10
+ 'config',
11
+ 'history',
12
+ 'graph',
13
+ 'rename',
14
+ 'schedule.get',
15
+ 'schedule.create',
16
+ 'schedule.update'
17
+ ]);
8
18
  const commands = {
9
19
  current: [],
10
20
  list: ['cursor', 'limit', 'query', 'parent'],
@@ -22,6 +32,11 @@ const commands = {
22
32
  'client-message-id'
23
33
  ],
24
34
  config: ['model', 'effort'],
35
+ rename: ['title'],
36
+ 'schedule.list': ['cursor', 'limit'],
37
+ 'schedule.get': [],
38
+ 'schedule.create': ['cron', 'message', 'message-file'],
39
+ 'schedule.update': ['cron', 'message', 'message-file', 'expected-revision'],
25
40
  send: ['message', 'message-file', 'image', 'steer', 'queue', 'client-message-id'],
26
41
  stop: ['run'],
27
42
  'goal.get': [],
@@ -31,6 +46,7 @@ const commands = {
31
46
  };
32
47
  const fieldNames = {
33
48
  parent: 'parentSessionId',
49
+ 'expected-revision': 'expectedRevision',
34
50
  'message-file': 'messageFile',
35
51
  'client-message-id': 'clientMessageId',
36
52
  run: 'runId',
@@ -51,13 +67,17 @@ export function parseSessionArgs(argv, translate = createTranslator()) {
51
67
  return { command: 'sessions', help: true };
52
68
  const input = [...argv];
53
69
  let action = input.shift();
54
- if (action === 'goal') action += '.' + input.shift();
70
+ if (action === 'goal' || action === 'schedule') action += '.' + input.shift();
55
71
  if (!Object.hasOwn(commands, action)) throw invalid();
56
72
  const args = { command: 'sessions', action };
57
- if (!['current', 'list', 'start'].includes(action)) {
58
- const sessionId = input.shift();
59
- if (!sessionId || sessionId.startsWith('-') || sessionId.length > 512) throw invalid();
60
- args.sessionId = sessionId;
73
+ if (!['current', 'list', 'start', 'schedule.list'].includes(action)) {
74
+ if ((input[0] === undefined || input[0].startsWith('-')) && !currentSessionActions.has(action))
75
+ throw invalid();
76
+ if (input[0] !== undefined && !input[0].startsWith('-')) {
77
+ const sessionId = input.shift();
78
+ if (!sessionId || sessionId.length > 512) throw invalid();
79
+ args.sessionId = sessionId;
80
+ }
61
81
  }
62
82
  while (input.length > 0) {
63
83
  const rawFlag = input.shift();
@@ -86,8 +106,13 @@ export function parseSessionArgs(argv, translate = createTranslator()) {
86
106
  if (name === 'image') {
87
107
  (args.images ??= []).push(value);
88
108
  if (args.images.length > 5) throw invalid();
89
- } else if (name === 'limit') {
90
- if (!/^[1-9][0-9]*$/u.test(value) || Number(value) > 100) throw invalid();
109
+ } else if (name === 'limit' || name === 'expected-revision') {
110
+ if (
111
+ !/^[1-9][0-9]*$/u.test(value) ||
112
+ !Number.isSafeInteger(Number(value)) ||
113
+ (name === 'limit' && Number(value) > 100)
114
+ )
115
+ throw invalid();
91
116
  args[key] = Number(value);
92
117
  } else if (name === 'timeout') {
93
118
  const match = /^([1-9][0-9]*)(ms|s|m)$/u.exec(value);
@@ -100,7 +125,7 @@ export function parseSessionArgs(argv, translate = createTranslator()) {
100
125
  throw invalid('errors.sessionDeliveryRequired');
101
126
  if (args.message !== undefined && args.messageFile !== undefined) throw invalid();
102
127
  if (
103
- ['start', 'send', 'goal.set'].includes(action) &&
128
+ ['start', 'send', 'goal.set', 'schedule.create'].includes(action) &&
104
129
  args.messageFile === undefined &&
105
130
  args.message === undefined &&
106
131
  !args.images?.length
@@ -113,17 +138,37 @@ export function parseSessionArgs(argv, translate = createTranslator()) {
113
138
  throw invalid();
114
139
  if (action === 'wait' && (args.runId === undefined || args.timeoutMs === undefined))
115
140
  throw invalid();
141
+ if (action === 'rename' && args.title !== undefined) args.title = args.title.trim();
116
142
  if (args.title !== undefined && args.title.length > 160) throw invalid();
117
143
  if (args.model !== undefined && (args.model.trim().length === 0 || args.model.length > 120))
118
144
  throw invalid();
119
145
  if (args.effort !== undefined && args.effort.length > 120) throw invalid();
120
146
  if (action === 'config' && args.model === undefined && args.effort === undefined) throw invalid();
147
+ if (action === 'rename') {
148
+ if (args.title === undefined || args.title.trim().length === 0) throw invalid();
149
+ }
150
+ if (action === 'schedule.create' && args.cron === undefined) throw invalid();
151
+ if (args.cron !== undefined && (args.cron.trim().length === 0 || args.cron.length > 512))
152
+ throw invalid();
153
+ if (
154
+ action === 'schedule.update' &&
155
+ (args.expectedRevision === undefined ||
156
+ (args.cron === undefined && args.message === undefined && args.messageFile === undefined))
157
+ )
158
+ throw invalid();
121
159
  if (args.query !== undefined && args.query.length > 160) throw invalid();
122
160
  if (args.parentSessionId !== undefined && args.parentSessionId.length > 512) throw invalid();
123
161
  return args;
124
162
  }
125
163
 
126
164
  export async function runSessionCommand({ args, client, env, stdin, WebSocketClass, translate }) {
165
+ const targeted = !['current', 'list', 'start', 'schedule.list'].includes(args.action);
166
+ const sessionId =
167
+ args.sessionId ?? (currentSessionActions.has(args.action) ? env.MAR_CTL_SESSION_ID : undefined);
168
+ if (targeted && !sessionId)
169
+ throw new MarctlError(translate('errors.sessionContextRequired'), {
170
+ code: 'MARCTL_SESSION_CONTEXT_REQUIRED'
171
+ });
127
172
  if (args.action === 'wait') {
128
173
  const controller = new AbortController();
129
174
  const cancel = () => controller.abort();
@@ -132,7 +177,7 @@ export async function runSessionCommand({ args, client, env, stdin, WebSocketCla
132
177
  try {
133
178
  return await waitForSessionRun({
134
179
  client,
135
- sessionId: args.sessionId,
180
+ sessionId,
136
181
  runId: args.runId,
137
182
  timeoutMs: args.timeoutMs,
138
183
  WebSocketClass,
@@ -155,9 +200,12 @@ export async function runSessionCommand({ args, client, env, stdin, WebSocketCla
155
200
  'cursor',
156
201
  'limit',
157
202
  'query',
158
- 'runId'
203
+ 'runId',
204
+ 'cron',
205
+ 'expectedRevision'
159
206
  ])
160
207
  if (args[name] !== undefined) body[name] = args[name];
208
+ if (targeted) body.sessionId = sessionId;
161
209
  if (args.action === 'current' && env.MAR_CTL_SESSION_ID) body.sessionId = env.MAR_CTL_SESSION_ID;
162
210
  if (args.action === 'start' && env.MAR_CTL_SESSION_ID)
163
211
  body.parentSessionId = env.MAR_CTL_SESSION_ID;
@@ -167,7 +215,7 @@ export async function runSessionCommand({ args, client, env, stdin, WebSocketCla
167
215
  body.content = await messageContent(
168
216
  args,
169
217
  stdin,
170
- args.action === 'goal.set' ? 20000 : 100000,
218
+ args.action === 'goal.set' || args.action.startsWith('schedule.') ? 20000 : 100000,
171
219
  translate
172
220
  );
173
221
  if (args.images !== undefined) {
@@ -189,7 +237,17 @@ export async function runSessionCommand({ args, client, env, stdin, WebSocketCla
189
237
  } catch (error) {
190
238
  if (body.clientMessageId !== undefined) error.clientMessageId = body.clientMessageId;
191
239
  if (
192
- ['start', 'send', 'config', 'stop', 'goal.set', 'goal.clear'].includes(args.action) &&
240
+ [
241
+ 'start',
242
+ 'send',
243
+ 'config',
244
+ 'rename',
245
+ 'schedule.create',
246
+ 'schedule.update',
247
+ 'stop',
248
+ 'goal.set',
249
+ 'goal.clear'
250
+ ].includes(args.action) &&
193
251
  ['MARCTL_REQUEST_TIMEOUT', 'MARCTL_SERVER_UNREACHABLE', 'MARCTL_RESPONSE_INVALID'].includes(
194
252
  error.code
195
253
  )