Compare commits
	
		
			70 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8f1c694071 | ||
| 
						 | 
					789a8b0cf0 | ||
| 
						 | 
					c9dd02ace3 | ||
| 
						 | 
					ad5906c7b6 | ||
| 
						 | 
					e837c494cb | ||
| 
						 | 
					afc40fcbe3 | ||
| 
						 | 
					185f50787b | ||
| 
						 | 
					6c33676f73 | ||
| 
						 | 
					0290002444 | ||
| 
						 | 
					fc5195e817 | ||
| 
						 | 
					efd5c3dca1 | ||
| 
						 | 
					2f438feec2 | ||
| 
						 | 
					07ae9dfddf | ||
| 
						 | 
					64575c5f7d | ||
| 
						 | 
					e0fa339644 | ||
| 
						 | 
					b72a86e514 | ||
| 
						 | 
					62f0414afa | ||
| 
						 | 
					200a02b87b | ||
| 
						 | 
					da5dbeaf0f | ||
| 
						 | 
					4b6d099f72 | ||
| 
						 | 
					842661ada6 | ||
| 
						 | 
					f5148c87c8 | ||
| 
						 | 
					16164c0bbc | ||
| 
						 | 
					f38ddb840b | ||
| 
						 | 
					f86fe26ffe | ||
| 
						 | 
					162360bf45 | ||
| 
						 | 
					612aaa7880 | ||
| 
						 | 
					e91f3fe53d | ||
| 
						 | 
					f0fe4d64bc | ||
| 
						 | 
					07cc6aca6a | ||
| 
						 | 
					23bf81efbb | ||
| 
						 | 
					a55105e5ee | ||
| 
						 | 
					5832a426bc | ||
| 
						 | 
					38dc709108 | ||
| 
						 | 
					5696d3359b | ||
| 
						 | 
					1b4fa84753 | ||
| 
						 | 
					13f0f117da | ||
| 
						 | 
					2db4eeec05 | ||
| 
						 | 
					fe5e8aa5fe | ||
| 
						 | 
					13e35d24a2 | ||
| 
						 | 
					0b6ae80777 | ||
| 
						 | 
					5e0fab88a3 | ||
| 
						 | 
					bf8797264b | ||
| 
						 | 
					14bde967bd | ||
| 
						 | 
					596ce69789 | ||
| 
						 | 
					c5491dcb73 | ||
| 
						 | 
					3f6340f0a1 | ||
| 
						 | 
					351f0870a9 | ||
| 
						 | 
					f2638a4c5e | ||
| 
						 | 
					2bd00d5ca0 | ||
| 
						 | 
					00a40dd450 | ||
| 
						 | 
					cfe1cb2dbf | ||
| 
						 | 
					16fb75b56c | ||
| 
						 | 
					094cf45ce3 | ||
| 
						 | 
					d6984b3da9 | ||
| 
						 | 
					53fc6f4cde | ||
| 
						 | 
					e1dc8050e3 | ||
| 
						 | 
					49da10cf0b | ||
| 
						 | 
					a3e10910bf | ||
| 
						 | 
					3ff9edc424 | ||
| 
						 | 
					69414d4083 | ||
| 
						 | 
					e06b7a7775 | ||
| 
						 | 
					c006e4d922 | ||
| 
						 | 
					df6fe0863b | ||
| 
						 | 
					d55a29911c | ||
| 
						 | 
					d0e49d27fd | ||
| 
						 | 
					1299bfc93e | ||
| 
						 | 
					be999646d4 | ||
| 
						 | 
					e57d32f122 | ||
| 
						 | 
					fe8d88497f | 
							
								
								
									
										8
									
								
								.github/workflows/build-release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/build-release.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,11 +11,11 @@ jobs:
 | 
				
			|||||||
    name: Build web
 | 
					    name: Build web
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v3
 | 
					      - uses: actions/checkout@v4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - uses: actions/setup-node@v3
 | 
					      - uses: actions/setup-node@v4
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          node-version: 18
 | 
					          node-version: "20.11.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - run: touch env-config.js
 | 
					      - run: touch env-config.js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,6 +29,6 @@ jobs:
 | 
				
			|||||||
        run: tar -czvf trmm-web-${{github.ref_name}}.tar.gz dist/
 | 
					        run: tar -czvf trmm-web-${{github.ref_name}}.tar.gz dist/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Release
 | 
					      - name: Release
 | 
				
			||||||
        uses: softprops/action-gh-release@v1
 | 
					        uses: softprops/action-gh-release@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          files: trmm-web-${{github.ref_name}}.tar.gz
 | 
					          files: trmm-web-${{github.ref_name}}.tar.gz
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1143
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1143
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										51
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "web",
 | 
					  "name": "web",
 | 
				
			||||||
  "version": "0.101.36",
 | 
					  "version": "0.101.44",
 | 
				
			||||||
  "private": true,
 | 
					  "private": true,
 | 
				
			||||||
  "productName": "Tactical RMM",
 | 
					  "productName": "Tactical RMM",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
@@ -10,34 +10,37 @@
 | 
				
			|||||||
    "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
 | 
					    "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@quasar/extras": "1.16.7",
 | 
					    "@quasar/extras": "1.16.11",
 | 
				
			||||||
    "apexcharts": "3.44.0",
 | 
					    "apexcharts": "3.48.0",
 | 
				
			||||||
    "axios": "1.6.0",
 | 
					    "axios": "1.6.8",
 | 
				
			||||||
    "dotenv": "16.3.1",
 | 
					    "dotenv": "16.4.5",
 | 
				
			||||||
 | 
					    "pinia": "^2.1.7",
 | 
				
			||||||
    "qrcode.vue": "3.4.1",
 | 
					    "qrcode.vue": "3.4.1",
 | 
				
			||||||
    "quasar": "2.13.0",
 | 
					    "quasar": "2.15.2",
 | 
				
			||||||
    "vue": "3.3.8",
 | 
					    "vue": "3.4.21",
 | 
				
			||||||
    "vue3-apexcharts": "1.4.4",
 | 
					    "vue3-apexcharts": "1.5.2",
 | 
				
			||||||
    "vuedraggable": "4.1.0",
 | 
					    "vuedraggable": "4.1.0",
 | 
				
			||||||
    "vue-router": "4.2.5",
 | 
					    "vue-router": "4.3.0",
 | 
				
			||||||
    "@vueuse/core": "10.5.0",
 | 
					    "@vueuse/core": "10.9.0",
 | 
				
			||||||
    "@vueuse/shared": "10.5.0",
 | 
					    "@vueuse/shared": "10.9.0",
 | 
				
			||||||
    "monaco-editor": "0.44.0",
 | 
					    "monaco-editor": "0.47.0",
 | 
				
			||||||
    "vuex": "4.1.0",
 | 
					    "vuex": "4.1.0",
 | 
				
			||||||
    "yaml": "2.3.4"
 | 
					    "xterm": "^5.3.0",
 | 
				
			||||||
 | 
					    "xterm-addon-fit": "^0.8.0",
 | 
				
			||||||
 | 
					    "yaml": "2.4.1"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@quasar/cli": "2.3.0",
 | 
					    "@quasar/cli": "2.4.0",
 | 
				
			||||||
    "@intlify/unplugin-vue-i18n": "1.4.0",
 | 
					    "@intlify/unplugin-vue-i18n": "4.0.0",
 | 
				
			||||||
    "@quasar/app-vite": "1.6.2",
 | 
					    "@quasar/app-vite": "1.8.0",
 | 
				
			||||||
    "@types/node": "20.8.10",
 | 
					    "@types/node": "20.12.2",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "6.10.0",
 | 
					    "@typescript-eslint/eslint-plugin": "7.4.0",
 | 
				
			||||||
    "@typescript-eslint/parser": "6.10.0",
 | 
					    "@typescript-eslint/parser": "7.4.0",
 | 
				
			||||||
    "autoprefixer": "10.4.16",
 | 
					    "autoprefixer": "10.4.19",
 | 
				
			||||||
    "eslint": "8.53.0",
 | 
					    "eslint": "8.57.0",
 | 
				
			||||||
    "eslint-config-prettier": "9.0.0",
 | 
					    "eslint-config-prettier": "9.1.0",
 | 
				
			||||||
    "eslint-plugin-vue": "8.7.1",
 | 
					    "eslint-plugin-vue": "8.7.1",
 | 
				
			||||||
    "prettier": "3.0.3",
 | 
					    "prettier": "3.2.5",
 | 
				
			||||||
    "typescript": "5.2.2"
 | 
					    "typescript": "5.4.3"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ module.exports = configure(function (/* ctx */) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // https://github.com/quasarframework/quasar/tree/dev/extras
 | 
					    // https://github.com/quasarframework/quasar/tree/dev/extras
 | 
				
			||||||
    extras: [
 | 
					    extras: [
 | 
				
			||||||
      // 'ionicons-v4',
 | 
					      "ionicons-v4",
 | 
				
			||||||
      "mdi-v5",
 | 
					      "mdi-v5",
 | 
				
			||||||
      "fontawesome-v6",
 | 
					      "fontawesome-v6",
 | 
				
			||||||
      // 'eva-icons',
 | 
					      // 'eva-icons',
 | 
				
			||||||
@@ -51,8 +51,8 @@ module.exports = configure(function (/* ctx */) {
 | 
				
			|||||||
    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
 | 
					    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
 | 
				
			||||||
    build: {
 | 
					    build: {
 | 
				
			||||||
      target: {
 | 
					      target: {
 | 
				
			||||||
        browser: ["es2021"],
 | 
					        browser: ["es2022"],
 | 
				
			||||||
        node: "node16",
 | 
					        node: "node20",
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      vueRouterMode: "history", // available values: 'hash', 'history'
 | 
					      vueRouterMode: "history", // available values: 'hash', 'history'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -191,6 +191,11 @@ export async function agentRebootNow(agent_id) {
 | 
				
			|||||||
  return data;
 | 
					  return data;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function agentShutdown(agent_id) {
 | 
				
			||||||
 | 
					  const { data } = await axios.post(`${baseUrl}/${agent_id}/shutdown/`);
 | 
				
			||||||
 | 
					  return data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function sendAgentRecoverMesh(agent_id, params = {}) {
 | 
					export async function sendAgentRecoverMesh(agent_id, params = {}) {
 | 
				
			||||||
  const { data } = await axios.post(
 | 
					  const { data } = await axios.post(
 | 
				
			||||||
    `${baseUrl}/${agent_id}/meshcentral/recover/`,
 | 
					    `${baseUrl}/${agent_id}/meshcentral/recover/`,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/trmm_256.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/trmm_256.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 15 KiB  | 
@@ -170,7 +170,7 @@
 | 
				
			|||||||
                overdueAlert(
 | 
					                overdueAlert(
 | 
				
			||||||
                  'dashboard',
 | 
					                  'dashboard',
 | 
				
			||||||
                  props.row,
 | 
					                  props.row,
 | 
				
			||||||
                  props.row.overdue_dashboard_alert
 | 
					                  props.row.overdue_dashboard_alert,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
              "
 | 
					              "
 | 
				
			||||||
              v-model="props.row.overdue_dashboard_alert"
 | 
					              v-model="props.row.overdue_dashboard_alert"
 | 
				
			||||||
@@ -431,8 +431,8 @@ export default {
 | 
				
			|||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
          else if (availability === "expired") {
 | 
					          else if (availability === "expired") {
 | 
				
			||||||
            let now = new Date();
 | 
					            let now = new Date();
 | 
				
			||||||
            let lastSeen = date.extractDate(row.last_seen, "MM DD YYYY HH:mm");
 | 
					            let last_seen = new Date(row.last_seen);
 | 
				
			||||||
            let diff = date.getDateDiff(now, lastSeen, "days");
 | 
					            let diff = date.getDateDiff(now, last_seen, "days");
 | 
				
			||||||
            if (diff < 30) return false;
 | 
					            if (diff < 30) return false;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -107,7 +107,7 @@
 | 
				
			|||||||
              />
 | 
					              />
 | 
				
			||||||
              <q-checkbox
 | 
					              <q-checkbox
 | 
				
			||||||
                v-model="localRole.can_reboot_agents"
 | 
					                v-model="localRole.can_reboot_agents"
 | 
				
			||||||
                label="Reboot Agents"
 | 
					                label="Shutdown / Reboot Agents"
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
              <q-checkbox
 | 
					              <q-checkbox
 | 
				
			||||||
                v-model="localRole.can_send_wol"
 | 
					                v-model="localRole.can_send_wol"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -176,6 +176,13 @@
 | 
				
			|||||||
      </q-menu>
 | 
					      </q-menu>
 | 
				
			||||||
    </q-item>
 | 
					    </q-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <q-item clickable v-close-popup @click="shutdown(agent)">
 | 
				
			||||||
 | 
					      <q-item-section side>
 | 
				
			||||||
 | 
					        <q-icon size="xs" name="power" />
 | 
				
			||||||
 | 
					      </q-item-section>
 | 
				
			||||||
 | 
					      <q-item-section>Shutdown</q-item-section>
 | 
				
			||||||
 | 
					    </q-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <q-item clickable v-close-popup @click="showPolicyAdd(agent)">
 | 
					    <q-item clickable v-close-popup @click="showPolicyAdd(agent)">
 | 
				
			||||||
      <q-item-section side>
 | 
					      <q-item-section side>
 | 
				
			||||||
        <q-icon size="xs" name="policy" />
 | 
					        <q-icon size="xs" name="policy" />
 | 
				
			||||||
@@ -192,9 +199,9 @@
 | 
				
			|||||||
      "
 | 
					      "
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <q-item-section side>
 | 
					      <q-item-section side>
 | 
				
			||||||
        <q-icon size="xs" name="integration_instructions" />
 | 
					        <q-icon size="xs" name="analytics" />
 | 
				
			||||||
      </q-item-section>
 | 
					      </q-item-section>
 | 
				
			||||||
      <q-item-section>Integrations</q-item-section>
 | 
					      <q-item-section>Reporting</q-item-section>
 | 
				
			||||||
      <q-item-section side>
 | 
					      <q-item-section side>
 | 
				
			||||||
        <q-icon name="keyboard_arrow_right" />
 | 
					        <q-icon name="keyboard_arrow_right" />
 | 
				
			||||||
      </q-item-section>
 | 
					      </q-item-section>
 | 
				
			||||||
@@ -231,6 +238,7 @@ import { fetchURLActions, runURLAction } from "@/api/core";
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
  editAgent,
 | 
					  editAgent,
 | 
				
			||||||
  agentRebootNow,
 | 
					  agentRebootNow,
 | 
				
			||||||
 | 
					  agentShutdown,
 | 
				
			||||||
  sendAgentPing,
 | 
					  sendAgentPing,
 | 
				
			||||||
  removeAgent,
 | 
					  removeAgent,
 | 
				
			||||||
  runRemoteBackground,
 | 
					  runRemoteBackground,
 | 
				
			||||||
@@ -298,7 +306,7 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (urlActions.value.length === 0) {
 | 
					        if (urlActions.value.length === 0) {
 | 
				
			||||||
          notifyWarning(
 | 
					          notifyWarning(
 | 
				
			||||||
            "No URL Actions configured. Go to Settings > Global Settings > URL Actions"
 | 
					            "No URL Actions configured. Go to Settings > Global Settings > URL Actions",
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          return;
 | 
					          return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -364,7 +372,7 @@ export default {
 | 
				
			|||||||
        notifySuccess(
 | 
					        notifySuccess(
 | 
				
			||||||
          `Maintenance mode was ${
 | 
					          `Maintenance mode was ${
 | 
				
			||||||
            agent.maintenance_mode ? "disabled" : "enabled"
 | 
					            agent.maintenance_mode ? "disabled" : "enabled"
 | 
				
			||||||
          } on ${agent.hostname}`
 | 
					          } on ${agent.hostname}`,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        store.commit("setRefreshSummaryTab", true);
 | 
					        store.commit("setRefreshSummaryTab", true);
 | 
				
			||||||
        refreshDashboard();
 | 
					        refreshDashboard();
 | 
				
			||||||
@@ -437,6 +445,32 @@ export default {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function shutdown(agent) {
 | 
				
			||||||
 | 
					      $q.dialog({
 | 
				
			||||||
 | 
					        title:
 | 
				
			||||||
 | 
					          'Please type <code style="color:red">yes</code> in the box below to confirm shutdown.',
 | 
				
			||||||
 | 
					        prompt: {
 | 
				
			||||||
 | 
					          model: "",
 | 
				
			||||||
 | 
					          type: "text",
 | 
				
			||||||
 | 
					          isValid: (val) => val === "yes",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        cancel: true,
 | 
				
			||||||
 | 
					        ok: { label: "Shutdown", color: "negative" },
 | 
				
			||||||
 | 
					        persistent: true,
 | 
				
			||||||
 | 
					        html: true,
 | 
				
			||||||
 | 
					      }).onOk(async () => {
 | 
				
			||||||
 | 
					        $q.loading.show();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          await agentShutdown(agent.agent_id);
 | 
				
			||||||
 | 
					          notifySuccess(`${agent.hostname} will now be shutdown`);
 | 
				
			||||||
 | 
					          $q.loading.hide();
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					          $q.loading.hide();
 | 
				
			||||||
 | 
					          console.error(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function showPolicyAdd(agent) {
 | 
					    function showPolicyAdd(agent) {
 | 
				
			||||||
      $q.dialog({
 | 
					      $q.dialog({
 | 
				
			||||||
        component: PolicyAdd,
 | 
					        component: PolicyAdd,
 | 
				
			||||||
@@ -505,7 +539,7 @@ export default {
 | 
				
			|||||||
          notifySuccess(data);
 | 
					          notifySuccess(data);
 | 
				
			||||||
          refreshDashboard(
 | 
					          refreshDashboard(
 | 
				
			||||||
            false /* clearTreeSelected */,
 | 
					            false /* clearTreeSelected */,
 | 
				
			||||||
            true /* clearSubTable */
 | 
					            true /* clearSubTable */,
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        } catch (e) {
 | 
					        } catch (e) {
 | 
				
			||||||
          console.error(e);
 | 
					          console.error(e);
 | 
				
			||||||
@@ -534,6 +568,7 @@ export default {
 | 
				
			|||||||
      runChecks,
 | 
					      runChecks,
 | 
				
			||||||
      showRebootLaterModal,
 | 
					      showRebootLaterModal,
 | 
				
			||||||
      rebootNow,
 | 
					      rebootNow,
 | 
				
			||||||
 | 
					      shutdown,
 | 
				
			||||||
      showPolicyAdd,
 | 
					      showPolicyAdd,
 | 
				
			||||||
      showAgentRecovery,
 | 
					      showAgentRecovery,
 | 
				
			||||||
      pingAgent,
 | 
					      pingAgent,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,7 @@
 | 
				
			|||||||
        :color="dash_warning_color"
 | 
					        :color="dash_warning_color"
 | 
				
			||||||
        class="q-mr-sm"
 | 
					        class="q-mr-sm"
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        <q-tooltip>Agent offline</q-tooltip>
 | 
					        <q-tooltip>{{ store.getters.formatDate(summary.last_seen) }}</q-tooltip>
 | 
				
			||||||
      </q-icon>
 | 
					      </q-icon>
 | 
				
			||||||
      <q-icon
 | 
					      <q-icon
 | 
				
			||||||
        v-else
 | 
					        v-else
 | 
				
			||||||
@@ -43,7 +43,7 @@
 | 
				
			|||||||
        :color="dash_positive_color"
 | 
					        :color="dash_positive_color"
 | 
				
			||||||
        class="q-mr-sm"
 | 
					        class="q-mr-sm"
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        <q-tooltip>Agent online</q-tooltip>
 | 
					        <q-tooltip>{{ store.getters.formatDate(summary.last_seen) }}</q-tooltip>
 | 
				
			||||||
      </q-icon>
 | 
					      </q-icon>
 | 
				
			||||||
      <b>{{ summary.hostname }}</b>
 | 
					      <b>{{ summary.hostname }}</b>
 | 
				
			||||||
      <span v-if="summary.maintenance_mode">
 | 
					      <span v-if="summary.maintenance_mode">
 | 
				
			||||||
@@ -267,7 +267,11 @@ export default {
 | 
				
			|||||||
    const loading = ref(false);
 | 
					    const loading = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const serial_number = computed(() => {
 | 
					    const serial_number = computed(() => {
 | 
				
			||||||
      return summary.value.wmi_detail.bios?.[0]?.[0]?.SerialNumber;
 | 
					      if (summary.value.plat === "windows") {
 | 
				
			||||||
 | 
					        return summary.value.wmi_detail.bios?.[0]?.[0]?.SerialNumber;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return summary.value.wmi_detail.serialnumber;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const cpu = computed(() => {
 | 
					    const cpu = computed(() => {
 | 
				
			||||||
@@ -280,7 +284,7 @@ export default {
 | 
				
			|||||||
    function diskBarColor(percent) {
 | 
					    function diskBarColor(percent) {
 | 
				
			||||||
      if (percent < 80) {
 | 
					      if (percent < 80) {
 | 
				
			||||||
        return dash_positive_color.value;
 | 
					        return dash_positive_color.value;
 | 
				
			||||||
      } else if (percent > 80 && percent < 95) {
 | 
					      } else if (percent >= 80 && percent < 95) {
 | 
				
			||||||
        return dash_warning_color.value;
 | 
					        return dash_warning_color.value;
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        return dash_negative_color.value;
 | 
					        return dash_negative_color.value;
 | 
				
			||||||
@@ -311,11 +315,11 @@ export default {
 | 
				
			|||||||
      const ret = [];
 | 
					      const ret = [];
 | 
				
			||||||
      for (const customField of summary.value.custom_fields) {
 | 
					      for (const customField of summary.value.custom_fields) {
 | 
				
			||||||
        const definition = customFieldsDefinitions.value.find(
 | 
					        const definition = customFieldsDefinitions.value.find(
 | 
				
			||||||
          (def) => def.id === customField.field
 | 
					          (def) => def.id === customField.field,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        if (
 | 
					        if (
 | 
				
			||||||
          definition &&
 | 
					          definition &&
 | 
				
			||||||
          !definition.hide_in_ui &&
 | 
					          !definition.hide_in_summary &&
 | 
				
			||||||
          customField.value?.length > 0
 | 
					          customField.value?.length > 0
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
          ret.push({
 | 
					          ret.push({
 | 
				
			||||||
@@ -381,6 +385,7 @@ export default {
 | 
				
			|||||||
      dash_negative_color,
 | 
					      dash_negative_color,
 | 
				
			||||||
      serial_number,
 | 
					      serial_number,
 | 
				
			||||||
      cpu,
 | 
					      cpu,
 | 
				
			||||||
 | 
					      store,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // methods
 | 
					      // methods
 | 
				
			||||||
      getSummary,
 | 
					      getSummary,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -254,7 +254,7 @@ export default {
 | 
				
			|||||||
      pagination: {
 | 
					      pagination: {
 | 
				
			||||||
        rowsPerPage: 0,
 | 
					        rowsPerPage: 0,
 | 
				
			||||||
        sortBy: "name",
 | 
					        sortBy: "name",
 | 
				
			||||||
        descending: true,
 | 
					        descending: false,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@@ -321,7 +321,7 @@ export default {
 | 
				
			|||||||
    runTask(task) {
 | 
					    runTask(task) {
 | 
				
			||||||
      if (!task.enabled) {
 | 
					      if (!task.enabled) {
 | 
				
			||||||
        this.notifyError(
 | 
					        this.notifyError(
 | 
				
			||||||
          "Task cannot be run when it's disabled. Enable it first."
 | 
					          "Task cannot be run when it's disabled. Enable it first.",
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <q-dialog ref="dialog" @hide="onHide">
 | 
					  <q-dialog ref="dialog" @hide="onHide">
 | 
				
			||||||
    <q-card class="q-dialog-plugin" style="width: 90vw">
 | 
					    <q-card class="q-dialog-plugin" style="min-width: 70vw">
 | 
				
			||||||
      <q-bar>
 | 
					      <q-bar>
 | 
				
			||||||
        {{ title.slice(0, 27) }}
 | 
					        {{ title.slice(0, 27) }}
 | 
				
			||||||
        <q-space />
 | 
					        <q-space />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -137,7 +137,7 @@
 | 
				
			|||||||
            <q-radio
 | 
					            <q-radio
 | 
				
			||||||
              v-model="goarch"
 | 
					              v-model="goarch"
 | 
				
			||||||
              :val="GOARCH_ARM64"
 | 
					              :val="GOARCH_ARM64"
 | 
				
			||||||
              label="Apple Silicon (M1, M2)"
 | 
					              label="Apple Silicon (M1, M2, M3)"
 | 
				
			||||||
              v-show="agentOS === 'darwin'"
 | 
					              v-show="agentOS === 'darwin'"
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <q-radio
 | 
					            <q-radio
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -142,6 +142,11 @@
 | 
				
			|||||||
            v-model="localField.hide_in_ui"
 | 
					            v-model="localField.hide_in_ui"
 | 
				
			||||||
            color="green"
 | 
					            color="green"
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
 | 
					          <q-toggle
 | 
				
			||||||
 | 
					            label="Hide in Summary Tab"
 | 
				
			||||||
 | 
					            v-model="localField.hide_in_summary"
 | 
				
			||||||
 | 
					            color="green"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
        </q-card-section>
 | 
					        </q-card-section>
 | 
				
			||||||
        <q-card-actions align="right">
 | 
					        <q-card-actions align="right">
 | 
				
			||||||
          <q-btn flat label="Cancel" v-close-popup />
 | 
					          <q-btn flat label="Cancel" v-close-popup />
 | 
				
			||||||
@@ -172,6 +177,7 @@ export default {
 | 
				
			|||||||
        default_value_bool: false,
 | 
					        default_value_bool: false,
 | 
				
			||||||
        default_values_multiple: [],
 | 
					        default_values_multiple: [],
 | 
				
			||||||
        hide_in_ui: false,
 | 
					        hide_in_ui: false,
 | 
				
			||||||
 | 
					        hide_in_summary: false,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      modelOptions: [
 | 
					      modelOptions: [
 | 
				
			||||||
        { label: "Client", value: "client" },
 | 
					        { label: "Client", value: "client" },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,6 +57,10 @@
 | 
				
			|||||||
        <q-td>
 | 
					        <q-td>
 | 
				
			||||||
          <q-icon v-if="props.row.hide_in_ui" name="check" />
 | 
					          <q-icon v-if="props.row.hide_in_ui" name="check" />
 | 
				
			||||||
        </q-td>
 | 
					        </q-td>
 | 
				
			||||||
 | 
					        <!-- hide in summary tab -->
 | 
				
			||||||
 | 
					        <q-td>
 | 
				
			||||||
 | 
					          <q-icon v-if="props.row.hide_in_summary" name="check" />
 | 
				
			||||||
 | 
					        </q-td>
 | 
				
			||||||
        <!-- default value -->
 | 
					        <!-- default value -->
 | 
				
			||||||
        <q-td v-if="props.row.type === 'checkbox'">
 | 
					        <q-td v-if="props.row.type === 'checkbox'">
 | 
				
			||||||
          {{ props.row.default_value_bool }}
 | 
					          {{ props.row.default_value_bool }}
 | 
				
			||||||
@@ -123,6 +127,13 @@ export default {
 | 
				
			|||||||
          align: "left",
 | 
					          align: "left",
 | 
				
			||||||
          sortable: true,
 | 
					          sortable: true,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          name: "hide_in_summary",
 | 
				
			||||||
 | 
					          label: "Hide in Summary Tab",
 | 
				
			||||||
 | 
					          field: "hide_in_summary",
 | 
				
			||||||
 | 
					          align: "left",
 | 
				
			||||||
 | 
					          sortable: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          name: "default_value",
 | 
					          name: "default_value",
 | 
				
			||||||
          label: "Default Value",
 | 
					          label: "Default Value",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -71,7 +71,7 @@
 | 
				
			|||||||
                        icon="info"
 | 
					                        icon="info"
 | 
				
			||||||
                        @click="
 | 
					                        @click="
 | 
				
			||||||
                          openURL(
 | 
					                          openURL(
 | 
				
			||||||
                            'https://quasar.dev/quasar-utils/date-utils#format-for-display'
 | 
					                            'https://quasar.dev/quasar-utils/date-utils#format-for-display',
 | 
				
			||||||
                          )
 | 
					                          )
 | 
				
			||||||
                        "
 | 
					                        "
 | 
				
			||||||
                      >
 | 
					                      >
 | 
				
			||||||
@@ -216,7 +216,7 @@
 | 
				
			|||||||
                <div class="text-subtitle2">SMTP Settings</div>
 | 
					                <div class="text-subtitle2">SMTP Settings</div>
 | 
				
			||||||
                <q-separator />
 | 
					                <q-separator />
 | 
				
			||||||
                <q-card-section class="row">
 | 
					                <q-card-section class="row">
 | 
				
			||||||
                  <div class="col-2">From:</div>
 | 
					                  <div class="col-2">From email:</div>
 | 
				
			||||||
                  <div class="col-4"></div>
 | 
					                  <div class="col-4"></div>
 | 
				
			||||||
                  <q-input
 | 
					                  <q-input
 | 
				
			||||||
                    outlined
 | 
					                    outlined
 | 
				
			||||||
@@ -226,6 +226,16 @@
 | 
				
			|||||||
                    :rules="[(val) => isValidEmail(val) || 'Invalid email']"
 | 
					                    :rules="[(val) => isValidEmail(val) || 'Invalid email']"
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
                </q-card-section>
 | 
					                </q-card-section>
 | 
				
			||||||
 | 
					                <q-card-section class="row">
 | 
				
			||||||
 | 
					                  <div class="col-2">From name:</div>
 | 
				
			||||||
 | 
					                  <div class="col-4"></div>
 | 
				
			||||||
 | 
					                  <q-input
 | 
				
			||||||
 | 
					                    outlined
 | 
				
			||||||
 | 
					                    dense
 | 
				
			||||||
 | 
					                    v-model="settings.smtp_from_name"
 | 
				
			||||||
 | 
					                    class="col-6 q-pa-none"
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                </q-card-section>
 | 
				
			||||||
                <q-card-section class="row">
 | 
					                <q-card-section class="row">
 | 
				
			||||||
                  <div class="col-2">Host:</div>
 | 
					                  <div class="col-2">Host:</div>
 | 
				
			||||||
                  <div class="col-4"></div>
 | 
					                  <div class="col-4"></div>
 | 
				
			||||||
@@ -379,7 +389,7 @@
 | 
				
			|||||||
              <q-tab-panel name="meshcentral">
 | 
					              <q-tab-panel name="meshcentral">
 | 
				
			||||||
                <div class="text-subtitle2">MeshCentral Settings</div>
 | 
					                <div class="text-subtitle2">MeshCentral Settings</div>
 | 
				
			||||||
                <q-separator />
 | 
					                <q-separator />
 | 
				
			||||||
                <q-card-section class="row">
 | 
					                <q-card-section class="row" v-if="!hosted">
 | 
				
			||||||
                  <div class="col-4">Username:</div>
 | 
					                  <div class="col-4">Username:</div>
 | 
				
			||||||
                  <div class="col-2"></div>
 | 
					                  <div class="col-2"></div>
 | 
				
			||||||
                  <q-input
 | 
					                  <q-input
 | 
				
			||||||
@@ -395,7 +405,7 @@
 | 
				
			|||||||
                    ]"
 | 
					                    ]"
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
                </q-card-section>
 | 
					                </q-card-section>
 | 
				
			||||||
                <q-card-section class="row">
 | 
					                <q-card-section class="row" v-if="!hosted">
 | 
				
			||||||
                  <div class="col-4">Mesh Site:</div>
 | 
					                  <div class="col-4">Mesh Site:</div>
 | 
				
			||||||
                  <div class="col-2"></div>
 | 
					                  <div class="col-2"></div>
 | 
				
			||||||
                  <q-input
 | 
					                  <q-input
 | 
				
			||||||
@@ -405,7 +415,7 @@
 | 
				
			|||||||
                    class="col-6"
 | 
					                    class="col-6"
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
                </q-card-section>
 | 
					                </q-card-section>
 | 
				
			||||||
                <q-card-section class="row">
 | 
					                <q-card-section class="row" v-if="!hosted">
 | 
				
			||||||
                  <div class="col-4">Mesh Token:</div>
 | 
					                  <div class="col-4">Mesh Token:</div>
 | 
				
			||||||
                  <div class="col-2"></div>
 | 
					                  <div class="col-2"></div>
 | 
				
			||||||
                  <q-input
 | 
					                  <q-input
 | 
				
			||||||
@@ -415,7 +425,7 @@
 | 
				
			|||||||
                    class="col-6"
 | 
					                    class="col-6"
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
                </q-card-section>
 | 
					                </q-card-section>
 | 
				
			||||||
                <q-card-section class="row">
 | 
					                <q-card-section class="row" v-if="!hosted">
 | 
				
			||||||
                  <div class="col-4">Mesh Device Group Name:</div>
 | 
					                  <div class="col-4">Mesh Device Group Name:</div>
 | 
				
			||||||
                  <div class="col-2"></div>
 | 
					                  <div class="col-2"></div>
 | 
				
			||||||
                  <q-input
 | 
					                  <q-input
 | 
				
			||||||
@@ -425,17 +435,58 @@
 | 
				
			|||||||
                    class="col-6"
 | 
					                    class="col-6"
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
                </q-card-section>
 | 
					                </q-card-section>
 | 
				
			||||||
                <q-card-section class="row">
 | 
					                <q-card-section class="row" v-if="!hosted">
 | 
				
			||||||
                  <div class="col-4">
 | 
					                  <div class="col-4 flex items-center">
 | 
				
			||||||
                    Disable Auto Login for Remote Control and Remote background:
 | 
					                    Sync Mesh Perms with TRMM:
 | 
				
			||||||
 | 
					                    <q-icon
 | 
				
			||||||
 | 
					                      right
 | 
				
			||||||
 | 
					                      name="ion-information-circle-outline"
 | 
				
			||||||
 | 
					                      size="sm"
 | 
				
			||||||
 | 
					                      class="cursor-pointer"
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                      <q-tooltip class="text-caption">
 | 
				
			||||||
 | 
					                        It is recommended to keep this option enabled;
 | 
				
			||||||
 | 
					                        otherwise, all TRMM users will have full permissions in
 | 
				
			||||||
 | 
					                        MeshCentral regardless of their permissions in TRMM.
 | 
				
			||||||
 | 
					                      </q-tooltip>
 | 
				
			||||||
 | 
					                    </q-icon>
 | 
				
			||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
                  <div class="col-2"></div>
 | 
					                  <div class="col-2"></div>
 | 
				
			||||||
                  <q-checkbox
 | 
					                  <q-checkbox
 | 
				
			||||||
                    dense
 | 
					                    dense
 | 
				
			||||||
                    v-model="settings.mesh_disable_auto_login"
 | 
					                    :model-value="settings.sync_mesh_with_trmm"
 | 
				
			||||||
 | 
					                    @update:model-value="confirmSyncChange"
 | 
				
			||||||
                    class="col-6"
 | 
					                    class="col-6"
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
                </q-card-section>
 | 
					                </q-card-section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <q-card-section class="row items-center">
 | 
				
			||||||
 | 
					                  <div class="col-4 flex items-center">
 | 
				
			||||||
 | 
					                    Company Name:
 | 
				
			||||||
 | 
					                    <q-icon
 | 
				
			||||||
 | 
					                      name="ion-information-circle-outline"
 | 
				
			||||||
 | 
					                      size="sm"
 | 
				
			||||||
 | 
					                      class="q-ml-sm cursor-pointer"
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                      <q-tooltip class="text-caption">
 | 
				
			||||||
 | 
					                        Adding your company name here will append it to the
 | 
				
			||||||
 | 
					                        user's full name that appears when doing a remote
 | 
				
			||||||
 | 
					                        control session, for example: 'John Doe - Amidaware
 | 
				
			||||||
 | 
					                        Inc.'
 | 
				
			||||||
 | 
					                      </q-tooltip>
 | 
				
			||||||
 | 
					                    </q-icon>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  <div class="col-2"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  <q-input
 | 
				
			||||||
 | 
					                    dense
 | 
				
			||||||
 | 
					                    outlined
 | 
				
			||||||
 | 
					                    v-model="settings.mesh_company_name"
 | 
				
			||||||
 | 
					                    class="col-6"
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                  </q-input>
 | 
				
			||||||
 | 
					                </q-card-section>
 | 
				
			||||||
              </q-tab-panel>
 | 
					              </q-tab-panel>
 | 
				
			||||||
              <q-tab-panel name="customfields">
 | 
					              <q-tab-panel name="customfields">
 | 
				
			||||||
                <CustomFields />
 | 
					                <CustomFields />
 | 
				
			||||||
@@ -635,6 +686,11 @@ export default {
 | 
				
			|||||||
      ],
 | 
					      ],
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    hosted() {
 | 
				
			||||||
 | 
					      return this.$store.state.hosted;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    openURL(url) {
 | 
					    openURL(url) {
 | 
				
			||||||
      openURL(url);
 | 
					      openURL(url);
 | 
				
			||||||
@@ -669,6 +725,19 @@ export default {
 | 
				
			|||||||
        }));
 | 
					        }));
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    confirmSyncChange(newValue) {
 | 
				
			||||||
 | 
					      this.$q
 | 
				
			||||||
 | 
					        .dialog({
 | 
				
			||||||
 | 
					          title: "Are you sure?",
 | 
				
			||||||
 | 
					          message:
 | 
				
			||||||
 | 
					            "This operation may take several minutes to complete in the background and can be very CPU/disk intensive, depending on your hardware and number of agents. Please allow time for the sync to fully complete.",
 | 
				
			||||||
 | 
					          ok: { label: "Yes", color: "primary" },
 | 
				
			||||||
 | 
					          cancel: { label: "No", color: "negative" },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .onOk(() => {
 | 
				
			||||||
 | 
					          this.settings.sync_mesh_with_trmm = newValue;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    showResetPatchPolicy() {
 | 
					    showResetPatchPolicy() {
 | 
				
			||||||
      this.$q.dialog({
 | 
					      this.$q.dialog({
 | 
				
			||||||
        component: ResetPatchPolicy,
 | 
					        component: ResetPatchPolicy,
 | 
				
			||||||
@@ -711,13 +780,13 @@ export default {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    removeEmail(email) {
 | 
					    removeEmail(email) {
 | 
				
			||||||
      const removed = this.settings.email_alert_recipients.filter(
 | 
					      const removed = this.settings.email_alert_recipients.filter(
 | 
				
			||||||
        (k) => k !== email
 | 
					        (k) => k !== email,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      this.settings.email_alert_recipients = removed;
 | 
					      this.settings.email_alert_recipients = removed;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    removeSMSNumber(num) {
 | 
					    removeSMSNumber(num) {
 | 
				
			||||||
      const removed = this.settings.sms_alert_recipients.filter(
 | 
					      const removed = this.settings.sms_alert_recipients.filter(
 | 
				
			||||||
        (k) => k !== num
 | 
					        (k) => k !== num,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      this.settings.sms_alert_recipients = removed;
 | 
					      this.settings.sms_alert_recipients = removed;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <q-dialog ref="dialog" @hide="onHide">
 | 
					  <q-dialog ref="dialog" @hide="onHide">
 | 
				
			||||||
    <q-card class="q-dialog-plugin" style="min-width: 85vh">
 | 
					    <q-card class="q-dialog-plugin" style="min-width: 60vw">
 | 
				
			||||||
      <q-splitter v-model="splitterModel">
 | 
					      <q-splitter v-model="splitterModel">
 | 
				
			||||||
        <template v-slot:before>
 | 
					        <template v-slot:before>
 | 
				
			||||||
          <q-tabs dense v-model="tab" vertical class="text-primary">
 | 
					          <q-tabs dense v-model="tab" vertical class="text-primary">
 | 
				
			||||||
@@ -201,7 +201,7 @@
 | 
				
			|||||||
                        icon="info"
 | 
					                        icon="info"
 | 
				
			||||||
                        @click="
 | 
					                        @click="
 | 
				
			||||||
                          openURL(
 | 
					                          openURL(
 | 
				
			||||||
                            'https://quasar.dev/quasar-utils/date-utils#format-for-display'
 | 
					                            'https://quasar.dev/quasar-utils/date-utils#format-for-display',
 | 
				
			||||||
                          )
 | 
					                          )
 | 
				
			||||||
                        "
 | 
					                        "
 | 
				
			||||||
                      >
 | 
					                      >
 | 
				
			||||||
@@ -315,7 +315,7 @@ export default {
 | 
				
			|||||||
      this.$axios.get("/core/urlaction/").then((r) => {
 | 
					      this.$axios.get("/core/urlaction/").then((r) => {
 | 
				
			||||||
        if (r.data.length === 0) {
 | 
					        if (r.data.length === 0) {
 | 
				
			||||||
          this.notifyWarning(
 | 
					          this.notifyWarning(
 | 
				
			||||||
            "No URL Actions configured. Go to Settings > Global Settings > URL Actions"
 | 
					            "No URL Actions configured. Go to Settings > Global Settings > URL Actions",
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          return;
 | 
					          return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,11 @@
 | 
				
			|||||||
  <q-dialog
 | 
					  <q-dialog
 | 
				
			||||||
    ref="dialogRef"
 | 
					    ref="dialogRef"
 | 
				
			||||||
    maximized
 | 
					    maximized
 | 
				
			||||||
 | 
					    no-esc-dismiss
 | 
				
			||||||
    @hide="onDialogHide"
 | 
					    @hide="onDialogHide"
 | 
				
			||||||
    @show="loadEditor"
 | 
					    @show="loadEditor"
 | 
				
			||||||
    @before-hide="unloadEditor"
 | 
					    @before-hide="unloadEditor"
 | 
				
			||||||
 | 
					    @keydown.esc.stop="closeEditor"
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    <q-card class="q-dialog-plugin">
 | 
					    <q-card class="q-dialog-plugin">
 | 
				
			||||||
      <q-bar>
 | 
					      <q-bar>
 | 
				
			||||||
@@ -20,7 +22,7 @@
 | 
				
			|||||||
          @click="generateScriptOpenAI"
 | 
					          @click="generateScriptOpenAI"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        <q-space />
 | 
					        <q-space />
 | 
				
			||||||
        <q-btn dense flat icon="close" v-close-popup>
 | 
					        <q-btn dense flat icon="close" @click="closeEditor">
 | 
				
			||||||
          <q-tooltip class="bg-white text-primary">Close</q-tooltip>
 | 
					          <q-tooltip class="bg-white text-primary">Close</q-tooltip>
 | 
				
			||||||
        </q-btn>
 | 
					        </q-btn>
 | 
				
			||||||
      </q-bar>
 | 
					      </q-bar>
 | 
				
			||||||
@@ -190,7 +192,7 @@
 | 
				
			|||||||
          </template>
 | 
					          </template>
 | 
				
			||||||
        </tactical-dropdown>
 | 
					        </tactical-dropdown>
 | 
				
			||||||
        <q-space />
 | 
					        <q-space />
 | 
				
			||||||
        <q-btn dense flat label="Cancel" v-close-popup />
 | 
					        <q-btn dense flat label="Cancel" @click="closeEditor" />
 | 
				
			||||||
        <q-btn
 | 
					        <q-btn
 | 
				
			||||||
          v-if="!readonly"
 | 
					          v-if="!readonly"
 | 
				
			||||||
          :loading="loading"
 | 
					          :loading="loading"
 | 
				
			||||||
@@ -220,6 +222,35 @@ import TestScriptModal from "@/components/scripts/TestScriptModal.vue";
 | 
				
			|||||||
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
 | 
					import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
 | 
				
			||||||
import * as monaco from "monaco-editor";
 | 
					import * as monaco from "monaco-editor";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
 | 
				
			||||||
 | 
					import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
 | 
				
			||||||
 | 
					import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
 | 
				
			||||||
 | 
					import jsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
 | 
				
			||||||
 | 
					import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// https://github.com/microsoft/monaco-editor/issues/4045#issuecomment-1723787448
 | 
				
			||||||
 | 
					self.MonacoEnvironment = {
 | 
				
			||||||
 | 
					  getWorker: function (workerId, label) {
 | 
				
			||||||
 | 
					    switch (label) {
 | 
				
			||||||
 | 
					      case "json":
 | 
				
			||||||
 | 
					        return new jsonWorker();
 | 
				
			||||||
 | 
					      case "css":
 | 
				
			||||||
 | 
					      case "scss":
 | 
				
			||||||
 | 
					      case "less":
 | 
				
			||||||
 | 
					        return new cssWorker();
 | 
				
			||||||
 | 
					      case "html":
 | 
				
			||||||
 | 
					      case "handlebars":
 | 
				
			||||||
 | 
					      case "razor":
 | 
				
			||||||
 | 
					        return new htmlWorker();
 | 
				
			||||||
 | 
					      case "typescript":
 | 
				
			||||||
 | 
					      case "javascript":
 | 
				
			||||||
 | 
					        return new jsWorker();
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        return new editorWorker();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// types
 | 
					// types
 | 
				
			||||||
import type { Script } from "@/types/scripts";
 | 
					import type { Script } from "@/types/scripts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -285,8 +316,8 @@ const title = computed(() => {
 | 
				
			|||||||
    return props.readonly
 | 
					    return props.readonly
 | 
				
			||||||
      ? `Viewing ${script.name}`
 | 
					      ? `Viewing ${script.name}`
 | 
				
			||||||
      : props.clone
 | 
					      : props.clone
 | 
				
			||||||
      ? `Copying ${script.name}`
 | 
					        ? `Copying ${script.name}`
 | 
				
			||||||
      : `Editing ${script.name}`;
 | 
					        : `Editing ${script.name}`;
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    return "Adding new script";
 | 
					    return "Adding new script";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -294,11 +325,21 @@ const title = computed(() => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// convert highlighter language to match what ace expects
 | 
					// convert highlighter language to match what ace expects
 | 
				
			||||||
const lang = computed(() => {
 | 
					const lang = computed(() => {
 | 
				
			||||||
  if (script.shell === "cmd") return "bat";
 | 
					  switch (script.shell) {
 | 
				
			||||||
  else if (script.shell === "powershell") return "powershell";
 | 
					    case "cmd":
 | 
				
			||||||
  else if (script.shell === "python") return "python";
 | 
					      return "bat";
 | 
				
			||||||
  else if (script.shell === "shell") return "shell";
 | 
					    case "powershell":
 | 
				
			||||||
  else return "";
 | 
					      return "powershell";
 | 
				
			||||||
 | 
					    case "python":
 | 
				
			||||||
 | 
					      return "python";
 | 
				
			||||||
 | 
					    case "shell":
 | 
				
			||||||
 | 
					    case "nushell":
 | 
				
			||||||
 | 
					      return "shell";
 | 
				
			||||||
 | 
					    case "deno":
 | 
				
			||||||
 | 
					      return "typescript";
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return "";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function submit() {
 | 
					async function submit() {
 | 
				
			||||||
@@ -337,12 +378,7 @@ const scriptEditor = ref<HTMLElement | null>(null);
 | 
				
			|||||||
let editor: monaco.editor.IStandaloneCodeEditor;
 | 
					let editor: monaco.editor.IStandaloneCodeEditor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function loadEditor() {
 | 
					function loadEditor() {
 | 
				
			||||||
  var modelUri = monaco.Uri.parse("model://new"); // a made up unique URI for our model
 | 
					  var model = monaco.editor.createModel(script.script_body, lang.value);
 | 
				
			||||||
  var model = monaco.editor.createModel(
 | 
					 | 
				
			||||||
    script.script_body,
 | 
					 | 
				
			||||||
    lang.value,
 | 
					 | 
				
			||||||
    modelUri,
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const theme = $q.dark.isActive ? "vs-dark" : "vs-light";
 | 
					  const theme = $q.dark.isActive ? "vs-dark" : "vs-light";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -363,7 +399,23 @@ function loadEditor() {
 | 
				
			|||||||
    downloadScript(script.id, { with_snippets: props.readonly }).then((r) => {
 | 
					    downloadScript(script.id, { with_snippets: props.readonly }).then((r) => {
 | 
				
			||||||
      script.script_body = r.code;
 | 
					      script.script_body = r.code;
 | 
				
			||||||
      editor.setValue(r.code);
 | 
					      editor.setValue(r.code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // need to add this in the download function otherwise the above will trigger an edit
 | 
				
			||||||
 | 
					      watch(
 | 
				
			||||||
 | 
					        () => script.script_body,
 | 
				
			||||||
 | 
					        () => {
 | 
				
			||||||
 | 
					          edited.value = true;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					  else {
 | 
				
			||||||
 | 
					    watch(
 | 
				
			||||||
 | 
					      () => script.script_body,
 | 
				
			||||||
 | 
					      () => {
 | 
				
			||||||
 | 
					        edited.value = true;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // watch for changes in language
 | 
					  // watch for changes in language
 | 
				
			||||||
  watch(lang, () => {
 | 
					  watch(lang, () => {
 | 
				
			||||||
@@ -394,6 +446,21 @@ function generateScriptOpenAI() {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// add are you sure prompt to unsaved script
 | 
				
			||||||
 | 
					const edited = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function closeEditor() {
 | 
				
			||||||
 | 
					  if (edited.value)
 | 
				
			||||||
 | 
					    $q.dialog({
 | 
				
			||||||
 | 
					      title: "You have unsaved changes. Are you sure you want to close?",
 | 
				
			||||||
 | 
					      cancel: true,
 | 
				
			||||||
 | 
					      ok: true,
 | 
				
			||||||
 | 
					    }).onOk(async () => {
 | 
				
			||||||
 | 
					      unloadEditor();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  else unloadEditor();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// component life cycle hooks
 | 
					// component life cycle hooks
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
  agentLoading.value = true;
 | 
					  agentLoading.value = true;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,6 +175,28 @@
 | 
				
			|||||||
              >
 | 
					              >
 | 
				
			||||||
                <q-tooltip> Shell </q-tooltip>
 | 
					                <q-tooltip> Shell </q-tooltip>
 | 
				
			||||||
              </q-icon>
 | 
					              </q-icon>
 | 
				
			||||||
 | 
					              <q-icon
 | 
				
			||||||
 | 
					                v-else-if="props.node.shell === 'nushell'"
 | 
				
			||||||
 | 
					                name="mdi-code-greater-than"
 | 
				
			||||||
 | 
					                color="primary"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <q-tooltip> Nushell </q-tooltip>
 | 
				
			||||||
 | 
					              </q-icon>
 | 
				
			||||||
 | 
					              <q-icon
 | 
				
			||||||
 | 
					                v-else-if="props.node.shell === 'deno'"
 | 
				
			||||||
 | 
					                name="mdi-language-typescript"
 | 
				
			||||||
 | 
					                color="primary"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <q-tooltip> Deno </q-tooltip>
 | 
				
			||||||
 | 
					              </q-icon>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              <!-- is community script icon -->
 | 
				
			||||||
 | 
					              <img
 | 
				
			||||||
 | 
					                v-if="props.node.script_type === 'builtin'"
 | 
				
			||||||
 | 
					                class="vertical-middle"
 | 
				
			||||||
 | 
					                :src="trmmLogo"
 | 
				
			||||||
 | 
					                style="height: 20px; max-width: 20px"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <span
 | 
					              <span
 | 
				
			||||||
                class="q-pl-xs text-weight-bold"
 | 
					                class="q-pl-xs text-weight-bold"
 | 
				
			||||||
@@ -463,6 +485,22 @@
 | 
				
			|||||||
              >
 | 
					              >
 | 
				
			||||||
                <q-tooltip> Shell </q-tooltip>
 | 
					                <q-tooltip> Shell </q-tooltip>
 | 
				
			||||||
              </q-icon>
 | 
					              </q-icon>
 | 
				
			||||||
 | 
					              <q-icon
 | 
				
			||||||
 | 
					                v-else-if="props.row.shell === 'nushell'"
 | 
				
			||||||
 | 
					                size="sm"
 | 
				
			||||||
 | 
					                name="mdi-code-greater-than"
 | 
				
			||||||
 | 
					                color="primary"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <q-tooltip> Nushell </q-tooltip>
 | 
				
			||||||
 | 
					              </q-icon>
 | 
				
			||||||
 | 
					              <q-icon
 | 
				
			||||||
 | 
					                v-else-if="props.row.shell === 'deno'"
 | 
				
			||||||
 | 
					                size="sm"
 | 
				
			||||||
 | 
					                name="mdi-language-typescript"
 | 
				
			||||||
 | 
					                color="primary"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <q-tooltip> Deno </q-tooltip>
 | 
				
			||||||
 | 
					              </q-icon>
 | 
				
			||||||
            </q-td>
 | 
					            </q-td>
 | 
				
			||||||
            <!-- supported platforms -->
 | 
					            <!-- supported platforms -->
 | 
				
			||||||
            <q-td key="supported_platforms" :props="props">
 | 
					            <q-td key="supported_platforms" :props="props">
 | 
				
			||||||
@@ -488,6 +526,12 @@
 | 
				
			|||||||
              :props="props"
 | 
					              :props="props"
 | 
				
			||||||
              :style="{ color: props.row.hidden ? 'grey' : '' }"
 | 
					              :style="{ color: props.row.hidden ? 'grey' : '' }"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
 | 
					              <!-- is community script icon -->
 | 
				
			||||||
 | 
					              <img
 | 
				
			||||||
 | 
					                v-if="props.row.script_type === 'builtin'"
 | 
				
			||||||
 | 
					                :src="trmmLogo"
 | 
				
			||||||
 | 
					                style="height: 20px; max-width: 20px"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
              {{ truncateText(props.row.name, 50) }}
 | 
					              {{ truncateText(props.row.name, 50) }}
 | 
				
			||||||
              <q-tooltip
 | 
					              <q-tooltip
 | 
				
			||||||
                v-if="props.row.name.length >= 50"
 | 
					                v-if="props.row.name.length >= 50"
 | 
				
			||||||
@@ -550,6 +594,8 @@ import ScriptFormModal from "@/components/scripts/ScriptFormModal.vue";
 | 
				
			|||||||
import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue";
 | 
					import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue";
 | 
				
			||||||
import TacticalTable from "@/components/ui/TacticalTable.vue";
 | 
					import TacticalTable from "@/components/ui/TacticalTable.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import trmmLogo from "@/assets/trmm_256.png";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// static data
 | 
					// static data
 | 
				
			||||||
const columns = [
 | 
					const columns = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
@@ -620,7 +666,7 @@ export default {
 | 
				
			|||||||
    // setup vuex store
 | 
					    // setup vuex store
 | 
				
			||||||
    const store = useStore();
 | 
					    const store = useStore();
 | 
				
			||||||
    const showCommunityScripts = computed(
 | 
					    const showCommunityScripts = computed(
 | 
				
			||||||
      () => store.state.showCommunityScripts
 | 
					      () => store.state.showCommunityScripts,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // setup quasar plugins
 | 
					    // setup quasar plugins
 | 
				
			||||||
@@ -721,7 +767,7 @@ export default {
 | 
				
			|||||||
        return showCommunityScripts.value
 | 
					        return showCommunityScripts.value
 | 
				
			||||||
          ? scripts.value.filter((i) => !i.hidden)
 | 
					          ? scripts.value.filter((i) => !i.hidden)
 | 
				
			||||||
          : scripts.value.filter(
 | 
					          : scripts.value.filter(
 | 
				
			||||||
              (i) => i.script_type !== "builtin" && !i.hidden
 | 
					              (i) => i.script_type !== "builtin" && !i.hidden,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -884,6 +930,7 @@ export default {
 | 
				
			|||||||
      loading,
 | 
					      loading,
 | 
				
			||||||
      showCommunityScripts,
 | 
					      showCommunityScripts,
 | 
				
			||||||
      showHiddenScripts,
 | 
					      showHiddenScripts,
 | 
				
			||||||
 | 
					      trmmLogo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // computed
 | 
					      // computed
 | 
				
			||||||
      visibleScripts,
 | 
					      visibleScripts,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,6 +86,35 @@ import { notifySuccess } from "@/utils/notify";
 | 
				
			|||||||
// ui imports
 | 
					// ui imports
 | 
				
			||||||
import * as monaco from "monaco-editor";
 | 
					import * as monaco from "monaco-editor";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
 | 
				
			||||||
 | 
					import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
 | 
				
			||||||
 | 
					import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
 | 
				
			||||||
 | 
					import jsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
 | 
				
			||||||
 | 
					import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// https://github.com/microsoft/monaco-editor/issues/4045#issuecomment-1723787448
 | 
				
			||||||
 | 
					self.MonacoEnvironment = {
 | 
				
			||||||
 | 
					  getWorker: function (workerId, label) {
 | 
				
			||||||
 | 
					    switch (label) {
 | 
				
			||||||
 | 
					      case "json":
 | 
				
			||||||
 | 
					        return new jsonWorker();
 | 
				
			||||||
 | 
					      case "css":
 | 
				
			||||||
 | 
					      case "scss":
 | 
				
			||||||
 | 
					      case "less":
 | 
				
			||||||
 | 
					        return new cssWorker();
 | 
				
			||||||
 | 
					      case "html":
 | 
				
			||||||
 | 
					      case "handlebars":
 | 
				
			||||||
 | 
					      case "razor":
 | 
				
			||||||
 | 
					        return new htmlWorker();
 | 
				
			||||||
 | 
					      case "typescript":
 | 
				
			||||||
 | 
					      case "javascript":
 | 
				
			||||||
 | 
					        return new jsWorker();
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        return new editorWorker();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// types
 | 
					// types
 | 
				
			||||||
import type { ScriptSnippet } from "@/types/scripts";
 | 
					import type { ScriptSnippet } from "@/types/scripts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -124,11 +153,21 @@ const title = computed(() => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// convert highlighter language to match what ace expects
 | 
					// convert highlighter language to match what ace expects
 | 
				
			||||||
const lang = computed(() => {
 | 
					const lang = computed(() => {
 | 
				
			||||||
  if (snippet.shell === "cmd") return "bat";
 | 
					  switch (snippet.shell) {
 | 
				
			||||||
  else if (snippet.shell === "powershell") return "powershell";
 | 
					    case "cmd":
 | 
				
			||||||
  else if (snippet.shell === "python") return "python";
 | 
					      return "bat";
 | 
				
			||||||
  else if (snippet.shell === "shell") return "shell";
 | 
					    case "powershell":
 | 
				
			||||||
  else return "";
 | 
					      return "powershell";
 | 
				
			||||||
 | 
					    case "python":
 | 
				
			||||||
 | 
					      return "python";
 | 
				
			||||||
 | 
					    case "shell":
 | 
				
			||||||
 | 
					    case "nushell":
 | 
				
			||||||
 | 
					      return "shell";
 | 
				
			||||||
 | 
					    case "deno":
 | 
				
			||||||
 | 
					      return "typescript";
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return "";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function submit() {
 | 
					async function submit() {
 | 
				
			||||||
@@ -150,8 +189,7 @@ const snippetEditor = ref<HTMLElement | null>(null);
 | 
				
			|||||||
let editor: monaco.editor.IStandaloneCodeEditor;
 | 
					let editor: monaco.editor.IStandaloneCodeEditor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function loadEditor() {
 | 
					function loadEditor() {
 | 
				
			||||||
  var modelUri = monaco.Uri.parse("model://snippet"); // a made up unique URI for our model
 | 
					  var model = monaco.editor.createModel(snippet.code, lang.value);
 | 
				
			||||||
  var model = monaco.editor.createModel(snippet.code, lang.value, modelUri);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const theme = $q.dark.isActive ? "vs-dark" : "vs-light";
 | 
					  const theme = $q.dark.isActive ? "vs-dark" : "vs-light";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -124,6 +124,22 @@
 | 
				
			|||||||
              >
 | 
					              >
 | 
				
			||||||
                <q-tooltip> Shell </q-tooltip>
 | 
					                <q-tooltip> Shell </q-tooltip>
 | 
				
			||||||
              </q-icon>
 | 
					              </q-icon>
 | 
				
			||||||
 | 
					              <q-icon
 | 
				
			||||||
 | 
					                v-else-if="props.row.shell === 'nushell'"
 | 
				
			||||||
 | 
					                name="mdi-nushell"
 | 
				
			||||||
 | 
					                color="primary"
 | 
				
			||||||
 | 
					                size="sm"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <q-tooltip> Nushell </q-tooltip>
 | 
				
			||||||
 | 
					              </q-icon>
 | 
				
			||||||
 | 
					              <q-icon
 | 
				
			||||||
 | 
					                v-else-if="props.row.shell === 'deno'"
 | 
				
			||||||
 | 
					                name="mdi-typescript"
 | 
				
			||||||
 | 
					                color="primary"
 | 
				
			||||||
 | 
					                size="sm"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <q-tooltip> Deno </q-tooltip>
 | 
				
			||||||
 | 
					              </q-icon>
 | 
				
			||||||
            </q-td>
 | 
					            </q-td>
 | 
				
			||||||
            <!-- name -->
 | 
					            <!-- name -->
 | 
				
			||||||
            <q-td>{{ props.row.name }}</q-td>
 | 
					            <q-td>{{ props.row.name }}</q-td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,8 +8,25 @@
 | 
				
			|||||||
          <q-tooltip class="bg-white text-primary">Close</q-tooltip>
 | 
					          <q-tooltip class="bg-white text-primary">Close</q-tooltip>
 | 
				
			||||||
        </q-btn>
 | 
					        </q-btn>
 | 
				
			||||||
      </q-bar>
 | 
					      </q-bar>
 | 
				
			||||||
      <q-card-section class="scroll" style="max-height: 70vh; height: 70vh">
 | 
					      <q-card-section style="height: 70vh" class="scroll">
 | 
				
			||||||
        <pre v-if="ret">{{ ret }}</pre>
 | 
					        <div>
 | 
				
			||||||
 | 
					          Run Time:
 | 
				
			||||||
 | 
					          <code>{{ ret.execution_time }} seconds</code>
 | 
				
			||||||
 | 
					          <br />Return Code:
 | 
				
			||||||
 | 
					          <code>{{ ret.retcode }}</code>
 | 
				
			||||||
 | 
					          <br />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <br />
 | 
				
			||||||
 | 
					        <div v-if="ret.stdout">
 | 
				
			||||||
 | 
					          Standard Output
 | 
				
			||||||
 | 
					          <q-separator />
 | 
				
			||||||
 | 
					          <pre>{{ ret.stdout }}</pre>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div v-if="ret.stderr">
 | 
				
			||||||
 | 
					          Standard Error
 | 
				
			||||||
 | 
					          <q-separator />
 | 
				
			||||||
 | 
					          <pre>{{ ret.stderr }}</pre>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
        <q-inner-loading :showing="loading" />
 | 
					        <q-inner-loading :showing="loading" />
 | 
				
			||||||
      </q-card-section>
 | 
					      </q-card-section>
 | 
				
			||||||
    </q-card>
 | 
					    </q-card>
 | 
				
			||||||
@@ -34,7 +51,12 @@ export default {
 | 
				
			|||||||
    const { dialogRef, onDialogHide } = useDialogPluginComponent();
 | 
					    const { dialogRef, onDialogHide } = useDialogPluginComponent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // main run script functionality
 | 
					    // main run script functionality
 | 
				
			||||||
    const ret = ref(null);
 | 
					    const ret = ref({
 | 
				
			||||||
 | 
					      execution_time: "",
 | 
				
			||||||
 | 
					      retcode: "",
 | 
				
			||||||
 | 
					      stdout: "",
 | 
				
			||||||
 | 
					      stderr: "",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    const loading = ref(false);
 | 
					    const loading = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function runTestScript() {
 | 
					    async function runTestScript() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,181 +87,183 @@
 | 
				
			|||||||
          :done="step > 2"
 | 
					          :done="step > 2"
 | 
				
			||||||
          :error="!isValidStep2"
 | 
					          :error="!isValidStep2"
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <q-form @submit.prevent="addAction">
 | 
					          <div class="scroll" style="max-height: 60vh">
 | 
				
			||||||
            <div class="row q-pa-sm q-gutter-x-xs items-center">
 | 
					            <q-form @submit.prevent="addAction">
 | 
				
			||||||
              <div class="text-subtitle2 col-12">Action Type:</div>
 | 
					              <div class="row q-pa-sm q-gutter-x-xs items-center">
 | 
				
			||||||
              <q-option-group
 | 
					                <div class="text-subtitle2 col-12">Action Type:</div>
 | 
				
			||||||
                class="col-12"
 | 
					                <q-option-group
 | 
				
			||||||
                inline
 | 
					                  class="col-12"
 | 
				
			||||||
                v-model="actionType"
 | 
					                  inline
 | 
				
			||||||
                :options="[
 | 
					                  v-model="actionType"
 | 
				
			||||||
                  { label: 'Script', value: 'script' },
 | 
					                  :options="[
 | 
				
			||||||
                  { label: 'Command', value: 'cmd' },
 | 
					                    { label: 'Script', value: 'script' },
 | 
				
			||||||
                ]"
 | 
					                    { label: 'Command', value: 'cmd' },
 | 
				
			||||||
              />
 | 
					                  ]"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <tactical-dropdown
 | 
					                <tactical-dropdown
 | 
				
			||||||
                v-if="actionType === 'script'"
 | 
					                  v-if="actionType === 'script'"
 | 
				
			||||||
                class="col-3"
 | 
					                  class="col-3"
 | 
				
			||||||
                label="Select script"
 | 
					                  label="Select script"
 | 
				
			||||||
                v-model="script"
 | 
					                  v-model="script"
 | 
				
			||||||
                :options="scriptOptions"
 | 
					                  :options="scriptOptions"
 | 
				
			||||||
                filled
 | 
					                  filled
 | 
				
			||||||
                mapOptions
 | 
					                  mapOptions
 | 
				
			||||||
                filterable
 | 
					                  filterable
 | 
				
			||||||
              />
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <q-select
 | 
					                <q-select
 | 
				
			||||||
                v-if="actionType === 'script'"
 | 
					                  v-if="actionType === 'script'"
 | 
				
			||||||
                class="col-3"
 | 
					                  class="col-3"
 | 
				
			||||||
                dense
 | 
					                  dense
 | 
				
			||||||
                label="Script Arguments (press Enter after typing each argument)"
 | 
					                  label="Script Arguments (press Enter after typing each argument)"
 | 
				
			||||||
                filled
 | 
					                  filled
 | 
				
			||||||
                v-model="defaultArgs"
 | 
					                  v-model="defaultArgs"
 | 
				
			||||||
                use-input
 | 
					                  use-input
 | 
				
			||||||
                use-chips
 | 
					                  use-chips
 | 
				
			||||||
                multiple
 | 
					                  multiple
 | 
				
			||||||
                hide-dropdown-icon
 | 
					                  hide-dropdown-icon
 | 
				
			||||||
                input-debounce="0"
 | 
					                  input-debounce="0"
 | 
				
			||||||
                new-value-mode="add"
 | 
					                  new-value-mode="add"
 | 
				
			||||||
              />
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <q-select
 | 
					                <q-select
 | 
				
			||||||
                v-if="actionType === 'script'"
 | 
					                  v-if="actionType === 'script'"
 | 
				
			||||||
                class="col-3"
 | 
					                  class="col-3"
 | 
				
			||||||
                dense
 | 
					                  dense
 | 
				
			||||||
                :label="envVarsLabel"
 | 
					                  :label="envVarsLabel"
 | 
				
			||||||
                filled
 | 
					                  filled
 | 
				
			||||||
                v-model="defaultEnvVars"
 | 
					                  v-model="defaultEnvVars"
 | 
				
			||||||
                use-input
 | 
					                  use-input
 | 
				
			||||||
                use-chips
 | 
					                  use-chips
 | 
				
			||||||
                multiple
 | 
					                  multiple
 | 
				
			||||||
                hide-dropdown-icon
 | 
					                  hide-dropdown-icon
 | 
				
			||||||
                input-debounce="0"
 | 
					                  input-debounce="0"
 | 
				
			||||||
                new-value-mode="add"
 | 
					                  new-value-mode="add"
 | 
				
			||||||
              />
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <q-input
 | 
					                <q-input
 | 
				
			||||||
                v-if="actionType === 'script'"
 | 
					                  v-if="actionType === 'script'"
 | 
				
			||||||
                class="col-2"
 | 
					                  class="col-2"
 | 
				
			||||||
                filled
 | 
					                  filled
 | 
				
			||||||
                dense
 | 
					                  dense
 | 
				
			||||||
                v-model.number="defaultTimeout"
 | 
					                  v-model.number="defaultTimeout"
 | 
				
			||||||
                type="number"
 | 
					                  type="number"
 | 
				
			||||||
                label="Timeout (seconds)"
 | 
					                  label="Timeout (seconds)"
 | 
				
			||||||
              />
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <q-input
 | 
					                <q-input
 | 
				
			||||||
                v-if="actionType === 'cmd'"
 | 
					                  v-if="actionType === 'cmd'"
 | 
				
			||||||
                label="Command"
 | 
					                  label="Command"
 | 
				
			||||||
                v-model="command"
 | 
					                  v-model="command"
 | 
				
			||||||
 | 
					                  dense
 | 
				
			||||||
 | 
					                  filled
 | 
				
			||||||
 | 
					                  class="col-7"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <q-input
 | 
				
			||||||
 | 
					                  v-if="actionType === 'cmd'"
 | 
				
			||||||
 | 
					                  class="col-2"
 | 
				
			||||||
 | 
					                  filled
 | 
				
			||||||
 | 
					                  dense
 | 
				
			||||||
 | 
					                  v-model.number="defaultTimeout"
 | 
				
			||||||
 | 
					                  type="number"
 | 
				
			||||||
 | 
					                  label="Timeout (seconds)"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <q-option-group
 | 
				
			||||||
 | 
					                  v-if="actionType === 'cmd'"
 | 
				
			||||||
 | 
					                  class="col-2 q-pl-sm"
 | 
				
			||||||
 | 
					                  inline
 | 
				
			||||||
 | 
					                  v-model="shell"
 | 
				
			||||||
 | 
					                  :options="[
 | 
				
			||||||
 | 
					                    { label: 'Batch', value: 'cmd' },
 | 
				
			||||||
 | 
					                    { label: 'Powershell', value: 'powershell' },
 | 
				
			||||||
 | 
					                  ]"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <q-btn
 | 
				
			||||||
 | 
					                  class="col-1"
 | 
				
			||||||
 | 
					                  type="submit"
 | 
				
			||||||
 | 
					                  style="width: 50px"
 | 
				
			||||||
 | 
					                  flat
 | 
				
			||||||
 | 
					                  dense
 | 
				
			||||||
 | 
					                  icon="add"
 | 
				
			||||||
 | 
					                  color="primary"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </q-form>
 | 
				
			||||||
 | 
					            <div class="text-subtitle2 q-pa-sm">
 | 
				
			||||||
 | 
					              Actions:
 | 
				
			||||||
 | 
					              <q-checkbox
 | 
				
			||||||
 | 
					                class="float-right"
 | 
				
			||||||
 | 
					                label="Continue on Errors"
 | 
				
			||||||
 | 
					                v-model="state.continue_on_error"
 | 
				
			||||||
                dense
 | 
					                dense
 | 
				
			||||||
                filled
 | 
					              >
 | 
				
			||||||
                class="col-7"
 | 
					                <q-tooltip>Continue task if an action fails</q-tooltip>
 | 
				
			||||||
              />
 | 
					              </q-checkbox>
 | 
				
			||||||
              <q-input
 | 
					 | 
				
			||||||
                v-if="actionType === 'cmd'"
 | 
					 | 
				
			||||||
                class="col-2"
 | 
					 | 
				
			||||||
                filled
 | 
					 | 
				
			||||||
                dense
 | 
					 | 
				
			||||||
                v-model.number="defaultTimeout"
 | 
					 | 
				
			||||||
                type="number"
 | 
					 | 
				
			||||||
                label="Timeout (seconds)"
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
              <q-option-group
 | 
					 | 
				
			||||||
                v-if="actionType === 'cmd'"
 | 
					 | 
				
			||||||
                class="col-2 q-pl-sm"
 | 
					 | 
				
			||||||
                inline
 | 
					 | 
				
			||||||
                v-model="shell"
 | 
					 | 
				
			||||||
                :options="[
 | 
					 | 
				
			||||||
                  { label: 'Batch', value: 'cmd' },
 | 
					 | 
				
			||||||
                  { label: 'Powershell', value: 'powershell' },
 | 
					 | 
				
			||||||
                ]"
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
              <q-btn
 | 
					 | 
				
			||||||
                class="col-1"
 | 
					 | 
				
			||||||
                type="submit"
 | 
					 | 
				
			||||||
                style="width: 50px"
 | 
					 | 
				
			||||||
                flat
 | 
					 | 
				
			||||||
                dense
 | 
					 | 
				
			||||||
                icon="add"
 | 
					 | 
				
			||||||
                color="primary"
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </q-form>
 | 
					            <div class="q-pt-sm" style="height: 150px">
 | 
				
			||||||
          <div class="text-subtitle2 q-pa-sm">
 | 
					              <draggable
 | 
				
			||||||
            Actions:
 | 
					                class="q-list"
 | 
				
			||||||
            <q-checkbox
 | 
					                handle=".handle"
 | 
				
			||||||
              class="float-right"
 | 
					                ghost-class="ghost"
 | 
				
			||||||
              label="Continue on Errors"
 | 
					                v-model="state.actions"
 | 
				
			||||||
              v-model="state.continue_on_error"
 | 
					                item-key="index"
 | 
				
			||||||
              dense
 | 
					              >
 | 
				
			||||||
            >
 | 
					                <template v-slot:item="{ index, element }">
 | 
				
			||||||
              <q-tooltip>Continue task if an action fails</q-tooltip>
 | 
					                  <q-item>
 | 
				
			||||||
            </q-checkbox>
 | 
					                    <q-item-section avatar>
 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div class="scroll q-pt-sm" style="height: 40vh; max-height: 40vh">
 | 
					 | 
				
			||||||
            <draggable
 | 
					 | 
				
			||||||
              class="q-list"
 | 
					 | 
				
			||||||
              handle=".handle"
 | 
					 | 
				
			||||||
              ghost-class="ghost"
 | 
					 | 
				
			||||||
              v-model="state.actions"
 | 
					 | 
				
			||||||
              item-key="index"
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              <template v-slot:item="{ index, element }">
 | 
					 | 
				
			||||||
                <q-item>
 | 
					 | 
				
			||||||
                  <q-item-section avatar>
 | 
					 | 
				
			||||||
                    <q-icon
 | 
					 | 
				
			||||||
                      class="handle"
 | 
					 | 
				
			||||||
                      style="cursor: move"
 | 
					 | 
				
			||||||
                      name="drag_handle"
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                  </q-item-section>
 | 
					 | 
				
			||||||
                  <q-item-section v-if="element.type === 'script'">
 | 
					 | 
				
			||||||
                    <q-item-label>
 | 
					 | 
				
			||||||
                      <q-icon size="sm" name="description" color="primary" />
 | 
					 | 
				
			||||||
                        {{ element.name }}
 | 
					 | 
				
			||||||
                    </q-item-label>
 | 
					 | 
				
			||||||
                    <q-item-label caption>
 | 
					 | 
				
			||||||
                      Arguments: {{ element.script_args }}
 | 
					 | 
				
			||||||
                    </q-item-label>
 | 
					 | 
				
			||||||
                    <q-item-label caption>
 | 
					 | 
				
			||||||
                      Env Vars: {{ element.env_vars }}
 | 
					 | 
				
			||||||
                    </q-item-label>
 | 
					 | 
				
			||||||
                    <q-item-label caption>
 | 
					 | 
				
			||||||
                      Timeout: {{ element.timeout }}
 | 
					 | 
				
			||||||
                    </q-item-label>
 | 
					 | 
				
			||||||
                  </q-item-section>
 | 
					 | 
				
			||||||
                  <q-item-section v-else>
 | 
					 | 
				
			||||||
                    <q-item-label>
 | 
					 | 
				
			||||||
                      <q-icon size="sm" name="terminal" color="primary" />
 | 
					 | 
				
			||||||
                       
 | 
					 | 
				
			||||||
                      <q-icon
 | 
					                      <q-icon
 | 
				
			||||||
                        size="sm"
 | 
					                        class="handle"
 | 
				
			||||||
                        :name="
 | 
					                        style="cursor: move"
 | 
				
			||||||
                          element.shell === 'cmd'
 | 
					                        name="drag_handle"
 | 
				
			||||||
                            ? 'mdi-microsoft-windows'
 | 
					 | 
				
			||||||
                            : 'mdi-powershell'
 | 
					 | 
				
			||||||
                        "
 | 
					 | 
				
			||||||
                        color="primary"
 | 
					 | 
				
			||||||
                      />
 | 
					                      />
 | 
				
			||||||
                      {{ element.command }}
 | 
					                    </q-item-section>
 | 
				
			||||||
                    </q-item-label>
 | 
					                    <q-item-section v-if="element.type === 'script'">
 | 
				
			||||||
                    <q-item-label caption>
 | 
					                      <q-item-label>
 | 
				
			||||||
                      Timeout: {{ element.timeout }}
 | 
					                        <q-icon size="sm" name="description" color="primary" />
 | 
				
			||||||
                    </q-item-label>
 | 
					                          {{ element.name }}
 | 
				
			||||||
                  </q-item-section>
 | 
					                      </q-item-label>
 | 
				
			||||||
                  <q-item-section side>
 | 
					                      <q-item-label caption>
 | 
				
			||||||
                    <q-icon
 | 
					                        Arguments: {{ element.script_args }}
 | 
				
			||||||
                      class="cursor-pointer"
 | 
					                      </q-item-label>
 | 
				
			||||||
                      color="negative"
 | 
					                      <q-item-label caption>
 | 
				
			||||||
                      name="close"
 | 
					                        Env Vars: {{ element.env_vars }}
 | 
				
			||||||
                      @click="removeAction(index)"
 | 
					                      </q-item-label>
 | 
				
			||||||
                    />
 | 
					                      <q-item-label caption>
 | 
				
			||||||
                  </q-item-section>
 | 
					                        Timeout: {{ element.timeout }}
 | 
				
			||||||
                </q-item>
 | 
					                      </q-item-label>
 | 
				
			||||||
              </template>
 | 
					                    </q-item-section>
 | 
				
			||||||
            </draggable>
 | 
					                    <q-item-section v-else>
 | 
				
			||||||
 | 
					                      <q-item-label>
 | 
				
			||||||
 | 
					                        <q-icon size="sm" name="terminal" color="primary" />
 | 
				
			||||||
 | 
					                         
 | 
				
			||||||
 | 
					                        <q-icon
 | 
				
			||||||
 | 
					                          size="sm"
 | 
				
			||||||
 | 
					                          :name="
 | 
				
			||||||
 | 
					                            element.shell === 'cmd'
 | 
				
			||||||
 | 
					                              ? 'mdi-microsoft-windows'
 | 
				
			||||||
 | 
					                              : 'mdi-powershell'
 | 
				
			||||||
 | 
					                          "
 | 
				
			||||||
 | 
					                          color="primary"
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                        {{ element.command }}
 | 
				
			||||||
 | 
					                      </q-item-label>
 | 
				
			||||||
 | 
					                      <q-item-label caption>
 | 
				
			||||||
 | 
					                        Timeout: {{ element.timeout }}
 | 
				
			||||||
 | 
					                      </q-item-label>
 | 
				
			||||||
 | 
					                    </q-item-section>
 | 
				
			||||||
 | 
					                    <q-item-section side>
 | 
				
			||||||
 | 
					                      <q-icon
 | 
				
			||||||
 | 
					                        class="cursor-pointer"
 | 
				
			||||||
 | 
					                        color="negative"
 | 
				
			||||||
 | 
					                        name="close"
 | 
				
			||||||
 | 
					                        @click="removeAction(index)"
 | 
				
			||||||
 | 
					                      />
 | 
				
			||||||
 | 
					                    </q-item-section>
 | 
				
			||||||
 | 
					                  </q-item>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </draggable>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </q-step>
 | 
					        </q-step>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -283,7 +285,7 @@
 | 
				
			|||||||
              <q-card-section
 | 
					              <q-card-section
 | 
				
			||||||
                v-if="
 | 
					                v-if="
 | 
				
			||||||
                  ['runonce', 'daily', 'weekly', 'monthly'].includes(
 | 
					                  ['runonce', 'daily', 'weekly', 'monthly'].includes(
 | 
				
			||||||
                    state.task_type
 | 
					                    state.task_type,
 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
                "
 | 
					                "
 | 
				
			||||||
                class="row"
 | 
					                class="row"
 | 
				
			||||||
@@ -314,6 +316,22 @@
 | 
				
			|||||||
                />
 | 
					                />
 | 
				
			||||||
              </q-card-section>
 | 
					              </q-card-section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              <q-card-section
 | 
				
			||||||
 | 
					                v-if="
 | 
				
			||||||
 | 
					                  state.task_type === 'onboarding' ||
 | 
				
			||||||
 | 
					                  state.task_type === 'runonce'
 | 
				
			||||||
 | 
					                "
 | 
				
			||||||
 | 
					                class="row"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <span v-if="state.task_type === 'onboarding'"
 | 
				
			||||||
 | 
					                  >This task will run as soon as it's created on the
 | 
				
			||||||
 | 
					                  agent.</span
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                <span v-else-if="state.task_type === 'runonce'"
 | 
				
			||||||
 | 
					                  >Start Time must be in the future for run once tasks.</span
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					              </q-card-section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <!-- daily options -->
 | 
					              <!-- daily options -->
 | 
				
			||||||
              <q-card-section v-if="state.task_type === 'daily'" class="row">
 | 
					              <q-card-section v-if="state.task_type === 'daily'" class="row">
 | 
				
			||||||
                <!-- daily interval -->
 | 
					                <!-- daily interval -->
 | 
				
			||||||
@@ -579,7 +597,8 @@
 | 
				
			|||||||
              <q-card-section
 | 
					              <q-card-section
 | 
				
			||||||
                v-if="
 | 
					                v-if="
 | 
				
			||||||
                  state.task_type !== 'checkfailure' &&
 | 
					                  state.task_type !== 'checkfailure' &&
 | 
				
			||||||
                  state.task_type !== 'manual'
 | 
					                  state.task_type !== 'manual' &&
 | 
				
			||||||
 | 
					                  state.task_type !== 'onboarding'
 | 
				
			||||||
                "
 | 
					                "
 | 
				
			||||||
                class="row"
 | 
					                class="row"
 | 
				
			||||||
              >
 | 
					              >
 | 
				
			||||||
@@ -617,7 +636,7 @@
 | 
				
			|||||||
                    (val) =>
 | 
					                    (val) =>
 | 
				
			||||||
                      convertPeriodToSeconds(val) >=
 | 
					                      convertPeriodToSeconds(val) >=
 | 
				
			||||||
                        convertPeriodToSeconds(
 | 
					                        convertPeriodToSeconds(
 | 
				
			||||||
                          state.task_repetition_interval
 | 
					                          state.task_repetition_interval,
 | 
				
			||||||
                        ) ||
 | 
					                        ) ||
 | 
				
			||||||
                      'Repetition duration must be greater than repetition interval',
 | 
					                      'Repetition duration must be greater than repetition interval',
 | 
				
			||||||
                  ]"
 | 
					                  ]"
 | 
				
			||||||
@@ -712,7 +731,7 @@
 | 
				
			|||||||
          @click="
 | 
					          @click="
 | 
				
			||||||
            validateStep(
 | 
					            validateStep(
 | 
				
			||||||
              step === 1 ? $refs.taskGeneralForm : undefined,
 | 
					              step === 1 ? $refs.taskGeneralForm : undefined,
 | 
				
			||||||
              $refs.stepper
 | 
					              $refs.stepper,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
          "
 | 
					          "
 | 
				
			||||||
          color="primary"
 | 
					          color="primary"
 | 
				
			||||||
@@ -769,6 +788,7 @@ const taskTypeOptions = [
 | 
				
			|||||||
  { label: "Monthly", value: "monthly" },
 | 
					  { label: "Monthly", value: "monthly" },
 | 
				
			||||||
  { label: "Run Once", value: "runonce" },
 | 
					  { label: "Run Once", value: "runonce" },
 | 
				
			||||||
  { label: "On check failure", value: "checkfailure" },
 | 
					  { label: "On check failure", value: "checkfailure" },
 | 
				
			||||||
 | 
					  { label: "Onboarding", value: "onboarding" },
 | 
				
			||||||
  { label: "Manual", value: "manual" },
 | 
					  { label: "Manual", value: "manual" },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -933,7 +953,7 @@ export default {
 | 
				
			|||||||
        task.value.actions.push({
 | 
					        task.value.actions.push({
 | 
				
			||||||
          type: "script",
 | 
					          type: "script",
 | 
				
			||||||
          name: scriptOptions.value.find(
 | 
					          name: scriptOptions.value.find(
 | 
				
			||||||
            (option) => option.value === script.value
 | 
					            (option) => option.value === script.value,
 | 
				
			||||||
          ).label,
 | 
					          ).label,
 | 
				
			||||||
          script: script.value,
 | 
					          script: script.value,
 | 
				
			||||||
          timeout: defaultTimeout.value,
 | 
					          timeout: defaultTimeout.value,
 | 
				
			||||||
@@ -1019,13 +1039,13 @@ export default {
 | 
				
			|||||||
      // remove milliseconds and Z to work with native date input
 | 
					      // remove milliseconds and Z to work with native date input
 | 
				
			||||||
      task.value.run_time_date = formatDateInputField(
 | 
					      task.value.run_time_date = formatDateInputField(
 | 
				
			||||||
        task.value.run_time_date,
 | 
					        task.value.run_time_date,
 | 
				
			||||||
        true
 | 
					        true,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (task.value.expire_date)
 | 
					      if (task.value.expire_date)
 | 
				
			||||||
        task.value.expire_date = formatDateInputField(
 | 
					        task.value.expire_date = formatDateInputField(
 | 
				
			||||||
          task.value.expire_date,
 | 
					          task.value.expire_date,
 | 
				
			||||||
          true
 | 
					          true,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // set task type if monthlydow is being used
 | 
					      // set task type if monthlydow is being used
 | 
				
			||||||
@@ -1069,7 +1089,7 @@ export default {
 | 
				
			|||||||
        task.value.monthly_weeks_of_month = [];
 | 
					        task.value.monthly_weeks_of_month = [];
 | 
				
			||||||
        task.value.task_instance_policy = 0;
 | 
					        task.value.task_instance_policy = 0;
 | 
				
			||||||
        task.value.expire_date = null;
 | 
					        task.value.expire_date = null;
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // check the collector box when editing task and custom field is set
 | 
					    // check the collector box when editing task and custom field is set
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,13 +25,21 @@
 | 
				
			|||||||
        :key="mapOptions ? scope.opt.value : scope.opt"
 | 
					        :key="mapOptions ? scope.opt.value : scope.opt"
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        <q-item-section>
 | 
					        <q-item-section>
 | 
				
			||||||
          <q-item-label
 | 
					          <q-item-label v-html="mapOptions ? scope.opt.label : scope.opt" />
 | 
				
			||||||
            v-html="mapOptions ? scope.opt.label : scope.opt"
 | 
					        </q-item-section>
 | 
				
			||||||
          ></q-item-label>
 | 
					        <q-item-section
 | 
				
			||||||
 | 
					          v-if="
 | 
				
			||||||
 | 
					            (filtered && mapOptions && scope.opt.cat) || scope.opt.img_right
 | 
				
			||||||
 | 
					          "
 | 
				
			||||||
 | 
					          side
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          {{ scope.opt.cat || "" }}
 | 
				
			||||||
 | 
					          <img
 | 
				
			||||||
 | 
					            v-if="scope.opt.img_right"
 | 
				
			||||||
 | 
					            :src="scope.opt.img_right"
 | 
				
			||||||
 | 
					            style="height: 20px; max-width: 20px"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
        </q-item-section>
 | 
					        </q-item-section>
 | 
				
			||||||
        <q-item-section v-if="filtered && mapOptions && scope.opt.cat" side>{{
 | 
					 | 
				
			||||||
          scope.opt.cat
 | 
					 | 
				
			||||||
        }}</q-item-section>
 | 
					 | 
				
			||||||
      </q-item>
 | 
					      </q-item>
 | 
				
			||||||
      <q-item-label
 | 
					      <q-item-label
 | 
				
			||||||
        v-if="scope.opt.category"
 | 
					        v-if="scope.opt.category"
 | 
				
			||||||
@@ -80,7 +88,7 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          if (!props.mapOptions)
 | 
					          if (!props.mapOptions)
 | 
				
			||||||
            filteredOptions.value = props.options.filter(
 | 
					            filteredOptions.value = props.options.filter(
 | 
				
			||||||
              (v) => v.toLowerCase().indexOf(needle) > -1
 | 
					              (v) => v.toLowerCase().indexOf(needle) > -1,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
          else
 | 
					          else
 | 
				
			||||||
            filteredOptions.value = props.options.filter((v) => {
 | 
					            filteredOptions.value = props.options.filter((v) => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
 | 
				
			|||||||
  // specify parameters to filter out community scripts
 | 
					  // specify parameters to filter out community scripts
 | 
				
			||||||
  async function getScriptOptions(showCommunityScripts = false) {
 | 
					  async function getScriptOptions(showCommunityScripts = false) {
 | 
				
			||||||
    scriptOptions.value = Object.freeze(
 | 
					    scriptOptions.value = Object.freeze(
 | 
				
			||||||
      formatScriptOptions(await fetchScripts({ showCommunityScripts }))
 | 
					      formatScriptOptions(await fetchScripts({ showCommunityScripts })),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,7 +26,7 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
 | 
				
			|||||||
  watch([script, scriptOptions], () => {
 | 
					  watch([script, scriptOptions], () => {
 | 
				
			||||||
    if (script.value && scriptOptions.value.length > 0) {
 | 
					    if (script.value && scriptOptions.value.length > 0) {
 | 
				
			||||||
      const tmpScript = scriptOptions.value.find(
 | 
					      const tmpScript = scriptOptions.value.find(
 | 
				
			||||||
        (i) => i.value === script.value
 | 
					        (i) => i.value === script.value,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      defaultTimeout.value = tmpScript.timeout;
 | 
					      defaultTimeout.value = tmpScript.timeout;
 | 
				
			||||||
      defaultArgs.value = tmpScript.args;
 | 
					      defaultArgs.value = tmpScript.args;
 | 
				
			||||||
@@ -65,4 +65,6 @@ export const shellOptions = [
 | 
				
			|||||||
  { label: "Batch", value: "cmd" },
 | 
					  { label: "Batch", value: "cmd" },
 | 
				
			||||||
  { label: "Python", value: "python" },
 | 
					  { label: "Python", value: "python" },
 | 
				
			||||||
  { label: "Shell", value: "shell" },
 | 
					  { label: "Shell", value: "shell" },
 | 
				
			||||||
 | 
					  { label: "Nushell", value: "nushell" },
 | 
				
			||||||
 | 
					  { label: "Deno", value: "deno" },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ For details, see: https://license.tacticalrmm.com/ee
 | 
				
			|||||||
        :rows="reportTemplates"
 | 
					        :rows="reportTemplates"
 | 
				
			||||||
        :columns="columns"
 | 
					        :columns="columns"
 | 
				
			||||||
        :loading="isLoading"
 | 
					        :loading="isLoading"
 | 
				
			||||||
        :pagination="{ rowsPerPage: 0, sortBy: 'name', descending: true }"
 | 
					        :pagination="{ rowsPerPage: 0, sortBy: 'name', descending: false }"
 | 
				
			||||||
        :filter="search"
 | 
					        :filter="search"
 | 
				
			||||||
        row-key="id"
 | 
					        row-key="id"
 | 
				
			||||||
        binary-state-sort
 | 
					        binary-state-sort
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,8 +25,8 @@
 | 
				
			|||||||
          If you have downgraded or cancelled your sponsorship, please delete
 | 
					          If you have downgraded or cancelled your sponsorship, please delete
 | 
				
			||||||
          your token from the Code Signing modal and refresh to get rid of this
 | 
					          your token from the Code Signing modal and refresh to get rid of this
 | 
				
			||||||
          banner.<br /><br />
 | 
					          banner.<br /><br />
 | 
				
			||||||
          For any issues or to renew your sponsorship please email
 | 
					          For any issues or to renew your sponsorship please open a ticket at
 | 
				
			||||||
          support@amidaware.com<br /><br
 | 
					          support.amidaware.com<br /><br
 | 
				
			||||||
        /></span>
 | 
					        /></span>
 | 
				
			||||||
        <q-btn
 | 
					        <q-btn
 | 
				
			||||||
          color="dark"
 | 
					          color="dark"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import type { AgentPlatformType } from "@/types/agents";
 | 
					import type { AgentPlatformType } from "@/types/agents";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type ScriptShellType = "powershell" | "cmd" | "shell" | "python";
 | 
					export type ScriptShellType = "powershell" | "cmd" | "shell" | "python" | "nushell" | "deno";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Script {
 | 
					export interface Script {
 | 
				
			||||||
  id?: number;
 | 
					  id?: number;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import { date } from "quasar";
 | 
					import { date } from "quasar";
 | 
				
			||||||
import { validateTimePeriod } from "@/utils/validation";
 | 
					import { validateTimePeriod } from "@/utils/validation";
 | 
				
			||||||
 | 
					import trmmLogo from "@/assets/trmm_256.png";
 | 
				
			||||||
// dropdown options formatting
 | 
					// dropdown options formatting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function removeExtraOptionCategories(array) {
 | 
					export function removeExtraOptionCategories(array) {
 | 
				
			||||||
@@ -24,7 +25,7 @@ function _formatOptions(
 | 
				
			|||||||
    flat = false,
 | 
					    flat = false,
 | 
				
			||||||
    allowDuplicates = true,
 | 
					    allowDuplicates = true,
 | 
				
			||||||
    appendToOptionObject = {},
 | 
					    appendToOptionObject = {},
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  if (!flat)
 | 
					  if (!flat)
 | 
				
			||||||
    // returns array of options in object format [{label: label, value: 1}]
 | 
					    // returns array of options in object format [{label: label, value: 1}]
 | 
				
			||||||
@@ -64,6 +65,7 @@ export function formatScriptOptions(data) {
 | 
				
			|||||||
    data.forEach((script) => {
 | 
					    data.forEach((script) => {
 | 
				
			||||||
      if (script.category === cat) {
 | 
					      if (script.category === cat) {
 | 
				
			||||||
        tmp.push({
 | 
					        tmp.push({
 | 
				
			||||||
 | 
					          img_right: script.script_type === "builtin" ? trmmLogo : undefined,
 | 
				
			||||||
          label: script.name,
 | 
					          label: script.name,
 | 
				
			||||||
          value: script.id,
 | 
					          value: script.id,
 | 
				
			||||||
          timeout: script.default_timeout,
 | 
					          timeout: script.default_timeout,
 | 
				
			||||||
@@ -100,7 +102,7 @@ export function formatScriptOptions(data) {
 | 
				
			|||||||
export function formatAgentOptions(
 | 
					export function formatAgentOptions(
 | 
				
			||||||
  data,
 | 
					  data,
 | 
				
			||||||
  flat = false,
 | 
					  flat = false,
 | 
				
			||||||
  value_field = "agent_id"
 | 
					  value_field = "agent_id",
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  if (flat) {
 | 
					  if (flat) {
 | 
				
			||||||
    // returns just agent hostnames in array
 | 
					    // returns just agent hostnames in array
 | 
				
			||||||
@@ -185,7 +187,7 @@ export function formatSiteOptions(data, flat = false) {
 | 
				
			|||||||
        label: "name",
 | 
					        label: "name",
 | 
				
			||||||
        flat: flat,
 | 
					        flat: flat,
 | 
				
			||||||
        appendToOptionObject: { cat: client.name },
 | 
					        appendToOptionObject: { cat: client.name },
 | 
				
			||||||
      })
 | 
					      }),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -361,7 +363,7 @@ export function convertToBitArray(number) {
 | 
				
			|||||||
        bitArray.push(1);
 | 
					        bitArray.push(1);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        bitArray.push(
 | 
					        bitArray.push(
 | 
				
			||||||
          parseInt(binary.slice(i), 2) - parseInt(binary.slice(i + 1), 2)
 | 
					          parseInt(binary.slice(i), 2) - parseInt(binary.slice(i + 1), 2),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,6 +53,26 @@
 | 
				
			|||||||
                :options="allTimezones"
 | 
					                :options="allTimezones"
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
            </q-card-section>
 | 
					            </q-card-section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <q-card-section>
 | 
				
			||||||
 | 
					              <div>
 | 
				
			||||||
 | 
					                Company name:
 | 
				
			||||||
 | 
					                <q-icon
 | 
				
			||||||
 | 
					                  name="ion-information-circle-outline"
 | 
				
			||||||
 | 
					                  size="sm"
 | 
				
			||||||
 | 
					                  class="q-ml-sm cursor-pointer"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <q-tooltip class="text-caption">
 | 
				
			||||||
 | 
					                    Adding your company name here will append it to the user's
 | 
				
			||||||
 | 
					                    full name that appears when doing a remote control session,
 | 
				
			||||||
 | 
					                    for example: 'John Doe - Amidaware Inc.'
 | 
				
			||||||
 | 
					                  </q-tooltip>
 | 
				
			||||||
 | 
					                </q-icon>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              <q-input dense outlined v-model="companyname"> </q-input>
 | 
				
			||||||
 | 
					            </q-card-section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <q-card-actions align="center">
 | 
					            <q-card-actions align="center">
 | 
				
			||||||
              <q-btn
 | 
					              <q-btn
 | 
				
			||||||
                label="Finish"
 | 
					                label="Finish"
 | 
				
			||||||
@@ -86,6 +106,7 @@ export default {
 | 
				
			|||||||
      allTimezones: [],
 | 
					      allTimezones: [],
 | 
				
			||||||
      timezone: null,
 | 
					      timezone: null,
 | 
				
			||||||
      arch: "64",
 | 
					      arch: "64",
 | 
				
			||||||
 | 
					      companyname: "",
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
@@ -95,6 +116,7 @@ export default {
 | 
				
			|||||||
        client: this.client,
 | 
					        client: this.client,
 | 
				
			||||||
        site: this.site,
 | 
					        site: this.site,
 | 
				
			||||||
        timezone: this.timezone,
 | 
					        timezone: this.timezone,
 | 
				
			||||||
 | 
					        companyname: this.companyname,
 | 
				
			||||||
        initialsetup: true,
 | 
					        initialsetup: true,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,6 +63,7 @@
 | 
				
			|||||||
                  autofocus
 | 
					                  autofocus
 | 
				
			||||||
                  outlined
 | 
					                  outlined
 | 
				
			||||||
                  v-model="credentials.twofactor"
 | 
					                  v-model="credentials.twofactor"
 | 
				
			||||||
 | 
					                  autocomplete="one-time-code"
 | 
				
			||||||
                  :rules="[
 | 
					                  :rules="[
 | 
				
			||||||
                    (val) =>
 | 
					                    (val) =>
 | 
				
			||||||
                      (val && val.length > 0) || 'This field is required',
 | 
					                      (val && val.length > 0) || 'This field is required',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,7 +90,7 @@ export default {
 | 
				
			|||||||
        control.value = data.control;
 | 
					        control.value = data.control;
 | 
				
			||||||
        status.value = data.status;
 | 
					        status.value = data.status;
 | 
				
			||||||
        useMeta({
 | 
					        useMeta({
 | 
				
			||||||
          title: `${data.hostname} - ${data.client} - ${data.site} | Remote Background`,
 | 
					          title: `${data.hostname} - ${data.client} - ${data.site} | Take Control`,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } catch (e) {
 | 
					      } catch (e) {
 | 
				
			||||||
        console.error(e);
 | 
					        console.error(e);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user