infaira-canvas 0.1.10 → 0.2.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 CHANGED
@@ -11,6 +11,13 @@ CLI toolkit for building and publishing widgets for the **InfAIra Canvas (ICan)*
11
11
  1. **Scaffold** a new widget project with all required files
12
12
  2. **Upload** a built widget bundle to an ICan portal
13
13
 
14
+ Widgets scaffolded with this CLI can call:
15
+
16
+ - **ICan native actions** — `icanContext.executeAction('Buildings', 'List', { siteId })`
17
+ - **Mythos workflow actions** — `icanContext.executeAction('mythos:Inventory', 'UpdateStock', { sku, qty })`
18
+
19
+ The only difference at the call site is the `mythos:` prefix on the model name. The ICan backend handles routing (set `MYTHOS_BASE_URL` on the backend to enable). See **section 2a** of the bundled `ICan-Widget-Development-Guide.md` for the full Mythos integration walkthrough.
20
+
14
21
  ---
15
22
 
16
23
  ## Installation
@@ -294,14 +294,35 @@ export interface IActionOptions {
294
294
  executeImmediately?: boolean;
295
295
  }
296
296
 
297
+ /** A Mythos action published and callable from this widget. Returned by
298
+ * IContextProvider.listMythosActions. Invoke one via
299
+ * executeAction('mythos:' + model_name, action_name, params). */
300
+ export interface IMythosAction {
301
+ model_id: string;
302
+ model_name: string;
303
+ action_id: string;
304
+ action_name: string;
305
+ description?: string;
306
+ capability?: string;
307
+ }
308
+
297
309
  export interface IContextProvider {
298
310
  environment: 'dev' | 'prod';
299
311
  orchUrl?: string;
300
312
  userKey: string;
301
313
  root: string;
302
314
  scriptFiles?: string[];
315
+ /** Invoke a model action.
316
+ * - executeAction('Buildings', 'List', { siteId }) → ICan native
317
+ * - executeAction('mythos:Inventory', 'UpdateStock', p) → Mythos engine
318
+ * The 'mythos:' prefix is the only difference at the call site; the
319
+ * ICan backend routes the call appropriately. */
303
320
  executeAction(model: string, action: string, parameters: unknown, options?: IActionOptions): Promise<unknown>;
304
321
  executeService(app: string, service: string, parameters: unknown, options?: IActionOptions): Promise<unknown>;
322
+ /** List Mythos actions callable via executeAction('mythos:...', ...).
323
+ * Optional — present only when MYTHOS_BASE_URL is configured on the ICan
324
+ * backend. Treat absence as "Mythos integration disabled". */
325
+ listMythosActions?(): Promise<IMythosAction[]>;
305
326
  fireEvent(eventId: string): Promise<void>;
306
327
  hasAppRole(app: string, role: string): boolean;
307
328
  themeName?: string;
package/dist/index.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "infaira-canvas",
3
- "version": "0.1.10",
4
- "description": "InfAIra Canvas CLI — Widget development toolkit for InfAIra Canvas",
3
+ "version": "0.2.0",
4
+ "description": "InfAIra Canvas CLI — scaffold widgets that talk to ICan and Mythos.",
5
5
  "keywords": [
6
6
  "infaira",
7
7
  "ican",
8
+ "mythos",
8
9
  "widget",
9
10
  "cli",
10
- "scaffold"
11
+ "scaffold",
12
+ "workflow"
11
13
  ],
12
14
  "homepage": "https://infaira.co",
13
15
  "license": "MIT",
@@ -38,7 +38,8 @@ In the portal, `icanContext` is always defined — the widget is never mounted u
38
38
  | `language` | `string` | Active language code, e.g. `'en'` |
39
39
  | `themeName` | `string \| undefined` | e.g. `'Dark'`, `'Glass Light'` |
40
40
  | `themeType` | `'Dark' \| 'Light' \| 'Glass-Dark' \| 'Glass-Light' \| undefined` | Structured theme type for chart colours |
41
- | `executeAction` | `(model, action, params?, options?) => Promise<unknown>` | Call an Orch model |
41
+ | `executeAction` | `(model, action, params?, options?) => Promise<unknown>` | Call an action. ICan native by default; prefix `model` with `mythos:` to route to the Mythos engine instead. |
42
+ | `listMythosActions` | `() => Promise<IMythosAction[]> \| undefined` | List Mythos actions callable from this widget. Present only when the ICan backend has `MYTHOS_BASE_URL` configured. |
42
43
  | `fireEvent` | `(eventId) => Promise<void>` | Trigger a portal event |
43
44
  | `hasAppRole` | `(app, role) => boolean` | Check if the user has a role |
44
45
  | `$L` | `(code, params?) => string` | Translate a localisation key |
@@ -73,6 +74,59 @@ The dev harness always sets `environment: 'dev'`. The portal always sets `enviro
73
74
 
74
75
  ---
75
76
 
77
+ ## 2a. Calling Mythos actions — the `mythos:` prefix
78
+
79
+ ICan widgets can invoke actions running in the Mythos workflow engine through the exact same `executeAction` call, just by prefixing the `model` argument with `mythos:`. The host backend inspects the prefix and routes the request to the Mythos engine via the shared JWT.
80
+
81
+ ```tsx
82
+ // 1. Discover what's available (call once at mount and cache)
83
+ const actions = await icanContext.listMythosActions?.();
84
+ // → [{ model_id, model_name: 'Inventory', action_id, action_name: 'UpdateStock', description }]
85
+
86
+ // 2. Invoke a Mythos action — only the `mythos:` prefix differs from a
87
+ // native ICan call
88
+ const result = await icanContext.executeAction(
89
+ 'mythos:Inventory', // <- the prefix is what triggers Mythos routing
90
+ 'UpdateStock',
91
+ { sku: 'ABC-123', qty: 5 },
92
+ );
93
+ // → { run_id: '...', status: 'ok', output?: {...} }
94
+ ```
95
+
96
+ ### When to use Mythos vs native
97
+
98
+ | Use Mythos when… | Use native ICan when… |
99
+ |---|---|
100
+ | The logic is a multi-step workflow with branches, retries, or external calls | The call is a direct read/write to a backing service (Locations, Assets, etc.) |
101
+ | You want the call recorded in Mythos's execution log + debugger | You don't need step-level tracing |
102
+ | Multiple widgets need to call the same business operation | The operation is widget-local |
103
+ | You want to publish the same logic over a REST endpoint too | The widget owns the orchestration |
104
+
105
+ ### Failure modes to handle
106
+
107
+ `executeAction('mythos:...')` can reject with:
108
+
109
+ - **`mythos integration not configured`** — the ICan backend doesn't have `MYTHOS_BASE_URL` set. Surface this to the user and fall back gracefully.
110
+ - **`mythos model "X" not found or not published`** — the action either doesn't exist or isn't toggled `published: true` in Mythos.
111
+ - **HTTP / network errors** — same as any `executeAction` call.
112
+
113
+ `listMythosActions` is also `undefined` when integration is disabled — so guard with `?.()`.
114
+
115
+ ```tsx
116
+ const handleRun = async () => {
117
+ try {
118
+ const r = await icanContext.executeAction('mythos:Inventory', 'UpdateStock', { sku, qty });
119
+ setResult(r);
120
+ } catch (e) {
121
+ setError(e instanceof Error ? e.message : 'Failed');
122
+ }
123
+ };
124
+ ```
125
+
126
+ A working reference widget lives at `Widgets/ICan widgets/mythosdemo/` — pick an action, fill params, run, render the response.
127
+
128
+ ---
129
+
76
130
  ## 3. Widget settings panel — `IWidgetPropConfig`
77
131
 
78
132
  Widgets can expose a settings form in the portal. Users open it by clicking the ⚙ button on a widget cell in edit mode. You define the fields in `registerWidget`:
@@ -594,7 +594,10 @@ interface IContextProvider {
594
594
  orchUrl?: string;
595
595
  apiKey?: string;
596
596
 
597
- // Fetch data from Orch
597
+ // Invoke an action. Prefix model with `mythos:` to route to the Mythos
598
+ // workflow engine instead of the default ICan native pipeline:
599
+ // executeAction('Buildings', 'List', { siteId }) → ICan native
600
+ // executeAction('mythos:Inventory', 'UpdateStock', { ... }) → Mythos engine
598
601
  executeAction(
599
602
  model: string,
600
603
  action: string,
@@ -602,6 +605,16 @@ interface IContextProvider {
602
605
  options?: IActionOptions
603
606
  ): Promise<unknown>;
604
607
 
608
+ // List Mythos actions callable via executeAction('mythos:...', ...).
609
+ // Optional — present only when MYTHOS_BASE_URL is set on the ICan backend.
610
+ listMythosActions?(): Promise<Array<{
611
+ model_id: string;
612
+ model_name: string;
613
+ action_id: string;
614
+ action_name: string;
615
+ description?: string;
616
+ }>>;
617
+
605
618
  executeService(
606
619
  app: string,
607
620
  service: string,
@@ -701,17 +701,51 @@ declare module "ican/context" {
701
701
  email?: string;
702
702
  roles?: string[];
703
703
  };
704
- /** Invoke a registered model action on this dashboard. */
704
+ /**
705
+ * Invoke a model action on this dashboard.
706
+ *
707
+ * Routing happens server-side by inspecting `model`:
708
+ * - `executeAction('Buildings', 'List', { siteId })`
709
+ * → ICan native action (existing Orch pipeline).
710
+ * - `executeAction('mythos:Inventory', 'UpdateStock', { sku, qty })`
711
+ * → routed to the Mythos engine via the `mythos:` prefix. Resolves
712
+ * to the published Mythos action with that (model_name, action_name)
713
+ * and POSTs to `/api/models/{id}/actions/{id}/run`.
714
+ *
715
+ * Both call shapes return a Promise that resolves to the action's output
716
+ * envelope. For Mythos, the envelope is `{ run_id, status, output? }`.
717
+ */
705
718
  executeAction(
706
719
  model: string,
707
720
  action: string,
708
721
  params?: Record<string, unknown>
709
722
  ): Promise<unknown>;
723
+ /**
724
+ * List every Mythos action published and callable from this widget.
725
+ * Optional — present only when the ICan backend has MYTHOS_BASE_URL set.
726
+ * Widgets that build action pickers (settings panels, etc.) call this
727
+ * once at mount and cache the result themselves.
728
+ */
729
+ listMythosActions?: () => Promise<IMythosAction[]>;
710
730
  /** Resolve a relative ICan backend path against the portal base URL. */
711
731
  resolveUrl(path: string): string;
712
732
  /** Logging helper namespaced to this widget instance. */
713
733
  log(level: "info" | "warn" | "error", ...args: unknown[]): void;
714
734
  }
735
+
736
+ /**
737
+ * A single Mythos action published as callable from a widget. Returned by
738
+ * `icanContext.listMythosActions()`. To invoke one, call
739
+ * `icanContext.executeAction('mythos:' + model_name, action_name, params)`.
740
+ */
741
+ export interface IMythosAction {
742
+ model_id: string;
743
+ model_name: string;
744
+ action_id: string;
745
+ action_name: string;
746
+ description?: string;
747
+ capability?: string;
748
+ }
715
749
  }
716
750
 
717
751
  // ─── Global runtime declarations ─────────────────────────────────────────────
@@ -1419,13 +1419,30 @@
1419
1419
  orchUrl: localStorage.getItem(LS_ORCH_URL) || '',
1420
1420
  apiKey: localStorage.getItem(LS_API_KEY) || '',
1421
1421
  executeAction: function (model, action, params) {
1422
- console.log('[ICan Dev] executeAction:', model, action, params);
1422
+ // Mythos-routed calls use the `mythos:<ModelName>` prefix in production.
1423
+ // In the dev harness we just log them distinctively so authors can see
1424
+ // whether their widget is targeting ICan native or Mythos.
1425
+ var isMythos = typeof model === 'string' && model.toLowerCase().indexOf('mythos:') === 0;
1426
+ console.log('[ICan Dev]' + (isMythos ? ' [Mythos]' : '') + ' executeAction:', model, action, params);
1427
+ if (isMythos) {
1428
+ // Mock the Mythos response envelope: { run_id, status, output }.
1429
+ return Promise.resolve({ run_id: 'dev-' + Date.now(), status: 'ok', output: {} });
1430
+ }
1423
1431
  return Promise.resolve([]);
1424
1432
  },
1425
1433
  executeService: function (app, service, params) {
1426
1434
  console.log('[ICan Dev] executeService:', app, service, params);
1427
1435
  return Promise.resolve(null);
1428
1436
  },
1437
+ listMythosActions: function () {
1438
+ // Dev stub — returns a couple of synthetic actions so widgets that
1439
+ // populate dropdowns from discovery don't render empty.
1440
+ console.log('[ICan Dev] listMythosActions');
1441
+ return Promise.resolve([
1442
+ { model_id: 'dev-model-1', model_name: 'Inventory', action_id: 'dev-action-1', action_name: 'UpdateStock', description: 'Dev stub' },
1443
+ { model_id: 'dev-model-2', model_name: 'Buildings', action_id: 'dev-action-2', action_name: 'List', description: 'Dev stub' },
1444
+ ]);
1445
+ },
1429
1446
  fireEvent: function (id) {
1430
1447
  console.log('[ICan Dev] fireEvent:', id);
1431
1448
  return Promise.resolve();
package/templates/ui.html CHANGED
@@ -1123,13 +1123,25 @@
1123
1123
  orchUrl: localStorage.getItem(LS_ORCH_URL) || '',
1124
1124
  apiKey: localStorage.getItem(LS_API_KEY) || '',
1125
1125
  executeAction: function (model, action, params) {
1126
- console.log('[ICan Dev] executeAction:', model, action, params);
1126
+ // Mythos-routed calls use the `mythos:<ModelName>` prefix.
1127
+ var isMythos = typeof model === 'string' && model.toLowerCase().indexOf('mythos:') === 0;
1128
+ console.log('[ICan Dev]' + (isMythos ? ' [Mythos]' : '') + ' executeAction:', model, action, params);
1129
+ if (isMythos) {
1130
+ return Promise.resolve({ run_id: 'dev-' + Date.now(), status: 'ok', output: {} });
1131
+ }
1127
1132
  return Promise.resolve([]);
1128
1133
  },
1129
1134
  executeService: function (app, service, params) {
1130
1135
  console.log('[ICan Dev] executeService:', app, service, params);
1131
1136
  return Promise.resolve(null);
1132
1137
  },
1138
+ listMythosActions: function () {
1139
+ console.log('[ICan Dev] listMythosActions');
1140
+ return Promise.resolve([
1141
+ { model_id: 'dev-model-1', model_name: 'Inventory', action_id: 'dev-action-1', action_name: 'UpdateStock', description: 'Dev stub' },
1142
+ { model_id: 'dev-model-2', model_name: 'Buildings', action_id: 'dev-action-2', action_name: 'List', description: 'Dev stub' },
1143
+ ]);
1144
+ },
1133
1145
  fireEvent: function (id) {
1134
1146
  console.log('[ICan Dev] fireEvent:', id);
1135
1147
  return Promise.resolve();