@revenexx/studio-integrations 0.1.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.
Files changed (106) hide show
  1. package/README.md +51 -0
  2. package/dist/module.d.mts +6 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +45 -0
  5. package/dist/runtime/components/Integrations/Layout.d.vue.ts +12 -0
  6. package/dist/runtime/components/Integrations/Layout.vue +20 -0
  7. package/dist/runtime/components/Integrations/Layout.vue.d.ts +12 -0
  8. package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +16 -0
  9. package/dist/runtime/components/Integrations/NodeInspector.vue +450 -0
  10. package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +16 -0
  11. package/dist/runtime/components/Integrations/NodePalette.d.vue.ts +2 -0
  12. package/dist/runtime/components/Integrations/NodePalette.vue +140 -0
  13. package/dist/runtime/components/Integrations/NodePalette.vue.d.ts +2 -0
  14. package/dist/runtime/components/Integrations/RunDetailDialog.d.vue.ts +11 -0
  15. package/dist/runtime/components/Integrations/RunDetailDialog.vue +169 -0
  16. package/dist/runtime/components/Integrations/RunDetailDialog.vue.d.ts +11 -0
  17. package/dist/runtime/components/Integrations/Sidebar.d.vue.ts +2 -0
  18. package/dist/runtime/components/Integrations/Sidebar.vue +32 -0
  19. package/dist/runtime/components/Integrations/Sidebar.vue.d.ts +2 -0
  20. package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +23 -0
  21. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +86 -0
  22. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +23 -0
  23. package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +14 -0
  24. package/dist/runtime/components/Integrations/WorkflowEditor.vue +419 -0
  25. package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +14 -0
  26. package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +11 -0
  27. package/dist/runtime/components/Integrations/WorkflowNodeView.vue +200 -0
  28. package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +11 -0
  29. package/dist/runtime/components/Integrations/WorkflowTriggerView.d.vue.ts +8 -0
  30. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +82 -0
  31. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue.d.ts +8 -0
  32. package/dist/runtime/components/Integrations/WorkflowsTable.d.vue.ts +6 -0
  33. package/dist/runtime/components/Integrations/WorkflowsTable.vue +268 -0
  34. package/dist/runtime/components/Integrations/WorkflowsTable.vue.d.ts +6 -0
  35. package/dist/runtime/components/Integrations/workflowLayout.d.ts +17 -0
  36. package/dist/runtime/components/Integrations/workflowLayout.js +29 -0
  37. package/dist/runtime/composables/integrationsApi.d.ts +13 -0
  38. package/dist/runtime/composables/integrationsApi.js +55 -0
  39. package/dist/runtime/composables/useCredentialTypes.d.ts +19 -0
  40. package/dist/runtime/composables/useCredentialTypes.js +47 -0
  41. package/dist/runtime/composables/useIntegrationsAudit.d.ts +26 -0
  42. package/dist/runtime/composables/useIntegrationsAudit.js +39 -0
  43. package/dist/runtime/composables/useIntegrationsCredentials.d.ts +43 -0
  44. package/dist/runtime/composables/useIntegrationsCredentials.js +98 -0
  45. package/dist/runtime/composables/useIntegrationsFocus.d.ts +16 -0
  46. package/dist/runtime/composables/useIntegrationsFocus.js +17 -0
  47. package/dist/runtime/composables/useIntegrationsNodes.d.ts +6 -0
  48. package/dist/runtime/composables/useIntegrationsNodes.js +32 -0
  49. package/dist/runtime/composables/useIntegrationsRuns.d.ts +31 -0
  50. package/dist/runtime/composables/useIntegrationsRuns.js +109 -0
  51. package/dist/runtime/composables/useIntegrationsSchedules.d.ts +14 -0
  52. package/dist/runtime/composables/useIntegrationsSchedules.js +32 -0
  53. package/dist/runtime/composables/useIntegrationsSecrets.d.ts +27 -0
  54. package/dist/runtime/composables/useIntegrationsSecrets.js +76 -0
  55. package/dist/runtime/composables/useIntegrationsTemplates.d.ts +19 -0
  56. package/dist/runtime/composables/useIntegrationsTemplates.js +141 -0
  57. package/dist/runtime/composables/useIntegrationsTriggers.d.ts +39 -0
  58. package/dist/runtime/composables/useIntegrationsTriggers.js +39 -0
  59. package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +28 -0
  60. package/dist/runtime/composables/useIntegrationsWorkflows.js +152 -0
  61. package/dist/runtime/data/integrationNodeCatalog.d.ts +41 -0
  62. package/dist/runtime/data/integrationNodeCatalog.js +67 -0
  63. package/dist/runtime/data/integrationRuns.d.ts +7 -0
  64. package/dist/runtime/data/integrationRuns.js +109 -0
  65. package/dist/runtime/data/integrationTemplates.d.ts +11 -0
  66. package/dist/runtime/data/integrationTemplates.js +9 -0
  67. package/dist/runtime/data/integrationWorkflowSamples.d.ts +18 -0
  68. package/dist/runtime/data/integrationWorkflowSamples.js +318 -0
  69. package/dist/runtime/pages/integrations/audit.d.vue.ts +2 -0
  70. package/dist/runtime/pages/integrations/audit.vue +156 -0
  71. package/dist/runtime/pages/integrations/audit.vue.d.ts +2 -0
  72. package/dist/runtime/pages/integrations/credentials/oauth-complete.d.vue.ts +2 -0
  73. package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +28 -0
  74. package/dist/runtime/pages/integrations/credentials/oauth-complete.vue.d.ts +2 -0
  75. package/dist/runtime/pages/integrations/credentials.d.vue.ts +2 -0
  76. package/dist/runtime/pages/integrations/credentials.vue +403 -0
  77. package/dist/runtime/pages/integrations/credentials.vue.d.ts +2 -0
  78. package/dist/runtime/pages/integrations/index.d.vue.ts +2 -0
  79. package/dist/runtime/pages/integrations/index.vue +226 -0
  80. package/dist/runtime/pages/integrations/index.vue.d.ts +2 -0
  81. package/dist/runtime/pages/integrations/runs.d.vue.ts +2 -0
  82. package/dist/runtime/pages/integrations/runs.vue +100 -0
  83. package/dist/runtime/pages/integrations/runs.vue.d.ts +2 -0
  84. package/dist/runtime/pages/integrations/schedules.d.vue.ts +2 -0
  85. package/dist/runtime/pages/integrations/schedules.vue +111 -0
  86. package/dist/runtime/pages/integrations/schedules.vue.d.ts +2 -0
  87. package/dist/runtime/pages/integrations/secrets.d.vue.ts +2 -0
  88. package/dist/runtime/pages/integrations/secrets.vue +248 -0
  89. package/dist/runtime/pages/integrations/secrets.vue.d.ts +2 -0
  90. package/dist/runtime/pages/integrations/workflows/[id].d.vue.ts +2 -0
  91. package/dist/runtime/pages/integrations/workflows/[id].vue +326 -0
  92. package/dist/runtime/pages/integrations/workflows/[id].vue.d.ts +2 -0
  93. package/dist/runtime/pages/integrations/workflows/index.d.vue.ts +2 -0
  94. package/dist/runtime/pages/integrations/workflows/index.vue +31 -0
  95. package/dist/runtime/pages/integrations/workflows/index.vue.d.ts +2 -0
  96. package/dist/runtime/pages/integrations/workflows/new.d.vue.ts +2 -0
  97. package/dist/runtime/pages/integrations/workflows/new.vue +153 -0
  98. package/dist/runtime/pages/integrations/workflows/new.vue.d.ts +2 -0
  99. package/dist/runtime/types/integrations.d.ts +427 -0
  100. package/dist/runtime/types/integrations.js +0 -0
  101. package/dist/runtime/utils/localize.d.ts +9 -0
  102. package/dist/runtime/utils/localize.js +21 -0
  103. package/dist/runtime/utils/statusStyle.d.ts +15 -0
  104. package/dist/runtime/utils/statusStyle.js +78 -0
  105. package/dist/types.d.mts +9 -0
  106. package/package.json +46 -0
@@ -0,0 +1,98 @@
1
+ import { computed } from "vue";
2
+ import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
3
+ export function useIntegrationsCredentials() {
4
+ const credentials = useState("integrations.credentials", () => []);
5
+ const loading = useState("integrations.credentials.loading", () => false);
6
+ const error = useState("integrations.credentials.error", () => null);
7
+ const loaded = useState("integrations.credentials.loaded", () => false);
8
+ const api = useIntegrationsApi();
9
+ function normalize(res) {
10
+ const obj = res;
11
+ if (Array.isArray(obj)) return obj;
12
+ if (Array.isArray(obj?.data)) return obj.data;
13
+ return [];
14
+ }
15
+ function unwrap(res) {
16
+ return res?.data ?? res;
17
+ }
18
+ function captureError(e, fallback) {
19
+ if (e instanceof IntegrationsApiError) return e.message;
20
+ return e?.message ?? fallback;
21
+ }
22
+ async function load(force = false) {
23
+ if (loading.value) return credentials.value;
24
+ if (loaded.value && !force) return credentials.value;
25
+ loading.value = true;
26
+ error.value = null;
27
+ try {
28
+ credentials.value = normalize(await api.get("/credentials"));
29
+ loaded.value = true;
30
+ } catch (e) {
31
+ error.value = captureError(e, "Could not load credentials.");
32
+ } finally {
33
+ loading.value = false;
34
+ }
35
+ return credentials.value;
36
+ }
37
+ function ofType(slug) {
38
+ return credentials.value.filter((c) => c.credential_type_slug === slug);
39
+ }
40
+ async function get(id) {
41
+ return unwrap(await api.get(`/credentials/${encodeURIComponent(id)}`));
42
+ }
43
+ async function create(input) {
44
+ const created = unwrap(await api.post("/credentials", input));
45
+ await load(true);
46
+ return created;
47
+ }
48
+ async function update(id, input) {
49
+ const updated = unwrap(await api.patch(`/credentials/${encodeURIComponent(id)}`, input));
50
+ await load(true);
51
+ return updated;
52
+ }
53
+ async function remove(id) {
54
+ await api.del(`/credentials/${encodeURIComponent(id)}`);
55
+ credentials.value = credentials.value.filter((c) => c.id !== id);
56
+ }
57
+ async function testInline(credentialTypeSlug, config) {
58
+ try {
59
+ const res = await api.post("/credentials/test", { credential_type_slug: credentialTypeSlug, config });
60
+ return res?.ok === false ? res : { ok: true };
61
+ } catch (e) {
62
+ return { ok: false, error: captureError(e, "Connection test failed.") };
63
+ }
64
+ }
65
+ async function test(id) {
66
+ try {
67
+ const res = await api.post(`/credentials/${encodeURIComponent(id)}/test`);
68
+ return res?.ok === false ? res : { ok: true };
69
+ } catch (e) {
70
+ return { ok: false, error: captureError(e, "Connection test failed.") };
71
+ }
72
+ }
73
+ async function oauthAuthorizeUrl(id) {
74
+ const res = await api.post(`/credentials/${encodeURIComponent(id)}/oauth/authorize-url`);
75
+ const authorizeUrl = res?.authorize_url ?? res?.data?.authorize_url;
76
+ if (!authorizeUrl) throw new IntegrationsApiError(0, res, "No authorize URL returned.");
77
+ return authorizeUrl;
78
+ }
79
+ const total = computed(() => credentials.value.length);
80
+ const needsReauth = computed(() => credentials.value.filter((c) => c.status === "needs_reauth"));
81
+ return {
82
+ credentials,
83
+ loading,
84
+ error,
85
+ loaded,
86
+ total,
87
+ needsReauth,
88
+ load,
89
+ ofType,
90
+ get,
91
+ create,
92
+ update,
93
+ remove,
94
+ testInline,
95
+ test,
96
+ oauthAuthorizeUrl
97
+ };
98
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Tracks whether the Integration Studio is in "focus mode" — i.e. the
3
+ * left sidebar (with `Integration Studio` header + sections) is hidden so
4
+ * the canvas can run edge-to-edge.
5
+ *
6
+ * Pages opt in by calling `useIntegrationsFocus().enable()` in onMounted
7
+ * and `disable()` in onUnmounted (we provide an `autoEnable()` helper
8
+ * that wires both).
9
+ */
10
+ export declare function useIntegrationsFocus(): {
11
+ focused: any;
12
+ enable: () => void;
13
+ disable: () => void;
14
+ toggle: () => void;
15
+ autoEnable: () => void;
16
+ };
@@ -0,0 +1,17 @@
1
+ export function useIntegrationsFocus() {
2
+ const focused = useState("integrations.focus", () => false);
3
+ function enable() {
4
+ focused.value = true;
5
+ }
6
+ function disable() {
7
+ focused.value = false;
8
+ }
9
+ function toggle() {
10
+ focused.value = !focused.value;
11
+ }
12
+ function autoEnable() {
13
+ onMounted(() => enable());
14
+ onUnmounted(() => disable());
15
+ }
16
+ return { focused, enable, disable, toggle, autoEnable };
17
+ }
@@ -0,0 +1,6 @@
1
+ export declare function useIntegrationsNodes(): {
2
+ loading: any;
3
+ error: any;
4
+ loaded: any;
5
+ load: (force?: boolean) => Promise<void>;
6
+ };
@@ -0,0 +1,32 @@
1
+ import { applyNodeCatalog } from "../data/integrationNodeCatalog.js";
2
+ import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
3
+ export function useIntegrationsNodes() {
4
+ const loading = useState("integrations.nodes.loading", () => false);
5
+ const error = useState("integrations.nodes.error", () => null);
6
+ const loaded = useState("integrations.nodes.loaded", () => false);
7
+ const api = useIntegrationsApi();
8
+ async function load(force = false) {
9
+ if (loading.value) return;
10
+ if (loaded.value && !force) return;
11
+ loading.value = true;
12
+ error.value = null;
13
+ try {
14
+ const res = await api.get("/nodes");
15
+ const list = Array.isArray(res) ? res : res?.data ?? [];
16
+ const manifests = list.filter((n) => n.manifest).map((n) => ({
17
+ ...n.manifest,
18
+ slug: n.manifest.slug ?? n.slug,
19
+ version: n.manifest.version ?? n.version,
20
+ name: n.manifest.name ?? n.name
21
+ }));
22
+ applyNodeCatalog(manifests);
23
+ loaded.value = true;
24
+ } catch (e) {
25
+ applyNodeCatalog([]);
26
+ error.value = e instanceof IntegrationsApiError ? e.message : e?.message ?? "Could not load nodes.";
27
+ } finally {
28
+ loading.value = false;
29
+ }
30
+ }
31
+ return { loading, error, loaded, load };
32
+ }
@@ -0,0 +1,31 @@
1
+ import type { RunStatus, WorkflowRun } from '../types/integrations.js';
2
+ export declare function useIntegrationsRuns(): {
3
+ runs: any;
4
+ loading: any;
5
+ error: any;
6
+ loaded: any;
7
+ total: import("vue").ComputedRef<any>;
8
+ failedCount: import("vue").ComputedRef<any>;
9
+ runningCount: import("vue").ComputedRef<any>;
10
+ load: (force?: boolean) => Promise<WorkflowRun[]>;
11
+ list: (filter?: {
12
+ workflowId?: number;
13
+ status?: RunStatus;
14
+ }) => WorkflowRun[];
15
+ get: (id: string) => WorkflowRun | null;
16
+ trigger: (workflowId: number | string, payload?: Record<string, unknown>, mode?: "sync" | "async") => Promise<unknown>;
17
+ getRun: (workflowId: number | string, runId: number | string) => Promise<unknown>;
18
+ getDetails: (workflowId: number | string, runId: number | string) => Promise<{
19
+ source: string;
20
+ data: Record<string, unknown>;
21
+ }>;
22
+ getHistory: (workflowId: number | string, runId: number | string, params?: {
23
+ page_token?: string;
24
+ page_size?: number;
25
+ }) => Promise<{
26
+ source: string;
27
+ data: unknown[];
28
+ next_page_token: string | null;
29
+ }>;
30
+ getStackTrace: (workflowId: number | string, runId: number | string) => Promise<string>;
31
+ };
@@ -0,0 +1,109 @@
1
+ import { computed } from "vue";
2
+ import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
3
+ function mapStatus(status) {
4
+ switch (status) {
5
+ case "completed":
6
+ return "succeeded";
7
+ case "pending":
8
+ return "queued";
9
+ case "terminated":
10
+ return "failed";
11
+ case "running":
12
+ return "running";
13
+ case "failed":
14
+ return "failed";
15
+ case "cancelled":
16
+ return "cancelled";
17
+ default:
18
+ return "running";
19
+ }
20
+ }
21
+ export function useIntegrationsRuns() {
22
+ const runs = useState("integrations.runs", () => []);
23
+ const loading = useState("integrations.runs.loading", () => false);
24
+ const error = useState("integrations.runs.error", () => null);
25
+ const loaded = useState("integrations.runs.loaded", () => false);
26
+ const api = useIntegrationsApi();
27
+ const { workflows, load: loadWorkflows } = useIntegrationsWorkflows();
28
+ function mapRun(r, workflowName) {
29
+ return {
30
+ id: String(r.id),
31
+ workflowId: r.workflow_id,
32
+ workflowName,
33
+ status: mapStatus(r.status),
34
+ triggerId: "",
35
+ triggerType: "manual",
36
+ startedAt: r.started_at ?? "",
37
+ finishedAt: r.completed_at ?? null,
38
+ durationMs: null,
39
+ steps: []
40
+ };
41
+ }
42
+ async function load(force = false) {
43
+ if (loading.value) return runs.value;
44
+ if (loaded.value && !force) return runs.value;
45
+ loading.value = true;
46
+ error.value = null;
47
+ try {
48
+ await loadWorkflows();
49
+ const perWorkflow = await Promise.allSettled(
50
+ workflows.value.map(async (wf) => {
51
+ const res = await api.get(`/workflows/${wf.id}/runs`);
52
+ return (res?.data ?? []).map((r) => mapRun(r, wf.name));
53
+ })
54
+ );
55
+ runs.value = perWorkflow.flatMap((p) => p.status === "fulfilled" ? p.value : []).sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
56
+ loaded.value = true;
57
+ } catch (e) {
58
+ error.value = e instanceof IntegrationsApiError ? e.message : e?.message ?? "Could not load runs.";
59
+ } finally {
60
+ loading.value = false;
61
+ }
62
+ return runs.value;
63
+ }
64
+ function list(filter) {
65
+ let result = [...runs.value];
66
+ if (filter?.workflowId !== void 0) result = result.filter((r) => r.workflowId === filter.workflowId);
67
+ if (filter?.status) result = result.filter((r) => r.status === filter.status);
68
+ return result.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
69
+ }
70
+ function get(id) {
71
+ return runs.value.find((r) => r.id === id) ?? null;
72
+ }
73
+ async function trigger(workflowId, payload = {}, mode) {
74
+ const body = { trigger_payload: payload };
75
+ if (mode) body.mode = mode;
76
+ const res = await api.post(`/workflows/${workflowId}/runs`, body);
77
+ return res?.data ?? res;
78
+ }
79
+ const getRun = (workflowId, runId) => api.get(`/workflows/${workflowId}/runs/${runId}`);
80
+ const getDetails = (workflowId, runId) => api.get(`/workflows/${workflowId}/runs/${runId}/details`);
81
+ const getHistory = (workflowId, runId, params = {}) => {
82
+ const qs = new URLSearchParams();
83
+ if (params.page_token) qs.set("page_token", params.page_token);
84
+ if (params.page_size) qs.set("page_size", String(params.page_size));
85
+ const suffix = qs.toString() ? `?${qs}` : "";
86
+ return api.get(`/workflows/${workflowId}/runs/${runId}/history${suffix}`);
87
+ };
88
+ const getStackTrace = (workflowId, runId) => api.get(`/workflows/${workflowId}/runs/${runId}/stack-trace`).then((r) => r.stack_trace);
89
+ const total = computed(() => runs.value.length);
90
+ const failedCount = computed(() => runs.value.filter((r) => r.status === "failed").length);
91
+ const runningCount = computed(() => runs.value.filter((r) => r.status === "running").length);
92
+ return {
93
+ runs,
94
+ loading,
95
+ error,
96
+ loaded,
97
+ total,
98
+ failedCount,
99
+ runningCount,
100
+ load,
101
+ list,
102
+ get,
103
+ trigger,
104
+ getRun,
105
+ getDetails,
106
+ getHistory,
107
+ getStackTrace
108
+ };
109
+ }
@@ -0,0 +1,14 @@
1
+ import type { ScheduleEntry } from '../types/integrations.js';
2
+ /**
3
+ * Derived view — every trigger of type `schedule` across all workflows,
4
+ * sourced from the real triggers sub-resource (useIntegrationsTriggers).
5
+ * Next/last-run timestamps are mocked deterministically (no telemetry
6
+ * endpoint yet).
7
+ */
8
+ export declare function useIntegrationsSchedules(): {
9
+ entries: import("vue").ComputedRef<ScheduleEntry[]>;
10
+ total: import("vue").ComputedRef<number>;
11
+ activeCount: import("vue").ComputedRef<number>;
12
+ loading: any;
13
+ load: any;
14
+ };
@@ -0,0 +1,32 @@
1
+ import { computed } from "vue";
2
+ export function useIntegrationsSchedules() {
3
+ const { all, loading, loadAll } = useIntegrationsTriggers();
4
+ const entries = computed(
5
+ () => all.value.filter((t) => t.type === "schedule").map((t) => {
6
+ const cfg = t.config ?? {};
7
+ const triggerId = String(t.id);
8
+ return {
9
+ id: `${t.workflow_id}:${t.id}`,
10
+ workflowId: t.workflow_id,
11
+ workflowName: t.workflowName,
12
+ triggerId,
13
+ cron: String(cfg.cron ?? "* * * * *"),
14
+ timezone: String(cfg.timezone ?? "UTC"),
15
+ active: t.active,
16
+ nextRunAt: t.active ? mockNextRun(t.workflow_id, triggerId) : null,
17
+ lastRunAt: t.active ? mockLastRun(t.workflow_id, triggerId) : null
18
+ };
19
+ }).sort((a, b) => a.cron.localeCompare(b.cron))
20
+ );
21
+ const total = computed(() => entries.value.length);
22
+ const activeCount = computed(() => entries.value.filter((e) => e.active).length);
23
+ return { entries, total, activeCount, loading, load: loadAll };
24
+ }
25
+ function mockNextRun(workflowId, triggerId) {
26
+ const offsetMinutes = (workflowId * 7 + triggerId.length * 3) % 720 + 5;
27
+ return new Date(Date.now() + offsetMinutes * 6e4).toISOString();
28
+ }
29
+ function mockLastRun(workflowId, triggerId) {
30
+ const offsetMinutes = (workflowId * 13 + triggerId.length * 11) % 720 + 30;
31
+ return new Date(Date.now() - offsetMinutes * 6e4).toISOString();
32
+ }
@@ -0,0 +1,27 @@
1
+ import type { SecretRecord } from '../types/integrations.js';
2
+ /**
3
+ * Per-tenant encrypted key/value store, backed by the Integrations service
4
+ * (`/api/v1/secrets`). The backend NEVER returns values — only keys +
5
+ * timestamps — so the list is keys-only by design.
6
+ *
7
+ * GET /secrets → list (keys + timestamps)
8
+ * POST /secrets → upsert { key, value }
9
+ * DELETE /secrets/{key} → remove
10
+ */
11
+ export declare function useIntegrationsSecrets(): {
12
+ secrets: any;
13
+ loading: any;
14
+ error: any;
15
+ loaded: any;
16
+ total: import("vue").ComputedRef<any>;
17
+ keys: import("vue").ComputedRef<any>;
18
+ isValidKey: (key: string) => boolean;
19
+ KEY_PATTERN: RegExp;
20
+ load: (force?: boolean) => Promise<SecretRecord[]>;
21
+ list: () => Promise<SecretRecord[]>;
22
+ upsert: (key: string, value: string) => Promise<{
23
+ record: SecretRecord;
24
+ created: boolean;
25
+ }>;
26
+ remove: (key: string) => Promise<boolean>;
27
+ };
@@ -0,0 +1,76 @@
1
+ import { computed } from "vue";
2
+ import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
3
+ const KEY_PATTERN = /^[A-Z0-9_]+$/;
4
+ export function useIntegrationsSecrets() {
5
+ const secrets = useState("integrations.secrets", () => []);
6
+ const loading = useState("integrations.secrets.loading", () => false);
7
+ const error = useState("integrations.secrets.error", () => null);
8
+ const loaded = useState("integrations.secrets.loaded", () => false);
9
+ const api = useIntegrationsApi();
10
+ function isValidKey(key) {
11
+ return KEY_PATTERN.test(key);
12
+ }
13
+ function normalize(res) {
14
+ const obj = res;
15
+ if (Array.isArray(obj?.keys)) return obj.keys.map((key) => ({ key, created_at: "", updated_at: "" }));
16
+ if (Array.isArray(obj?.data)) return obj.data;
17
+ if (Array.isArray(obj?.secrets)) return obj.secrets;
18
+ if (Array.isArray(res)) return res;
19
+ return [];
20
+ }
21
+ function captureError(e, fallback) {
22
+ if (e instanceof IntegrationsApiError) return e.message;
23
+ return e?.message ?? fallback;
24
+ }
25
+ async function load(force = false) {
26
+ if (loading.value) return secrets.value;
27
+ if (loaded.value && !force) return secrets.value;
28
+ loading.value = true;
29
+ error.value = null;
30
+ try {
31
+ secrets.value = normalize(await api.get("/secrets"));
32
+ loaded.value = true;
33
+ } catch (e) {
34
+ error.value = captureError(e, "Could not load secrets.");
35
+ } finally {
36
+ loading.value = false;
37
+ }
38
+ return secrets.value;
39
+ }
40
+ async function list() {
41
+ if (!loaded.value) await load();
42
+ return [...secrets.value].sort((a, b) => a.key.localeCompare(b.key));
43
+ }
44
+ async function upsert(key, value) {
45
+ if (!isValidKey(key)) {
46
+ throw new Error("Key must match ^[A-Z0-9_]+$");
47
+ }
48
+ const created = !secrets.value.some((s) => s.key === key);
49
+ await api.post("/secrets", { key, value });
50
+ await load(true);
51
+ const record = secrets.value.find((s) => s.key === key) ?? { key, created_at: (/* @__PURE__ */ new Date()).toISOString(), updated_at: (/* @__PURE__ */ new Date()).toISOString() };
52
+ return { record, created };
53
+ }
54
+ async function remove(key) {
55
+ const before = secrets.value.length;
56
+ await api.del(`/secrets/${encodeURIComponent(key)}`);
57
+ secrets.value = secrets.value.filter((s) => s.key !== key);
58
+ return secrets.value.length !== before;
59
+ }
60
+ const total = computed(() => secrets.value.length);
61
+ const keys = computed(() => secrets.value.map((s) => s.key).sort());
62
+ return {
63
+ secrets,
64
+ loading,
65
+ error,
66
+ loaded,
67
+ total,
68
+ keys,
69
+ isValidKey,
70
+ KEY_PATTERN,
71
+ load,
72
+ list,
73
+ upsert,
74
+ remove
75
+ };
76
+ }
@@ -0,0 +1,19 @@
1
+ import type { TemplateRequirements, WorkflowTemplate } from '../types/integrations.js';
2
+ export declare function useIntegrationsTemplates(): {
3
+ templates: any;
4
+ loaded: any;
5
+ loading: any;
6
+ error: any;
7
+ total: import("vue").ComputedRef<any>;
8
+ popular: import("vue").ComputedRef<any[]>;
9
+ load: (force?: boolean) => Promise<WorkflowTemplate[]>;
10
+ list: () => WorkflowTemplate[];
11
+ get: (slug: string) => WorkflowTemplate | null;
12
+ fetchDetail: (slug: string) => Promise<WorkflowTemplate | null>;
13
+ requirements: (slug: string) => Promise<TemplateRequirements | null>;
14
+ instantiate: (slug: string, opts?: {
15
+ name?: string;
16
+ credentialBindings?: Record<string, string>;
17
+ }) => Promise<number | null>;
18
+ install: (slug: string) => Promise<number | null>;
19
+ };
@@ -0,0 +1,141 @@
1
+ import { computed } from "vue";
2
+ import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
3
+ import { resolveLocalized } from "../utils/localize.js";
4
+ const BLOB_VERSION = "v0-draft";
5
+ const INDUSTRY_VALUES = ["mechanical-engineering", "chemicals", "wholesale", "manufacturing", "retail", "any"];
6
+ function publisherFor(slug, vendors) {
7
+ const ns = slug.includes(":") ? slug.split(":")[0] : vendors[0] ?? "revenexx";
8
+ const kind = ns === "revenexx" ? "revenexx" : "partner";
9
+ return { name: ns, kind };
10
+ }
11
+ function mapIndustries(arr) {
12
+ return (arr ?? []).filter((i) => INDUSTRY_VALUES.includes(i));
13
+ }
14
+ function emptyBlob(name) {
15
+ return { nodeManifestVersion: BLOB_VERSION, name, nodes: [], edges: [], triggerPositions: {}, triggers: [] };
16
+ }
17
+ function mapListItem(api) {
18
+ return {
19
+ slug: api.slug,
20
+ name: resolveLocalized(api.name),
21
+ description: resolveLocalized(api.description || api.shortDescription || ""),
22
+ category: api.category ?? "sync",
23
+ industries: mapIndustries(api.industries),
24
+ vendors: api.vendors ?? [],
25
+ requiredSecrets: [],
26
+ level: api.level ?? "beginner",
27
+ outline: [],
28
+ nodeCount: 0,
29
+ installs: 0,
30
+ publisher: publisherFor(api.slug, api.vendors ?? []),
31
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
32
+ blob: emptyBlob(resolveLocalized(api.name)),
33
+ fromApi: true
34
+ };
35
+ }
36
+ function applyDetail(base, api) {
37
+ const def = api.definition ?? {};
38
+ const triggers = (api.triggers ?? []).map((t) => ({
39
+ id: t.handle ?? String(t.id ?? ""),
40
+ type: t.type,
41
+ name: typeof t.name === "string" ? t.name : void 0,
42
+ active: t.active,
43
+ config: t.config ?? {}
44
+ }));
45
+ const nodes = def.nodes ?? [];
46
+ const blob = {
47
+ nodeManifestVersion: def.nodeManifestVersion ?? BLOB_VERSION,
48
+ name: def.name ?? base.name,
49
+ nodes,
50
+ edges: def.edges ?? [],
51
+ triggerPositions: {},
52
+ triggers
53
+ };
54
+ return {
55
+ ...base,
56
+ description: resolveLocalized(api.description || api.shortDescription || "") || base.description,
57
+ blob,
58
+ nodeCount: nodes.length,
59
+ outline: nodes.map((n) => n.nodeSlug ?? n.id).filter(Boolean)
60
+ };
61
+ }
62
+ export function useIntegrationsTemplates() {
63
+ const templates = useState("integrations.templates", () => []);
64
+ const loaded = useState("integrations.templates.loaded", () => false);
65
+ const loading = useState("integrations.templates.loading", () => false);
66
+ const error = useState("integrations.templates.error", () => null);
67
+ const api = useIntegrationsApi();
68
+ function unwrap(res) {
69
+ return res?.data ?? res;
70
+ }
71
+ function captureError(e, fallback) {
72
+ return e instanceof IntegrationsApiError ? e.message : e?.message ?? fallback;
73
+ }
74
+ async function load(force = false) {
75
+ if (loading.value) return templates.value;
76
+ if (loaded.value && !force) return templates.value;
77
+ loading.value = true;
78
+ error.value = null;
79
+ try {
80
+ const list2 = unwrap(await api.get("/templates")) ?? [];
81
+ templates.value = list2.map(mapListItem);
82
+ loaded.value = true;
83
+ } catch (e) {
84
+ error.value = captureError(e, "Could not load templates.");
85
+ templates.value = [];
86
+ loaded.value = true;
87
+ } finally {
88
+ loading.value = false;
89
+ }
90
+ return templates.value;
91
+ }
92
+ function list() {
93
+ return templates.value;
94
+ }
95
+ function get(slug) {
96
+ return templates.value.find((t) => t.slug === slug) ?? null;
97
+ }
98
+ async function fetchDetail(slug) {
99
+ const base = get(slug);
100
+ if (!base?.fromApi) return base;
101
+ try {
102
+ const detail = unwrap(await api.get(`/templates/${slug}`));
103
+ const merged = applyDetail(base, detail);
104
+ templates.value = templates.value.map((t) => t.slug === slug ? merged : t);
105
+ return merged;
106
+ } catch {
107
+ return base;
108
+ }
109
+ }
110
+ async function requirements(slug) {
111
+ try {
112
+ const raw = unwrap(await api.get(`/templates/${slug}/requirements`));
113
+ if (!raw) return null;
114
+ return {
115
+ ...raw,
116
+ credentialTypes: (raw.credentialTypes ?? []).map((c) => ({
117
+ ...c,
118
+ name: c.name ? resolveLocalized(c.name) : c.name
119
+ }))
120
+ };
121
+ } catch {
122
+ return null;
123
+ }
124
+ }
125
+ async function instantiate(slug, opts = {}) {
126
+ const body = {};
127
+ if (opts.name) body.name = opts.name;
128
+ const bindings = Object.fromEntries(
129
+ Object.entries(opts.credentialBindings ?? {}).filter(([, v]) => v)
130
+ );
131
+ if (Object.keys(bindings).length) body.credential_bindings = bindings;
132
+ const created = unwrap(await api.post(`/templates/${slug}/instantiate`, body));
133
+ return Number(created.id);
134
+ }
135
+ async function install(slug) {
136
+ return instantiate(slug);
137
+ }
138
+ const total = computed(() => templates.value.length);
139
+ const popular = computed(() => [...templates.value].sort((a, b) => b.installs - a.installs).slice(0, 4));
140
+ return { templates, loaded, loading, error, total, popular, load, list, get, fetchDetail, requirements, instantiate, install };
141
+ }
@@ -0,0 +1,39 @@
1
+ import type { TriggerType } from '../types/integrations.js';
2
+ /**
3
+ * Workflow triggers — a per-workflow sub-resource in the Integrations service
4
+ * (`/workflows/{id}/triggers`), NOT part of the workflow blob. The global
5
+ * Schedules / Webhooks views fan `loadAll()` out across the tenant's workflows
6
+ * and filter by `type`.
7
+ */
8
+ export interface ApiTrigger {
9
+ id: number;
10
+ workflow_id: number;
11
+ handle: string;
12
+ type: TriggerType;
13
+ name?: string | Record<string, string> | null;
14
+ config?: Record<string, unknown> | null;
15
+ active: boolean;
16
+ created_at?: string;
17
+ updated_at?: string;
18
+ }
19
+ export interface TriggerWithWorkflow extends ApiTrigger {
20
+ workflowName: string;
21
+ }
22
+ export interface TriggerInput {
23
+ handle: string;
24
+ type: TriggerType;
25
+ name?: string | null;
26
+ config?: Record<string, unknown> | null;
27
+ active?: boolean;
28
+ }
29
+ export declare function useIntegrationsTriggers(): {
30
+ all: any;
31
+ loading: any;
32
+ error: any;
33
+ loaded: any;
34
+ list: (workflowId: number | string) => Promise<ApiTrigger[]>;
35
+ loadAll: (force?: boolean) => Promise<TriggerWithWorkflow[]>;
36
+ create: (workflowId: number | string, data: TriggerInput) => Promise<ApiTrigger>;
37
+ update: (workflowId: number | string, triggerId: number, data: Omit<TriggerInput, "handle">) => Promise<ApiTrigger>;
38
+ remove: (workflowId: number | string, triggerId: number) => Promise<unknown>;
39
+ };