diff --git a/common/constants.ts b/common/constants.ts
new file mode 100644
index 00000000..3cc13ee1
--- /dev/null
+++ b/common/constants.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+export const PLUGIN_ID = 'aiFlowDashboards';
+
+export const BASE_NODE_API_PATH = '/api/ai_flow';
diff --git a/common/index.ts b/common/index.ts
new file mode 100644
index 00000000..2e209c79
--- /dev/null
+++ b/common/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './constants';
diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json
new file mode 100644
index 00000000..1f62086b
--- /dev/null
+++ b/opensearch_dashboards.json
@@ -0,0 +1,9 @@
+{
+ "id": "aiFlowDashboards",
+ "version": "3.0.0.0",
+ "opensearchDashboardsVersion": "3.0.0",
+ "server": true,
+ "ui": true,
+ "requiredPlugins": ["navigation"],
+ "optionalPlugins": []
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..ae6192bd
--- /dev/null
+++ b/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "ai-flow-dashboards",
+ "version": "3.0.0.0",
+ "description": "OpenSearch AI Flow Dashboards Plugin",
+ "main": "index.js",
+ "config": {
+ "plugin_version": "3.0.0.0",
+ "plugin_name": "aiFlowDashboards",
+ "plugin_zip_name": "ai-flow-dashboards"
+ },
+ "private": true,
+ "scripts": {
+ "plugin-helpers": "../../scripts/use_node ../../scripts/plugin_helpers",
+ "osd": "../../scripts/use_node ../../scripts/osd",
+ "opensearch": "node ../../scripts/opensearch",
+ "lint": "node ../../scripts/eslint .",
+ "build": "yarn plugin-helpers build && echo Renaming artifact to $npm_package_config_plugin_zip_name-$npm_package_config_plugin_version.zip && mv ./build/$npm_package_config_plugin_name*.zip ./build/$npm_package_config_plugin_zip_name-$npm_package_config_plugin_version.zip"
+ },
+ "lint-staged": {
+ "*.{ts,tsx,js,jsx,json,css,md}": [
+ "prettier --write",
+ "git add"
+ ]
+ },
+ "devDependencies": {},
+ "dependencies": {},
+ "resolutions": {}
+}
diff --git a/public/app.tsx b/public/app.tsx
new file mode 100644
index 00000000..4ad21a9e
--- /dev/null
+++ b/public/app.tsx
@@ -0,0 +1,80 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { I18nProvider } from '@osd/i18n/react';
+import { Route, RouteComponentProps, Switch } from 'react-router-dom';
+import { EuiPageSideBar, EuiSideNav, EuiPageTemplate } from '@elastic/eui';
+import { CoreStart } from '../../../src/core/public';
+import { Navigation, APP_PATH } from './utils';
+import { Overview, UseCases, Workflows } from './pages';
+import { CoreServicesConsumer } from './core_services';
+
+interface Props extends RouteComponentProps {}
+
+export const AiFlowDashboardsApp = (props: Props) => {
+ const sidebar = (
+
+
+
+ );
+
+ // Render the application DOM.
+ return (
+
+ {(core: CoreStart | null) =>
+ core && (
+
+ <>
+
+
+ }
+ />
+ }
+ />
+ {/* Defaulting to Overview page */}
+ }
+ />
+
+
+ >
+
+ )
+ }
+
+ );
+};
diff --git a/public/core_services.ts b/public/core_services.ts
new file mode 100644
index 00000000..e2e8ced4
--- /dev/null
+++ b/public/core_services.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { createContext } from 'react';
+import { CoreStart } from '../../../src/core/public';
+
+const CoreServicesContext = createContext(null);
+
+const CoreServicesConsumer = CoreServicesContext.Consumer;
+
+export { CoreServicesContext, CoreServicesConsumer };
diff --git a/public/index.scss b/public/index.scss
new file mode 100644
index 00000000..ff711240
--- /dev/null
+++ b/public/index.scss
@@ -0,0 +1 @@
+/* stylelint-disable no-empty-source */
diff --git a/public/index.ts b/public/index.ts
new file mode 100644
index 00000000..2447574c
--- /dev/null
+++ b/public/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import './index.scss';
+
+import { AiFlowDashboardsPlugin } from './plugin';
+
+// This exports static code and TypeScript types,
+// as well as, OpenSearch Dashboards Platform `plugin()` initializer.
+export function plugin() {
+ return new AiFlowDashboardsPlugin();
+}
+export {
+ AiFlowDashboardsPluginSetup,
+ AiFlowDashboardsPluginStart,
+} from './types';
+
+export * from './core_services';
diff --git a/public/pages/index.ts b/public/pages/index.ts
new file mode 100644
index 00000000..98812058
--- /dev/null
+++ b/public/pages/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './use_cases';
+export * from './workflows';
+export * from './overview';
diff --git a/public/pages/overview/index.ts b/public/pages/overview/index.ts
new file mode 100644
index 00000000..5c460c3d
--- /dev/null
+++ b/public/pages/overview/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { Overview } from './overview';
diff --git a/public/pages/overview/overview.tsx b/public/pages/overview/overview.tsx
new file mode 100644
index 00000000..7d8c73d4
--- /dev/null
+++ b/public/pages/overview/overview.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useEffect } from 'react';
+import { EuiPageHeader, EuiText } from '@elastic/eui';
+import { CoreServicesContext } from '../../core_services';
+import { CoreStart } from '../../../../../src/core/public';
+import { BREADCRUMBS } from '../../utils';
+
+export function Overview() {
+ const core = React.useContext(CoreServicesContext) as CoreStart;
+ useEffect(() => {
+ core.chrome.setBreadcrumbs([BREADCRUMBS.AI_APPLICATION_BUILDER]);
+ });
+
+ return (
+
+ Welcome to the AI Application Builder!
+
+ );
+}
diff --git a/public/pages/use_cases/index.ts b/public/pages/use_cases/index.ts
new file mode 100644
index 00000000..d569570e
--- /dev/null
+++ b/public/pages/use_cases/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { UseCases } from './use_cases';
diff --git a/public/pages/use_cases/use_cases.tsx b/public/pages/use_cases/use_cases.tsx
new file mode 100644
index 00000000..efd3c310
--- /dev/null
+++ b/public/pages/use_cases/use_cases.tsx
@@ -0,0 +1,26 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useEffect } from 'react';
+import { EuiPageHeader, EuiText } from '@elastic/eui';
+import { CoreServicesContext } from '../../core_services';
+import { CoreStart } from '../../../../../src/core/public';
+import { BREADCRUMBS } from '../../utils';
+
+export function UseCases() {
+ const core = React.useContext(CoreServicesContext) as CoreStart;
+ useEffect(() => {
+ core.chrome.setBreadcrumbs([
+ BREADCRUMBS.AI_APPLICATION_BUILDER,
+ BREADCRUMBS.USE_CASES,
+ ]);
+ });
+
+ return (
+
+ Use cases page placeholder...
+
+ );
+}
diff --git a/public/pages/workflows/index.ts b/public/pages/workflows/index.ts
new file mode 100644
index 00000000..047a1868
--- /dev/null
+++ b/public/pages/workflows/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { Workflows } from './workflows';
diff --git a/public/pages/workflows/workflows.tsx b/public/pages/workflows/workflows.tsx
new file mode 100644
index 00000000..405151d0
--- /dev/null
+++ b/public/pages/workflows/workflows.tsx
@@ -0,0 +1,26 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useEffect } from 'react';
+import { EuiPageHeader, EuiText } from '@elastic/eui';
+import { CoreServicesContext } from '../../core_services';
+import { CoreStart } from '../../../../../src/core/public';
+import { BREADCRUMBS } from '../../utils';
+
+export function Workflows() {
+ const core = React.useContext(CoreServicesContext) as CoreStart;
+ useEffect(() => {
+ core.chrome.setBreadcrumbs([
+ BREADCRUMBS.AI_APPLICATION_BUILDER,
+ BREADCRUMBS.WORKFLOWS,
+ ]);
+ });
+
+ return (
+
+ Workflows page placeholder...
+
+ );
+}
diff --git a/public/plugin.ts b/public/plugin.ts
new file mode 100644
index 00000000..8ef92f99
--- /dev/null
+++ b/public/plugin.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ AppMountParameters,
+ CoreSetup,
+ CoreStart,
+ Plugin,
+} from '../../../src/core/public';
+import {
+ AiFlowDashboardsPluginSetup,
+ AiFlowDashboardsPluginStart,
+} from './types';
+import { PLUGIN_ID } from '../common';
+
+export class AiFlowDashboardsPlugin
+ implements Plugin {
+ public setup(core: CoreSetup): AiFlowDashboardsPluginSetup {
+ // Register the plugin in the side navigation
+ core.application.register({
+ id: PLUGIN_ID,
+ title: 'AI Application Builder',
+ category: {
+ id: 'opensearch',
+ label: 'OpenSearch plugins',
+ order: 2000,
+ },
+ // TODO: can i remove this below order
+ order: 5000,
+ async mount(params: AppMountParameters) {
+ const { renderApp } = await import('./render_app');
+ const [coreStart] = await core.getStartServices();
+ return renderApp(coreStart, params);
+ },
+ });
+ return {};
+ }
+
+ public start(core: CoreStart): AiFlowDashboardsPluginStart {
+ return {};
+ }
+
+ public stop() {}
+}
diff --git a/public/render_app.tsx b/public/render_app.tsx
new file mode 100644
index 00000000..bd80668d
--- /dev/null
+++ b/public/render_app.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { BrowserRouter as Router, Route } from 'react-router-dom';
+import { AppMountParameters, CoreStart } from '../../../src/core/public';
+import { AiFlowDashboardsApp } from './app';
+import { CoreServicesContext } from './core_services';
+
+export const renderApp = (
+ coreStart: CoreStart,
+ { appBasePath, element }: AppMountParameters
+) => {
+ ReactDOM.render(
+
+ (
+
+
+
+ )}
+ >
+ ,
+ element
+ );
+
+ return () => ReactDOM.unmountComponentAtNode(element);
+};
diff --git a/public/types.ts b/public/types.ts
new file mode 100644
index 00000000..c6b021cc
--- /dev/null
+++ b/public/types.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public';
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface AiFlowDashboardsPluginSetup {}
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface AiFlowDashboardsPluginStart {}
+
+export interface AppPluginStartDependencies {
+ navigation: NavigationPublicPluginStart;
+}
diff --git a/public/utils/constants.ts b/public/utils/constants.ts
new file mode 100644
index 00000000..0bbcc29c
--- /dev/null
+++ b/public/utils/constants.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export enum Navigation {
+ AiApplicationBuilder = 'AI Application Builder',
+ UseCases = 'Use Cases',
+ Workflows = 'Workflows',
+}
+
+export enum APP_PATH {
+ HOME = '/',
+ USE_CASES = '/use-cases',
+ WORKSPACE = '/workspace',
+ WORKFLOWS = '/workflows',
+ WORKFLOW_DETAIL = '/workflows/:workflowId/',
+}
+
+export const BREADCRUMBS = Object.freeze({
+ AI_APPLICATION_BUILDER: { text: 'AI application builder', href: '#/' },
+ USE_CASES: { text: 'Use cases', href: `#${APP_PATH.USE_CASES}` },
+ WORKFLOWS: { text: 'Workflows', href: `#${APP_PATH.WORKFLOWS}` },
+});
diff --git a/public/utils/index.ts b/public/utils/index.ts
new file mode 100644
index 00000000..2e209c79
--- /dev/null
+++ b/public/utils/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './constants';
diff --git a/server/index.ts b/server/index.ts
new file mode 100644
index 00000000..ea11c96f
--- /dev/null
+++ b/server/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { PluginInitializerContext } from '../../../src/core/server';
+import { AiFlowDashboardsPlugin } from './plugin';
+
+// This exports static code and TypeScript types,
+// as well as, OpenSearch Dashboards Platform `plugin()` initializer.
+
+export function plugin(initializerContext: PluginInitializerContext) {
+ return new AiFlowDashboardsPlugin(initializerContext);
+}
+
+export {
+ AiFlowDashboardsPluginSetup,
+ AiFlowDashboardsPluginStart,
+} from './types';
diff --git a/server/plugin.ts b/server/plugin.ts
new file mode 100644
index 00000000..766ea951
--- /dev/null
+++ b/server/plugin.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ PluginInitializerContext,
+ CoreSetup,
+ CoreStart,
+ Plugin,
+ Logger,
+} from '../../../src/core/server';
+
+import {
+ AiFlowDashboardsPluginSetup,
+ AiFlowDashboardsPluginStart,
+} from './types';
+import { defineRoutes } from './routes';
+
+export class AiFlowDashboardsPlugin
+ implements Plugin {
+ private readonly logger: Logger;
+
+ constructor(initializerContext: PluginInitializerContext) {
+ this.logger = initializerContext.logger.get();
+ }
+
+ public setup(core: CoreSetup) {
+ this.logger.debug('ai-flow-dashboards: Setup');
+ const router = core.http.createRouter();
+
+ // Register server side APIs
+ defineRoutes(router);
+
+ return {};
+ }
+
+ public start(core: CoreStart) {
+ this.logger.debug('ai-flow-dashboards: Started');
+ return {};
+ }
+
+ public stop() {}
+}
diff --git a/server/routes/index.ts b/server/routes/index.ts
new file mode 100644
index 00000000..0ae579e8
--- /dev/null
+++ b/server/routes/index.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { IRouter } from '../../../../src/core/server';
+import { BASE_NODE_API_PATH } from '../../common';
+
+export function defineRoutes(router: IRouter) {
+ router.get(
+ {
+ path: BASE_NODE_API_PATH,
+ validate: false,
+ },
+ async (context, request, response) => {
+ return response.ok({
+ body: {
+ time: new Date().toISOString(),
+ },
+ });
+ }
+ );
+}
diff --git a/server/types.ts b/server/types.ts
new file mode 100644
index 00000000..1fd42a8a
--- /dev/null
+++ b/server/types.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface AiFlowDashboardsPluginSetup {}
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface AiFlowDashboardsPluginStart {}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..a598d0f4
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,60 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "skipLibCheck": true,
+ "baseUrl": ".",
+ "paths": {
+ // Allows for importing from `opensearch-dashboards` package for the exported types.
+ "opensearch-dashboards": ["./opensearch_dashboards"],
+ "ui/*": ["src/legacy/ui/public/*"],
+ "test_utils/*": ["src/test_utils/public/*"]
+ },
+ // Support .tsx files and transform JSX into calls to React.createElement
+ "jsx": "react",
+ // Enables all strict type checking options.
+ "strict": true,
+ // enables "core language features"
+ "lib": [
+ // ESNext auto includes previous versions all the way back to es5
+ "esnext",
+ // includes support for browser APIs
+ "dom"
+ ],
+ // Node 8 should support everything output by esnext, we override this
+ // in webpack with loader-level compiler options
+ "target": "esnext",
+ // Use commonjs for node, overridden in webpack to keep import statements
+ // to maintain support for things like `await import()`
+ "module": "commonjs",
+ // Allows default imports from modules with no default export. This does not affect code emit, just type checking.
+ // We have to enable this option explicitly since `esModuleInterop` doesn't enable it automatically when ES2015 or
+ // ESNext module format is used.
+ "allowSyntheticDefaultImports": true,
+ // Emits __importStar and __importDefault helpers for runtime babel ecosystem compatibility.
+ "esModuleInterop": true,
+ // Resolve modules in the same way as Node.js. Aka make `require` works the
+ // same in TypeScript as it does in Node.js.
+ "moduleResolution": "node",
+ // Disallow inconsistently-cased references to the same file.
+ "forceConsistentCasingInFileNames": true,
+ // Disable the breaking keyof behaviour introduced in TS 2.9.2 until EUI is updated to support that too
+ "keyofStringsOnly": true,
+ // Forbid unused local variables as the rule was deprecated by ts-lint
+ "noUnusedLocals": true,
+ // Provide full support for iterables in for..of, spread and destructuring when targeting ES5 or ES3.
+ "downlevelIteration": true,
+ // import tslib helpers rather than inlining helpers for iteration or spreading, for instance
+ "importHelpers": true,
+ // adding global typings
+ "types": ["node", "jest", "react"]
+ },
+ "include": [
+ "index.ts",
+ "common/**/*.ts",
+ "public/**/*.ts",
+ "public/**/*.tsx",
+ "server/**/*.ts",
+ "../../typings/**/*"
+ ],
+ "exclude": ["node_modules", "*/node_modules/"]
+}
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 00000000..fb57ccd1
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,4 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+