@oomol-lab/open-flow 0.1.0-beta.63 → 0.1.0-beta.64

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.
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+ export declare const canvasMinZoom = 0.1;
3
+ export declare const canvasMaxZoom = 3;
4
+ /** Transient operations on each connected canvas's own viewport. */
5
+ export declare const canvasCommandSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6
+ type: z.ZodLiteral<"focus">;
7
+ nodeId: z.ZodString;
8
+ }, z.core.$strict>, z.ZodObject<{
9
+ type: z.ZodLiteral<"zoom">;
10
+ zoom: z.ZodNumber;
11
+ }, z.core.$strict>, z.ZodObject<{
12
+ type: z.ZodLiteral<"pan">;
13
+ dx: z.ZodNumber;
14
+ dy: z.ZodNumber;
15
+ }, z.core.$strict>, z.ZodObject<{
16
+ type: z.ZodLiteral<"fit">;
17
+ }, z.core.$strict>], "type">;
18
+ export type CanvasCommand = z.infer<typeof canvasCommandSchema>;
19
+ export type CanvasCommandSubscriber = (execute: (command: CanvasCommand) => void) => () => void;
@@ -11468,6 +11468,26 @@ var ControlClient = class {
11468
11468
  method: "POST"
11469
11469
  }));
11470
11470
  }
11471
+ async broadcastCanvas(flowId, command) {
11472
+ const source = record$1(await this.request(`/v1/flows/${segment(flowId)}/canvas`, {
11473
+ method: "POST",
11474
+ body: JSON.stringify({
11475
+ version: 1,
11476
+ command
11477
+ })
11478
+ }));
11479
+ exact(source, [
11480
+ "version",
11481
+ "flowId",
11482
+ "broadcast"
11483
+ ]);
11484
+ if (source.version != 1 || source.broadcast !== true || source.flowId !== flowId) return invalidResponse();
11485
+ return {
11486
+ version: 1,
11487
+ flowId: string(source.flowId),
11488
+ broadcast: true
11489
+ };
11490
+ }
11471
11491
  async getFlow(flowId) {
11472
11492
  return flow(await this.request(`/v1/flows/${segment(flowId)}`));
11473
11493
  }
@@ -1,3 +1,5 @@
1
+ import type { CanvasCommand } from './canvas-commands.js';
2
+ export type { CanvasCommand, CanvasCommandSubscriber } from './canvas-commands.js';
1
3
  import type { LiveDetails } from './liveDetails.js';
2
4
  export { liveEntries } from './liveDetails.js';
3
5
  export type { LiveDetails, LiveEntry } from './liveDetails.js';
@@ -30,6 +32,11 @@ export type { JsonValue, TriggerKeySnapshot, WaitAction } from '../browser/flow-
30
32
  export type { RunStatus } from './run-lifecycle.js';
31
33
  export { ApiError, controlErrorCode, controlErrorMetadata, type ControlErrorCode } from './control-api-errors.js';
32
34
  export type { FlowCatalogEvent, FlowChangeEvent } from './flow-notifications.js';
35
+ export interface CanvasBroadcast {
36
+ readonly version: 1;
37
+ readonly flowId: string;
38
+ readonly broadcast: true;
39
+ }
33
40
  export type ControlRequest = (path: string, init?: RequestInit) => Promise<Response>;
34
41
  export interface ErrorListener {
35
42
  readonly flowId: string;
@@ -791,6 +798,7 @@ export declare class ControlClient {
791
798
  readonly idempotencyKey?: string;
792
799
  }): Promise<Flow>;
793
800
  createFlow(name: string, idempotencyKey?: string, teamId?: string): Promise<Flow>;
801
+ broadcastCanvas(flowId: string, command: CanvasCommand): Promise<CanvasBroadcast>;
794
802
  getFlow(flowId: string): Promise<Flow>;
795
803
  getErrorListeners(flowId: string, signal?: AbortSignal): Promise<readonly ErrorListener[]>;
796
804
  renameFlow(flowId: string, name: string): Promise<Flow>;
@@ -12124,6 +12124,26 @@ var ControlClient = class {
12124
12124
  method: "POST"
12125
12125
  }));
12126
12126
  }
12127
+ async broadcastCanvas(flowId, command) {
12128
+ const source = record(await this.request(`/v1/flows/${segment(flowId)}/canvas`, {
12129
+ method: "POST",
12130
+ body: JSON.stringify({
12131
+ version: 1,
12132
+ command
12133
+ })
12134
+ }));
12135
+ exact(source, [
12136
+ "version",
12137
+ "flowId",
12138
+ "broadcast"
12139
+ ]);
12140
+ if (source.version != 1 || source.broadcast !== true || source.flowId !== flowId) return invalidResponse();
12141
+ return {
12142
+ version: 1,
12143
+ flowId: string(source.flowId),
12144
+ broadcast: true
12145
+ };
12146
+ }
12127
12147
  async getFlow(flowId) {
12128
12148
  return flow(await this.request(`/v1/flows/${segment(flowId)}`));
12129
12149
  }
@@ -1,3 +1,4 @@
1
+ export { canvasCommandSchema } from './canvas-commands.js';
1
2
  export * from './authoring.js';
2
3
  import type { JsonValue } from '../browser/flow-change.js';
3
4
  import type { DraftOperation } from './draftOperations.js';
@@ -9,6 +10,22 @@ export declare const waitActionBodySchema: {
9
10
  };
10
11
  };
11
12
  export declare const controlRequests: {
13
+ canvas: (value: unknown) => {
14
+ command: {
15
+ type: "focus";
16
+ nodeId: string;
17
+ } | {
18
+ type: "zoom";
19
+ zoom: number;
20
+ } | {
21
+ type: "pan";
22
+ dx: number;
23
+ dy: number;
24
+ } | {
25
+ type: "fit";
26
+ };
27
+ version: 1;
28
+ };
12
29
  authoringCheck: (value: unknown) => {
13
30
  revisionId: string;
14
31
  };
@@ -5852,6 +5852,23 @@ function preprocess(fn, schema) {
5852
5852
  out: schema
5853
5853
  });
5854
5854
  }
5855
+ /** Transient operations on each connected canvas's own viewport. */
5856
+ var canvasCommandSchema = discriminatedUnion("type", [
5857
+ strictObject({
5858
+ type: literal("focus"),
5859
+ nodeId: string().min(1)
5860
+ }),
5861
+ strictObject({
5862
+ type: literal("zoom"),
5863
+ zoom: number().min(.1).max(3)
5864
+ }),
5865
+ strictObject({
5866
+ type: literal("pan"),
5867
+ dx: number(),
5868
+ dy: number()
5869
+ }),
5870
+ strictObject({ type: literal("fit") })
5871
+ ]);
5855
5872
  //#endregion
5856
5873
  //#region ../../node_modules/.bun/dequal@2.0.3/node_modules/dequal/lite/index.mjs
5857
5874
  var has = Object.prototype.hasOwnProperty;
@@ -14012,6 +14029,10 @@ var trigger = strictObject({
14012
14029
  outputs: record$3(string(), json)
14013
14030
  });
14014
14031
  var schemas = {
14032
+ canvas: strictObject({
14033
+ command: canvasCommandSchema,
14034
+ version
14035
+ }),
14015
14036
  authoringCheck: authoringCheckSchema,
14016
14037
  authoringEdit: authoringRequestSchema,
14017
14038
  authoringRead: authoringReadSchema,
@@ -14123,6 +14144,7 @@ function decoder(schema) {
14123
14144
  return (value) => schema.parse(value);
14124
14145
  }
14125
14146
  var controlRequests = {
14147
+ canvas: decoder(schemas.canvas),
14126
14148
  authoringCheck: decoder(schemas.authoringCheck),
14127
14149
  authoringEdit: decoder(schemas.authoringEdit),
14128
14150
  authoringRead: decoder(schemas.authoringRead),
@@ -14171,4 +14193,4 @@ function controlRequestSchema(name) {
14171
14193
  return schema;
14172
14194
  }
14173
14195
  //#endregion
14174
- export { AuthoringError, authoringCheckResponse, authoringCheckSchema, authoringConfigurations, authoringDiagnosticSchema, authoringDiagnostics, authoringEditResponse, authoringEditSchema, authoringExample, authoringExamples, authoringNode, authoringReadResponse, authoringReadSchema, authoringRequestContract, authoringRequestSchema, authoringSchema, authoringSchemaQuery, authoringSearchResponse, authoringSearchSchema, authoringSourceSchema, authoringTypes, compileAuthoring, controlRequestSchema, controlRequests, decodeDraftOperations, draftOperationsSchema, readAuthoring, resolveDraftOperations, searchAuthoring, waitActionBodySchema };
14196
+ export { AuthoringError, authoringCheckResponse, authoringCheckSchema, authoringConfigurations, authoringDiagnosticSchema, authoringDiagnostics, authoringEditResponse, authoringEditSchema, authoringExample, authoringExamples, authoringNode, authoringReadResponse, authoringReadSchema, authoringRequestContract, authoringRequestSchema, authoringSchema, authoringSchemaQuery, authoringSearchResponse, authoringSearchSchema, authoringSourceSchema, authoringTypes, canvasCommandSchema, compileAuthoring, controlRequestSchema, controlRequests, decodeDraftOperations, draftOperationsSchema, readAuthoring, resolveDraftOperations, searchAuthoring, waitActionBodySchema };
@@ -1,3 +1,4 @@
1
+ import type { CanvasCommand } from '../common/canvas-commands.js';
1
2
  export type FlowCatalogEvent = {
2
3
  readonly kind: 'flows.changed';
3
4
  readonly version: 1;
@@ -7,6 +8,11 @@ export type FlowCatalogEvent = {
7
8
  readonly version: 1;
8
9
  };
9
10
  export type FlowChangeEvent = {
11
+ readonly kind: 'canvas.command';
12
+ readonly flowId: string;
13
+ readonly command: CanvasCommand;
14
+ readonly version: 1;
15
+ } | {
10
16
  readonly accessRevision: number;
11
17
  readonly flowId: string;
12
18
  readonly kind: 'access.changed';
@@ -3,6 +3,48 @@ import { z } from 'zod';
3
3
  export declare const mcpProtocolVersion = "2026-07-28";
4
4
  export declare const mcpInstructions: string;
5
5
  export declare const mcpTools: {
6
+ flow_canvas: {
7
+ description: string;
8
+ annotations: {
9
+ readOnlyHint: boolean;
10
+ destructiveHint: boolean;
11
+ idempotentHint: boolean;
12
+ openWorldHint: boolean;
13
+ };
14
+ inputSchema: {
15
+ '~standard': {
16
+ version: 1;
17
+ vendor: string;
18
+ jsonSchema: {
19
+ input: () => Record<string, unknown>;
20
+ output: () => Record<string, unknown>;
21
+ };
22
+ validate(value: unknown): {
23
+ value: {
24
+ flowId: string;
25
+ command: {
26
+ type: "focus";
27
+ nodeId: string;
28
+ } | {
29
+ type: "zoom";
30
+ zoom: number;
31
+ } | {
32
+ type: "pan";
33
+ dx: number;
34
+ dy: number;
35
+ } | {
36
+ type: "fit";
37
+ };
38
+ };
39
+ } | {
40
+ issues: {
41
+ message: string;
42
+ path: string[];
43
+ }[];
44
+ };
45
+ };
46
+ };
47
+ };
6
48
  flow_live: {
7
49
  description: string;
8
50
  annotations: {
@@ -5851,6 +5851,23 @@ function preprocess(fn, schema) {
5851
5851
  out: schema
5852
5852
  });
5853
5853
  }
5854
+ /** Transient operations on each connected canvas's own viewport. */
5855
+ var canvasCommandSchema = discriminatedUnion("type", [
5856
+ strictObject({
5857
+ type: literal("focus"),
5858
+ nodeId: string$1().min(1)
5859
+ }),
5860
+ strictObject({
5861
+ type: literal("zoom"),
5862
+ zoom: number().min(.1).max(3)
5863
+ }),
5864
+ strictObject({
5865
+ type: literal("pan"),
5866
+ dx: number(),
5867
+ dy: number()
5868
+ }),
5869
+ strictObject({ type: literal("fit") })
5870
+ ]);
5854
5871
  //#endregion
5855
5872
  //#region ../../node_modules/.bun/dequal@2.0.3/node_modules/dequal/lite/index.mjs
5856
5873
  var has = Object.prototype.hasOwnProperty;
@@ -11670,6 +11687,26 @@ var ControlClient = class {
11670
11687
  method: "POST"
11671
11688
  }));
11672
11689
  }
11690
+ async broadcastCanvas(flowId, command) {
11691
+ const source = record(await this.request(`/v1/flows/${segment(flowId)}/canvas`, {
11692
+ method: "POST",
11693
+ body: JSON.stringify({
11694
+ version: 1,
11695
+ command
11696
+ })
11697
+ }));
11698
+ exact(source, [
11699
+ "version",
11700
+ "flowId",
11701
+ "broadcast"
11702
+ ]);
11703
+ if (source.version != 1 || source.broadcast !== true || source.flowId !== flowId) return invalidResponse();
11704
+ return {
11705
+ version: 1,
11706
+ flowId: string(source.flowId),
11707
+ broadcast: true
11708
+ };
11709
+ }
11673
11710
  async getFlow(flowId) {
11674
11711
  return flow$1(await this.request(`/v1/flows/${segment(flowId)}`));
11675
11712
  }
@@ -12390,14 +12427,14 @@ var mutationKey = id.max(256).describe("Stable identity for this mutation. Retry
12390
12427
  var pageLimit = int().min(1).max(100).default(50);
12391
12428
  var flow = id.describe("Exact Flow ID returned by flow_list or flow_create.");
12392
12429
  var run = id.describe("Exact Run ID returned by flow_run or run_list.");
12393
- var mcpInstructions = "Use flow_list, flow_read and flow_search to inspect root workflows. Read node type schemas with flow_schema. Use flow_edit with the observed baseRevision and a stable requestId. Node edits affect only the selected node. Text edits require exactly one match. Execution edges and input sources are independent. Legacy Subflow content is unsupported. Edits can save incomplete drafts; inspect returned diagnostics and use flow_check before running. Use flow_check before flow_run. Select an explicit Trigger node ID and fixed revision or publication. A new Flow has no Trigger until you add one. Before adding a Feishu Trigger, use event_source_list and compare its Connection IDs with connector_connections in the Flow scope. If no source is available, create and verify one in Workbench. Use flow_publish to publish a fixed Revision, then poll flow_publish_status until succeeded or failed. Use flow_live to inspect current online entries after publication. Use flow_set_enabled to enable or disable the observed Live publication. flow_run returns an accepted Run, not its final result. Find Runs with run_list, poll run_get and use run_result after terminal. Resolve a waiting Run only with an explicit run_resolve_wait action allowed by run_get. Use run_results and run_result_read to inspect stored Agent tool results; these are separate from the terminal Run result. Retry flow_edit with identical arguments and the same requestId. Retry flow_create, flow_import, flow_publish and flow_run only with identical arguments and the same idempotencyKey; a new key can execute the Flow again. Other mutations use the same resource identity and arguments when retrying. Cancel a Run explicitly with run_cancel. Use nextCursor and nextAfter to read subsequent pages. Connector-backed deployments may require a Team; inspect connector_teams before creating a Flow.";
12394
- function tool(description, schema, readOnly) {
12430
+ var mcpInstructions = "Use flow_list, flow_read and flow_search to inspect root workflows. Read node type schemas with flow_schema. Use flow_edit with the observed baseRevision and a stable requestId. Node edits affect only the selected node. Text edits require exactly one match. Execution edges and input sources are independent. Legacy Subflow content is unsupported. Edits can save incomplete drafts; inspect returned diagnostics and use flow_check before running. Use flow_check before flow_run. Select an explicit Trigger node ID and fixed revision or publication. A new Flow has no Trigger until you add one. Before adding a Feishu Trigger, use event_source_list and compare its Connection IDs with connector_connections in the Flow scope. If no source is available, create and verify one in Workbench. Use flow_publish to publish a fixed Revision, then poll flow_publish_status until succeeded or failed. Use flow_live to inspect current online entries after publication. Use flow_set_enabled to enable or disable the observed Live publication. flow_run returns an accepted Run, not its final result. Find Runs with run_list, poll run_get and use run_result after terminal. Resolve a waiting Run only with an explicit run_resolve_wait action allowed by run_get. Use run_results and run_result_read to inspect stored Agent tool results; these are separate from the terminal Run result. Retry flow_edit with identical arguments and the same requestId. Retry flow_create, flow_import, flow_publish and flow_run only with identical arguments and the same idempotencyKey; a new key can execute the Flow again. Canvas commands are transient broadcasts without execution confirmation; never automatically retry flow_canvas because pan is relative. Other mutations use the same resource identity and arguments when retrying. Cancel a Run explicitly with run_cancel. Use nextCursor and nextAfter to read subsequent pages. Connector-backed deployments may require a Team; inspect connector_teams before creating a Flow.";
12431
+ function tool(description, schema, readOnly, idempotent = true) {
12395
12432
  return {
12396
12433
  description,
12397
12434
  annotations: {
12398
12435
  readOnlyHint: readOnly,
12399
12436
  destructiveHint: !readOnly,
12400
- idempotentHint: true,
12437
+ idempotentHint: idempotent,
12401
12438
  openWorldHint: true
12402
12439
  },
12403
12440
  inputSchema: { "~standard": {
@@ -12418,6 +12455,10 @@ function tool(description, schema, readOnly) {
12418
12455
  };
12419
12456
  }
12420
12457
  var mcpTools = {
12458
+ flow_canvas: tool("Broadcast a transient canvas command to connected Workbenches viewing this Flow. Success only confirms Server broadcast, not delivery or execution. No replay, session selection or Presentation save. Pan uses screen pixels (positive right/down); zoom is an absolute ratio (0.1–3); fit includes all nodes. Repeating pan moves again.", strictObject({
12459
+ flowId: flow,
12460
+ command: canvasCommandSchema
12461
+ }), false, false),
12421
12462
  flow_live: tool("Read the current published Flow, enablement and online entries, including Webhook URLs and request fields. Draft changes do not affect this view. An unpublished Flow returns not-published with no entries.", strictObject({ flowId: flow }), true),
12422
12463
  flow_list: tool("List Flows in this deployment. Continue with nextCursor; cursors are compatible with the Control API.", strictObject({
12423
12464
  cursor: id.optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oomol-lab/open-flow",
3
- "version": "0.1.0-beta.63",
3
+ "version": "0.1.0-beta.64",
4
4
  "description": "TypeScript contracts and Browser runtime for Open Flow.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -410,3 +410,32 @@ current online entries. The MCP equivalent is `flow_live({ flowId })`.
410
410
  A useful completion report identifies the Flow and Revision, what changed,
411
411
  what was actually checked or executed, and any remaining blocker. Its detail
412
412
  should match the task.
413
+
414
+ ## Workbench node annotations
415
+
416
+ Node annotations from the Codex browser show the node title as the annotation name.
417
+ Metadata contains `flowId`, `revisionId`, `nodeId`, and `syncState`, plus `description`
418
+ when nonempty. The identity fields are never truncated; `revisionId` identifies the
419
+ saved Revision. Descriptions may be shortened with an ellipsis to fit the annotation
420
+ limits; read full content
421
+ through the public API. The browser supplies the source page URL separately. Continue
422
+ using the configured deployment and verify it matches the referenced Flow.
423
+ Read the referenced node with the public read command before acting. Before editing,
424
+ read the current Draft and use its revision checks; a captured annotation may be older
425
+ than the current Draft. If the node no longer exists, report that instead of choosing
426
+ another node by name.
427
+
428
+ `syncState` describes the Workbench's known save state (`saved`, `dirty`, `saving`, or
429
+ `failed`). The revision always identifies saved data; pending local changes, including
430
+ unsaved code, are not part of that snapshot. An annotation does not save those changes
431
+ or establish a CLI/MCP connection. Missing or invalid metadata is not sufficient to
432
+ identify a node for editing.
433
+
434
+ A multi-node annotation carries the same `flowId`, `revisionId`, and `syncState`,
435
+ with `nodeIds` instead of `nodeId`. Parse `nodeIds` as a JSON-encoded string array
436
+ and read every referenced node. The group contains business nodes and Triggers;
437
+ Comment cards have no authoring node identity and are not included in this feature.
438
+ If the complete list exceeds the annotation limits, metadata contains `nodeCount`
439
+ and `error` instead of `nodeIds`. Ask for a smaller selection; never infer a partial
440
+ list or treat a missing list as the whole Flow. The annotation captures the selection
441
+ at creation time, not whichever nodes happen to be selected when the agent acts.