@myagentroam/marctl 0.10.9 → 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.9",
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,6 +93,14 @@ 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
+
83
104
  Reading JSON:
84
105
  show: session.activity, session.model, session.effort,
85
106
  session.customTitle / session.runnerTitle, runs[] (id/status),
@@ -243,6 +264,7 @@ or Server connection is required.`
243
264
  sessionWaitTimeout: 'Waiting timed out; the target Run was not stopped.',
244
265
 
245
266
  userContextUnavailable: 'This Node session has no valid marctl user authorization.',
267
+ sessionContextRequired: 'No current Session is available; specify a Session ID explicitly.',
246
268
  serverUrlInvalid: 'MAR_CTL_SERVER_URL is invalid.',
247
269
  serverUrlTransport: 'MAR_CTL_SERVER_URL must use HTTPS or loopback HTTP.',
248
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,6 +91,13 @@ ${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
+
82
101
  读取 JSON:
83
102
  show:session.activity、session.model、session.effort、
84
103
  session.customTitle / session.runnerTitle、runs[](id/status)、
@@ -230,6 +249,7 @@ ${configurationHelp}`,
230
249
  sessionWaitTimeout: '等待超时,未停止目标 Run。',
231
250
 
232
251
  userContextUnavailable: '此 Node 会话没有有效的 marctl 用户授权。',
252
+ sessionContextRequired: '当前上下文没有会话,请显式指定会话 ID。',
233
253
  serverUrlInvalid: 'MAR_CTL_SERVER_URL 无效。',
234
254
  serverUrlTransport: 'MAR_CTL_SERVER_URL 必须使用 HTTPS 或回环 HTTP。',
235
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
  )