@flowweave-ai/cli 0.2.0 → 0.4.0
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/README.md +6 -2
- package/bin/flowweave.mjs +83 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ flowweave health --ready
|
|
|
11
11
|
|
|
12
12
|
配置只保存基础 URL 到 `~/.config/flowweave/config.json`(可由 `FLOWWEAVE_CONFIG_PATH` 覆盖)。登录会话单独保存到同目录的 `auth.json`(可由 `FLOWWEAVE_AUTH_PATH` 覆盖),文件权限为 `0600`,并且只会发送给登录时的同一平台地址。使用 `flowweave auth status` 检查当前身份,使用 `flowweave auth logout` 撤销服务端会话并删除本地文件。默认登录会隐藏密码输入;非交互环境使用 `--password-stdin`,不要把密码写进命令参数、脚本或日志。
|
|
13
13
|
|
|
14
|
-
页面域命令包括 `node`、`node-directory`、`capability`、`environment`、`credential`、`flow`、`run`、`schedule`、`model` 和 `agent
|
|
14
|
+
页面域命令包括 `node`、`node-directory`、`capability`、`event-trigger`、`environment`、`credential`、`flow`、`run`、`schedule`、`model` 和 `agent`。它们分别覆盖节点资产、能力仓库、运行事件触发器、终端环境、网站认证条目、流程编排、FlowRun、周期调度、大模型配置与 Agent 工作台的常用原子操作。`credential` 管理网站凭据,不等于 `auth` 用户登录。每个命令的 JSON 请求体与在线 OpenAPI 一致;运行 `flowweave <域> --help` 查看映射。
|
|
15
15
|
|
|
16
16
|
常用命令示例:
|
|
17
17
|
|
|
@@ -21,7 +21,11 @@ flowweave health --ready
|
|
|
21
21
|
|
|
22
22
|
读取 FlowRun 中的记录使用 `flowweave run node <run-id> --node <node-run-id>`;只有用户明确要求时才能执行 `node-copy` 或 `node-delete`。暂停或恢复 Runtime 前,必须先读取 `run runtime`,将返回的 `generation` 与 session `row_version` 写入 `expected_generation`、`expected_session_row_version` 后传给 `run pause` 或 `run resume`。供应商上游余额/用量使用 `flowweave model usage <provider-id>`,它可能依赖该供应商的有效 API 凭据。
|
|
23
23
|
|
|
24
|
-
周期任务使用 `schedule list/create/pause/resume/trigger/delete
|
|
24
|
+
周期任务使用 `schedule list/templates/occurrences/create/pause/resume/trigger/delete`。先用 `schedule templates` 读取可作为冻结母版的已就绪连续运行记录;创建请求必须使用在线 `FlowRunScheduleWrite` schema:只提交任务名称、母版 `source_flow_run_id` 和五段 `cron_expression`,节点、环境、输入和启动提示词均来自该记录的冻结母版。`schedule occurrences <schedule-id> --page 1 --page-size 10` 按需读取该调度的执行历史及其 FlowRun/NodeRun,而不预加载所有记录。暂停或恢复前从 `schedule list` 读取当前 `row_version`,再传入 `--expected-row-version`。手动触发会新增一次 occurrence,不会改写既有运行;删除生成的 FlowRun 不会删除调度,而有生成记录的调度删除会被平台拒绝。
|
|
25
|
+
|
|
26
|
+
能力命令还支持受治理的 OpenHands Plugin Marketplace:`capability marketplace` 读取服务端固定到完整 commit 的公开目录;使用返回的 `source`、`commit`、`repo_path` 与插件名称调用 `capability marketplace-resolve --data-file ./marketplace-plugin.json`,随后读取 `capability plugin-resolution <resolution-id>`,并以当前 `state_version` 调用 `capability plugin-publish <resolution-id> --data '{"expected_state_version": 1}'`。自定义受信任 Marketplace 可先用 `capability marketplace-preview --data-file ./marketplace.json` 审阅目录。Agent Definition 只能以 UTF-8 `.md`/`.markdown` 的 OpenHands YAML frontmatter + Markdown 正文通过 `capability validate|import --type AGENT_DEFINITION --file <file>` 导入。Hook 使用受控表单参数:`capability validate --type HOOK --file ./hook.md --hook-name <name> --hook-event <event> --hook-mode <PROMPT|SCRIPT>`;可选 `--hook-description` 与工具事件的 `--hook-matcher` 会被编译为 OpenHands 原生 `hook_config`,不能提交任意 Hook JSON。
|
|
27
|
+
|
|
28
|
+
事件触发器是用户拥有的不可变版本:先 `event-trigger list` 或 `event-trigger get <trigger-key>` 读取,再用 `event-trigger create --data-file ./trigger.json --dry-run` 创建,或用 `event-trigger version <trigger-key> --data-file ./trigger.json --dry-run` 新建版本。请求体必须使用在线 `EventTriggerWrite` schema;`actions[].config` 不得包含 token、password、secret 等明文凭据,Webhook/OAuth 等凭据应通过平台受控引用提供。
|
|
25
29
|
|
|
26
30
|
`api`、`upload`、`ws` 是完整契约入口:任意当前或未来的 REST、multipart、WebSocket 原子接口均可直接调用,不需要等待 CLI 发布。三者都会使用当前 `auth login` 会话;不得用 `--header` 手工传 Cookie。写操作支持 `--dry-run`,并可使用 `-H 'Idempotency-Key: …'` 传入一次性幂等键。对于没有快捷命令的新接口,先运行 `flowweave openapi --paths`,再通过通用命令调用。
|
|
27
31
|
|
package/bin/flowweave.mjs
CHANGED
|
@@ -32,12 +32,13 @@ function usage() {
|
|
|
32
32
|
页面域原子操作:
|
|
33
33
|
node <list|get|create|update|delete> [ID] [--data JSON|--data-file FILE]
|
|
34
34
|
node-directory <list|create|delete|delete-many> [ID] [--id ID ...]
|
|
35
|
-
capability <list|validate|commit|import> ...
|
|
35
|
+
capability <list|validate|commit|import|marketplace|marketplace-preview|marketplace-resolve|plugin-resolution|plugin-publish> ...
|
|
36
|
+
event-trigger <list|get|create|version> ...
|
|
36
37
|
environment <list|get|create|update|delete|setup|publish|stop|version-delete> ...
|
|
37
38
|
credential <list|create|update|delete|delete-many> ...
|
|
38
39
|
flow <list|get|create|update|validate|delete> ...
|
|
39
40
|
run <list|get|start|delete|runtime|replace|pause|resume|cancel|complete|events|node|node-copy|node-delete|workspace-delete|work-directory-delete> ...
|
|
40
|
-
schedule <list|create|pause|resume|trigger|delete> ...
|
|
41
|
+
schedule <list|templates|occurrences|create|pause|resume|trigger|delete> ...
|
|
41
42
|
model <list|create|update|delete|discover|usage|test|oauth-start|oauth-poll|oauth-status|oauth-revoke> ...
|
|
42
43
|
agent <default|workspace|runtime|conversations|conversation|create|send|interrupt|resume|work-directories|work-directory-create|work-directory-delete|file-delete> ...
|
|
43
44
|
|
|
@@ -334,6 +335,26 @@ async function nodeDirectory(args) {
|
|
|
334
335
|
async function capability(args) {
|
|
335
336
|
const [action, id] = positional(args);
|
|
336
337
|
if (action === 'list') return request('GET', '/capabilities', args);
|
|
338
|
+
if (action === 'marketplace') {
|
|
339
|
+
if (id) throw new CliError('capability marketplace 不接受 ID');
|
|
340
|
+
return request('GET', '/plugin-marketplace-catalogs/openhands', args);
|
|
341
|
+
}
|
|
342
|
+
if (action === 'marketplace-preview') {
|
|
343
|
+
if (id) throw new CliError('capability marketplace-preview 不接受 ID');
|
|
344
|
+
return request('POST', '/plugin-marketplace-catalogs/preview', args);
|
|
345
|
+
}
|
|
346
|
+
if (action === 'marketplace-resolve') {
|
|
347
|
+
if (id) throw new CliError('capability marketplace-resolve 不接受 ID');
|
|
348
|
+
return request('POST', '/plugin-source-resolutions/marketplace', args);
|
|
349
|
+
}
|
|
350
|
+
if (action === 'plugin-resolution') {
|
|
351
|
+
if (!id) throw new CliError('capability plugin-resolution 需要 resolution ID');
|
|
352
|
+
return request('GET', `/plugin-source-resolutions/${id}`, args);
|
|
353
|
+
}
|
|
354
|
+
if (action === 'plugin-publish') {
|
|
355
|
+
if (!id) throw new CliError('capability plugin-publish 需要 resolution ID');
|
|
356
|
+
return request('POST', `/plugin-source-resolutions/${id}/publish`, args);
|
|
357
|
+
}
|
|
337
358
|
if (action === 'validate' || action === 'import') {
|
|
338
359
|
const type = option(args, '--type');
|
|
339
360
|
const source = option(args, '--file');
|
|
@@ -341,6 +362,27 @@ async function capability(args) {
|
|
|
341
362
|
const file = resolve(source);
|
|
342
363
|
const content = await readFile(file);
|
|
343
364
|
const body = { capability_type: type, filename: basename(file), content_base64: content.toString('base64') };
|
|
365
|
+
if (type === 'HOOK') {
|
|
366
|
+
const name = option(args, '--hook-name');
|
|
367
|
+
const event = option(args, '--hook-event');
|
|
368
|
+
const mode = option(args, '--hook-mode');
|
|
369
|
+
if (!name || !event || !mode) {
|
|
370
|
+
throw new CliError('HOOK 导入需要 --hook-name、--hook-event 和 --hook-mode');
|
|
371
|
+
}
|
|
372
|
+
if (!['pre_tool_use', 'post_tool_use', 'user_prompt_submit', 'session_start', 'session_end', 'stop'].includes(event)) {
|
|
373
|
+
throw new CliError('--hook-event 必须是 OpenHands 支持的生命周期事件');
|
|
374
|
+
}
|
|
375
|
+
if (!['PROMPT', 'SCRIPT'].includes(mode)) {
|
|
376
|
+
throw new CliError('--hook-mode 必须是 PROMPT 或 SCRIPT');
|
|
377
|
+
}
|
|
378
|
+
Object.assign(body, {
|
|
379
|
+
hook_name: name,
|
|
380
|
+
hook_description: option(args, '--hook-description') || '',
|
|
381
|
+
hook_event: event,
|
|
382
|
+
hook_matcher: option(args, '--hook-matcher') || '*',
|
|
383
|
+
hook_mode: mode,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
344
386
|
const validated = await request('POST', '/capability-imports/validate', args, { body });
|
|
345
387
|
if (action === 'validate' || flag(args, '--dry-run')) return validated;
|
|
346
388
|
return request('POST', '/capability-imports', args, { body: { import_token: validated.import_token } });
|
|
@@ -350,7 +392,23 @@ async function capability(args) {
|
|
|
350
392
|
if (!token) throw new CliError('capability commit 需要 --import-token');
|
|
351
393
|
return request('POST', '/capability-imports', args, { body: { import_token: token } });
|
|
352
394
|
}
|
|
353
|
-
throw new CliError('capability 支持 list|validate|commit|import');
|
|
395
|
+
throw new CliError('capability 支持 list|validate|commit|import|marketplace|marketplace-preview|marketplace-resolve|plugin-resolution|plugin-publish');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function eventTrigger(args) {
|
|
399
|
+
const [action, key] = positional(args);
|
|
400
|
+
if (action === 'list') {
|
|
401
|
+
if (key) throw new CliError('event-trigger list 不接受 trigger key');
|
|
402
|
+
return request('GET', '/event-triggers', args);
|
|
403
|
+
}
|
|
404
|
+
if (action === 'create') {
|
|
405
|
+
if (key) throw new CliError('event-trigger create 不接受 trigger key;请在请求体提供 trigger_key');
|
|
406
|
+
return request('POST', '/event-triggers', args);
|
|
407
|
+
}
|
|
408
|
+
if (!key) throw new CliError(`event-trigger ${action || ''} 需要 trigger key`);
|
|
409
|
+
if (action === 'get') return request('GET', `/event-triggers/${key}`, args);
|
|
410
|
+
if (action === 'version') return request('POST', `/event-triggers/${key}/versions`, args);
|
|
411
|
+
throw new CliError('event-trigger 支持 list|get|create|version');
|
|
354
412
|
}
|
|
355
413
|
|
|
356
414
|
async function environment(args) {
|
|
@@ -463,11 +521,31 @@ async function schedule(args) {
|
|
|
463
521
|
if (id) throw new CliError('schedule list 不接受 ID');
|
|
464
522
|
return request('GET', '/flow-run-schedules', args);
|
|
465
523
|
}
|
|
524
|
+
if (action === 'templates') {
|
|
525
|
+
if (id) throw new CliError('schedule templates 不接受 ID');
|
|
526
|
+
return request('GET', '/flow-run-schedule-templates', args);
|
|
527
|
+
}
|
|
466
528
|
if (action === 'create') {
|
|
467
529
|
if (id) throw new CliError('schedule create 不接受 ID');
|
|
468
530
|
return request('POST', '/flow-run-schedules', args);
|
|
469
531
|
}
|
|
470
532
|
if (!id) throw new CliError(`schedule ${action || ''} 需要调度 ID`);
|
|
533
|
+
if (action === 'occurrences') {
|
|
534
|
+
const page = option(args, '--page');
|
|
535
|
+
const pageSize = option(args, '--page-size');
|
|
536
|
+
const query = [];
|
|
537
|
+
if (page !== undefined) {
|
|
538
|
+
const value = Number(page);
|
|
539
|
+
if (!Number.isSafeInteger(value) || value < 1) throw new CliError('schedule occurrences 的 --page 必须为正整数');
|
|
540
|
+
query.push(['page', String(value)]);
|
|
541
|
+
}
|
|
542
|
+
if (pageSize !== undefined) {
|
|
543
|
+
const value = Number(pageSize);
|
|
544
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 100) throw new CliError('schedule occurrences 的 --page-size 必须为 1 到 100 的整数');
|
|
545
|
+
query.push(['page_size', String(value)]);
|
|
546
|
+
}
|
|
547
|
+
return request('GET', `/flow-run-schedules/${id}/occurrences`, args, { query });
|
|
548
|
+
}
|
|
471
549
|
if (action === 'pause' || action === 'resume') {
|
|
472
550
|
const expected = Number(option(args, '--expected-row-version'));
|
|
473
551
|
if (!Number.isSafeInteger(expected) || expected < 1) {
|
|
@@ -479,7 +557,7 @@ async function schedule(args) {
|
|
|
479
557
|
}
|
|
480
558
|
if (action === 'trigger') return request('POST', `/flow-run-schedules/${id}/trigger`, args, { body: {} });
|
|
481
559
|
if (action === 'delete') return request('DELETE', `/flow-run-schedules/${id}`, args);
|
|
482
|
-
throw new CliError('schedule 支持 list|create|pause|resume|trigger|delete');
|
|
560
|
+
throw new CliError('schedule 支持 list|templates|occurrences|create|pause|resume|trigger|delete');
|
|
483
561
|
}
|
|
484
562
|
|
|
485
563
|
async function model(args) {
|
|
@@ -616,6 +694,7 @@ async function main(argv) {
|
|
|
616
694
|
else if (command === 'node') result = crud(command, args);
|
|
617
695
|
else if (command === 'node-directory') result = nodeDirectory(args);
|
|
618
696
|
else if (command === 'capability') result = capability(args);
|
|
697
|
+
else if (command === 'event-trigger') result = eventTrigger(args);
|
|
619
698
|
else if (command === 'environment') result = environment(args);
|
|
620
699
|
else if (command === 'credential') result = credential(args);
|
|
621
700
|
else if (command === 'flow') result = flow(args);
|