You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

plugin.py 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. import io
  2. from flask import request, send_file
  3. from flask_login import current_user
  4. from flask_restful import Resource, reqparse
  5. from werkzeug.exceptions import Forbidden
  6. from configs import dify_config
  7. from controllers.console import api
  8. from controllers.console.workspace import plugin_permission_required
  9. from controllers.console.wraps import account_initialization_required, setup_required
  10. from core.model_runtime.utils.encoders import jsonable_encoder
  11. from core.plugin.impl.exc import PluginDaemonClientSideError
  12. from libs.login import login_required
  13. from models.account import TenantPluginAutoUpgradeStrategy, TenantPluginPermission
  14. from services.plugin.plugin_auto_upgrade_service import PluginAutoUpgradeService
  15. from services.plugin.plugin_parameter_service import PluginParameterService
  16. from services.plugin.plugin_permission_service import PluginPermissionService
  17. from services.plugin.plugin_service import PluginService
  18. class PluginDebuggingKeyApi(Resource):
  19. @setup_required
  20. @login_required
  21. @account_initialization_required
  22. @plugin_permission_required(debug_required=True)
  23. def get(self):
  24. tenant_id = current_user.current_tenant_id
  25. try:
  26. return {
  27. "key": PluginService.get_debugging_key(tenant_id),
  28. "host": dify_config.PLUGIN_REMOTE_INSTALL_HOST,
  29. "port": dify_config.PLUGIN_REMOTE_INSTALL_PORT,
  30. }
  31. except PluginDaemonClientSideError as e:
  32. raise ValueError(e)
  33. class PluginListApi(Resource):
  34. @setup_required
  35. @login_required
  36. @account_initialization_required
  37. def get(self):
  38. tenant_id = current_user.current_tenant_id
  39. parser = reqparse.RequestParser()
  40. parser.add_argument("page", type=int, required=False, location="args", default=1)
  41. parser.add_argument("page_size", type=int, required=False, location="args", default=256)
  42. args = parser.parse_args()
  43. try:
  44. plugins_with_total = PluginService.list_with_total(tenant_id, args["page"], args["page_size"])
  45. except PluginDaemonClientSideError as e:
  46. raise ValueError(e)
  47. return jsonable_encoder({"plugins": plugins_with_total.list, "total": plugins_with_total.total})
  48. class PluginListLatestVersionsApi(Resource):
  49. @setup_required
  50. @login_required
  51. @account_initialization_required
  52. def post(self):
  53. req = reqparse.RequestParser()
  54. req.add_argument("plugin_ids", type=list, required=True, location="json")
  55. args = req.parse_args()
  56. try:
  57. versions = PluginService.list_latest_versions(args["plugin_ids"])
  58. except PluginDaemonClientSideError as e:
  59. raise ValueError(e)
  60. return jsonable_encoder({"versions": versions})
  61. class PluginListInstallationsFromIdsApi(Resource):
  62. @setup_required
  63. @login_required
  64. @account_initialization_required
  65. def post(self):
  66. tenant_id = current_user.current_tenant_id
  67. parser = reqparse.RequestParser()
  68. parser.add_argument("plugin_ids", type=list, required=True, location="json")
  69. args = parser.parse_args()
  70. try:
  71. plugins = PluginService.list_installations_from_ids(tenant_id, args["plugin_ids"])
  72. except PluginDaemonClientSideError as e:
  73. raise ValueError(e)
  74. return jsonable_encoder({"plugins": plugins})
  75. class PluginIconApi(Resource):
  76. @setup_required
  77. def get(self):
  78. req = reqparse.RequestParser()
  79. req.add_argument("tenant_id", type=str, required=True, location="args")
  80. req.add_argument("filename", type=str, required=True, location="args")
  81. args = req.parse_args()
  82. try:
  83. icon_bytes, mimetype = PluginService.get_asset(args["tenant_id"], args["filename"])
  84. except PluginDaemonClientSideError as e:
  85. raise ValueError(e)
  86. icon_cache_max_age = dify_config.TOOL_ICON_CACHE_MAX_AGE
  87. return send_file(io.BytesIO(icon_bytes), mimetype=mimetype, max_age=icon_cache_max_age)
  88. class PluginUploadFromPkgApi(Resource):
  89. @setup_required
  90. @login_required
  91. @account_initialization_required
  92. @plugin_permission_required(install_required=True)
  93. def post(self):
  94. tenant_id = current_user.current_tenant_id
  95. file = request.files["pkg"]
  96. # check file size
  97. if file.content_length > dify_config.PLUGIN_MAX_PACKAGE_SIZE:
  98. raise ValueError("File size exceeds the maximum allowed size")
  99. content = file.read()
  100. try:
  101. response = PluginService.upload_pkg(tenant_id, content)
  102. except PluginDaemonClientSideError as e:
  103. raise ValueError(e)
  104. return jsonable_encoder(response)
  105. class PluginUploadFromGithubApi(Resource):
  106. @setup_required
  107. @login_required
  108. @account_initialization_required
  109. @plugin_permission_required(install_required=True)
  110. def post(self):
  111. tenant_id = current_user.current_tenant_id
  112. parser = reqparse.RequestParser()
  113. parser.add_argument("repo", type=str, required=True, location="json")
  114. parser.add_argument("version", type=str, required=True, location="json")
  115. parser.add_argument("package", type=str, required=True, location="json")
  116. args = parser.parse_args()
  117. try:
  118. response = PluginService.upload_pkg_from_github(tenant_id, args["repo"], args["version"], args["package"])
  119. except PluginDaemonClientSideError as e:
  120. raise ValueError(e)
  121. return jsonable_encoder(response)
  122. class PluginUploadFromBundleApi(Resource):
  123. @setup_required
  124. @login_required
  125. @account_initialization_required
  126. @plugin_permission_required(install_required=True)
  127. def post(self):
  128. tenant_id = current_user.current_tenant_id
  129. file = request.files["bundle"]
  130. # check file size
  131. if file.content_length > dify_config.PLUGIN_MAX_BUNDLE_SIZE:
  132. raise ValueError("File size exceeds the maximum allowed size")
  133. content = file.read()
  134. try:
  135. response = PluginService.upload_bundle(tenant_id, content)
  136. except PluginDaemonClientSideError as e:
  137. raise ValueError(e)
  138. return jsonable_encoder(response)
  139. class PluginInstallFromPkgApi(Resource):
  140. @setup_required
  141. @login_required
  142. @account_initialization_required
  143. @plugin_permission_required(install_required=True)
  144. def post(self):
  145. tenant_id = current_user.current_tenant_id
  146. parser = reqparse.RequestParser()
  147. parser.add_argument("plugin_unique_identifiers", type=list, required=True, location="json")
  148. args = parser.parse_args()
  149. # check if all plugin_unique_identifiers are valid string
  150. for plugin_unique_identifier in args["plugin_unique_identifiers"]:
  151. if not isinstance(plugin_unique_identifier, str):
  152. raise ValueError("Invalid plugin unique identifier")
  153. try:
  154. response = PluginService.install_from_local_pkg(tenant_id, args["plugin_unique_identifiers"])
  155. except PluginDaemonClientSideError as e:
  156. raise ValueError(e)
  157. return jsonable_encoder(response)
  158. class PluginInstallFromGithubApi(Resource):
  159. @setup_required
  160. @login_required
  161. @account_initialization_required
  162. @plugin_permission_required(install_required=True)
  163. def post(self):
  164. tenant_id = current_user.current_tenant_id
  165. parser = reqparse.RequestParser()
  166. parser.add_argument("repo", type=str, required=True, location="json")
  167. parser.add_argument("version", type=str, required=True, location="json")
  168. parser.add_argument("package", type=str, required=True, location="json")
  169. parser.add_argument("plugin_unique_identifier", type=str, required=True, location="json")
  170. args = parser.parse_args()
  171. try:
  172. response = PluginService.install_from_github(
  173. tenant_id,
  174. args["plugin_unique_identifier"],
  175. args["repo"],
  176. args["version"],
  177. args["package"],
  178. )
  179. except PluginDaemonClientSideError as e:
  180. raise ValueError(e)
  181. return jsonable_encoder(response)
  182. class PluginInstallFromMarketplaceApi(Resource):
  183. @setup_required
  184. @login_required
  185. @account_initialization_required
  186. @plugin_permission_required(install_required=True)
  187. def post(self):
  188. tenant_id = current_user.current_tenant_id
  189. parser = reqparse.RequestParser()
  190. parser.add_argument("plugin_unique_identifiers", type=list, required=True, location="json")
  191. args = parser.parse_args()
  192. # check if all plugin_unique_identifiers are valid string
  193. for plugin_unique_identifier in args["plugin_unique_identifiers"]:
  194. if not isinstance(plugin_unique_identifier, str):
  195. raise ValueError("Invalid plugin unique identifier")
  196. try:
  197. response = PluginService.install_from_marketplace_pkg(tenant_id, args["plugin_unique_identifiers"])
  198. except PluginDaemonClientSideError as e:
  199. raise ValueError(e)
  200. return jsonable_encoder(response)
  201. class PluginFetchMarketplacePkgApi(Resource):
  202. @setup_required
  203. @login_required
  204. @account_initialization_required
  205. @plugin_permission_required(install_required=True)
  206. def get(self):
  207. tenant_id = current_user.current_tenant_id
  208. parser = reqparse.RequestParser()
  209. parser.add_argument("plugin_unique_identifier", type=str, required=True, location="args")
  210. args = parser.parse_args()
  211. try:
  212. return jsonable_encoder(
  213. {
  214. "manifest": PluginService.fetch_marketplace_pkg(
  215. tenant_id,
  216. args["plugin_unique_identifier"],
  217. )
  218. }
  219. )
  220. except PluginDaemonClientSideError as e:
  221. raise ValueError(e)
  222. class PluginFetchManifestApi(Resource):
  223. @setup_required
  224. @login_required
  225. @account_initialization_required
  226. @plugin_permission_required(install_required=True)
  227. def get(self):
  228. tenant_id = current_user.current_tenant_id
  229. parser = reqparse.RequestParser()
  230. parser.add_argument("plugin_unique_identifier", type=str, required=True, location="args")
  231. args = parser.parse_args()
  232. try:
  233. return jsonable_encoder(
  234. {
  235. "manifest": PluginService.fetch_plugin_manifest(
  236. tenant_id, args["plugin_unique_identifier"]
  237. ).model_dump()
  238. }
  239. )
  240. except PluginDaemonClientSideError as e:
  241. raise ValueError(e)
  242. class PluginFetchInstallTasksApi(Resource):
  243. @setup_required
  244. @login_required
  245. @account_initialization_required
  246. @plugin_permission_required(install_required=True)
  247. def get(self):
  248. tenant_id = current_user.current_tenant_id
  249. parser = reqparse.RequestParser()
  250. parser.add_argument("page", type=int, required=True, location="args")
  251. parser.add_argument("page_size", type=int, required=True, location="args")
  252. args = parser.parse_args()
  253. try:
  254. return jsonable_encoder(
  255. {"tasks": PluginService.fetch_install_tasks(tenant_id, args["page"], args["page_size"])}
  256. )
  257. except PluginDaemonClientSideError as e:
  258. raise ValueError(e)
  259. class PluginFetchInstallTaskApi(Resource):
  260. @setup_required
  261. @login_required
  262. @account_initialization_required
  263. @plugin_permission_required(install_required=True)
  264. def get(self, task_id: str):
  265. tenant_id = current_user.current_tenant_id
  266. try:
  267. return jsonable_encoder({"task": PluginService.fetch_install_task(tenant_id, task_id)})
  268. except PluginDaemonClientSideError as e:
  269. raise ValueError(e)
  270. class PluginDeleteInstallTaskApi(Resource):
  271. @setup_required
  272. @login_required
  273. @account_initialization_required
  274. @plugin_permission_required(install_required=True)
  275. def post(self, task_id: str):
  276. tenant_id = current_user.current_tenant_id
  277. try:
  278. return {"success": PluginService.delete_install_task(tenant_id, task_id)}
  279. except PluginDaemonClientSideError as e:
  280. raise ValueError(e)
  281. class PluginDeleteAllInstallTaskItemsApi(Resource):
  282. @setup_required
  283. @login_required
  284. @account_initialization_required
  285. @plugin_permission_required(install_required=True)
  286. def post(self):
  287. tenant_id = current_user.current_tenant_id
  288. try:
  289. return {"success": PluginService.delete_all_install_task_items(tenant_id)}
  290. except PluginDaemonClientSideError as e:
  291. raise ValueError(e)
  292. class PluginDeleteInstallTaskItemApi(Resource):
  293. @setup_required
  294. @login_required
  295. @account_initialization_required
  296. @plugin_permission_required(install_required=True)
  297. def post(self, task_id: str, identifier: str):
  298. tenant_id = current_user.current_tenant_id
  299. try:
  300. return {"success": PluginService.delete_install_task_item(tenant_id, task_id, identifier)}
  301. except PluginDaemonClientSideError as e:
  302. raise ValueError(e)
  303. class PluginUpgradeFromMarketplaceApi(Resource):
  304. @setup_required
  305. @login_required
  306. @account_initialization_required
  307. @plugin_permission_required(install_required=True)
  308. def post(self):
  309. tenant_id = current_user.current_tenant_id
  310. parser = reqparse.RequestParser()
  311. parser.add_argument("original_plugin_unique_identifier", type=str, required=True, location="json")
  312. parser.add_argument("new_plugin_unique_identifier", type=str, required=True, location="json")
  313. args = parser.parse_args()
  314. try:
  315. return jsonable_encoder(
  316. PluginService.upgrade_plugin_with_marketplace(
  317. tenant_id, args["original_plugin_unique_identifier"], args["new_plugin_unique_identifier"]
  318. )
  319. )
  320. except PluginDaemonClientSideError as e:
  321. raise ValueError(e)
  322. class PluginUpgradeFromGithubApi(Resource):
  323. @setup_required
  324. @login_required
  325. @account_initialization_required
  326. @plugin_permission_required(install_required=True)
  327. def post(self):
  328. tenant_id = current_user.current_tenant_id
  329. parser = reqparse.RequestParser()
  330. parser.add_argument("original_plugin_unique_identifier", type=str, required=True, location="json")
  331. parser.add_argument("new_plugin_unique_identifier", type=str, required=True, location="json")
  332. parser.add_argument("repo", type=str, required=True, location="json")
  333. parser.add_argument("version", type=str, required=True, location="json")
  334. parser.add_argument("package", type=str, required=True, location="json")
  335. args = parser.parse_args()
  336. try:
  337. return jsonable_encoder(
  338. PluginService.upgrade_plugin_with_github(
  339. tenant_id,
  340. args["original_plugin_unique_identifier"],
  341. args["new_plugin_unique_identifier"],
  342. args["repo"],
  343. args["version"],
  344. args["package"],
  345. )
  346. )
  347. except PluginDaemonClientSideError as e:
  348. raise ValueError(e)
  349. class PluginUninstallApi(Resource):
  350. @setup_required
  351. @login_required
  352. @account_initialization_required
  353. @plugin_permission_required(install_required=True)
  354. def post(self):
  355. req = reqparse.RequestParser()
  356. req.add_argument("plugin_installation_id", type=str, required=True, location="json")
  357. args = req.parse_args()
  358. tenant_id = current_user.current_tenant_id
  359. try:
  360. return {"success": PluginService.uninstall(tenant_id, args["plugin_installation_id"])}
  361. except PluginDaemonClientSideError as e:
  362. raise ValueError(e)
  363. class PluginChangePermissionApi(Resource):
  364. @setup_required
  365. @login_required
  366. @account_initialization_required
  367. def post(self):
  368. user = current_user
  369. if not user.is_admin_or_owner:
  370. raise Forbidden()
  371. req = reqparse.RequestParser()
  372. req.add_argument("install_permission", type=str, required=True, location="json")
  373. req.add_argument("debug_permission", type=str, required=True, location="json")
  374. args = req.parse_args()
  375. install_permission = TenantPluginPermission.InstallPermission(args["install_permission"])
  376. debug_permission = TenantPluginPermission.DebugPermission(args["debug_permission"])
  377. tenant_id = user.current_tenant_id
  378. return {"success": PluginPermissionService.change_permission(tenant_id, install_permission, debug_permission)}
  379. class PluginFetchPermissionApi(Resource):
  380. @setup_required
  381. @login_required
  382. @account_initialization_required
  383. def get(self):
  384. tenant_id = current_user.current_tenant_id
  385. permission = PluginPermissionService.get_permission(tenant_id)
  386. if not permission:
  387. return jsonable_encoder(
  388. {
  389. "install_permission": TenantPluginPermission.InstallPermission.EVERYONE,
  390. "debug_permission": TenantPluginPermission.DebugPermission.EVERYONE,
  391. }
  392. )
  393. return jsonable_encoder(
  394. {
  395. "install_permission": permission.install_permission,
  396. "debug_permission": permission.debug_permission,
  397. }
  398. )
  399. class PluginFetchDynamicSelectOptionsApi(Resource):
  400. @setup_required
  401. @login_required
  402. @account_initialization_required
  403. def get(self):
  404. # check if the user is admin or owner
  405. if not current_user.is_admin_or_owner:
  406. raise Forbidden()
  407. tenant_id = current_user.current_tenant_id
  408. user_id = current_user.id
  409. parser = reqparse.RequestParser()
  410. parser.add_argument("plugin_id", type=str, required=True, location="args")
  411. parser.add_argument("provider", type=str, required=True, location="args")
  412. parser.add_argument("action", type=str, required=True, location="args")
  413. parser.add_argument("parameter", type=str, required=True, location="args")
  414. parser.add_argument("provider_type", type=str, required=True, location="args")
  415. args = parser.parse_args()
  416. try:
  417. options = PluginParameterService.get_dynamic_select_options(
  418. tenant_id,
  419. user_id,
  420. args["plugin_id"],
  421. args["provider"],
  422. args["action"],
  423. args["parameter"],
  424. args["provider_type"],
  425. )
  426. except PluginDaemonClientSideError as e:
  427. raise ValueError(e)
  428. return jsonable_encoder({"options": options})
  429. class PluginChangePreferencesApi(Resource):
  430. @setup_required
  431. @login_required
  432. @account_initialization_required
  433. def post(self):
  434. user = current_user
  435. if not user.is_admin_or_owner:
  436. raise Forbidden()
  437. req = reqparse.RequestParser()
  438. req.add_argument("permission", type=dict, required=True, location="json")
  439. req.add_argument("auto_upgrade", type=dict, required=True, location="json")
  440. args = req.parse_args()
  441. tenant_id = user.current_tenant_id
  442. permission = args["permission"]
  443. install_permission = TenantPluginPermission.InstallPermission(permission.get("install_permission", "everyone"))
  444. debug_permission = TenantPluginPermission.DebugPermission(permission.get("debug_permission", "everyone"))
  445. auto_upgrade = args["auto_upgrade"]
  446. strategy_setting = TenantPluginAutoUpgradeStrategy.StrategySetting(
  447. auto_upgrade.get("strategy_setting", "fix_only")
  448. )
  449. upgrade_time_of_day = auto_upgrade.get("upgrade_time_of_day", 0)
  450. upgrade_mode = TenantPluginAutoUpgradeStrategy.UpgradeMode(auto_upgrade.get("upgrade_mode", "exclude"))
  451. exclude_plugins = auto_upgrade.get("exclude_plugins", [])
  452. include_plugins = auto_upgrade.get("include_plugins", [])
  453. # set permission
  454. set_permission_result = PluginPermissionService.change_permission(
  455. tenant_id,
  456. install_permission,
  457. debug_permission,
  458. )
  459. if not set_permission_result:
  460. return jsonable_encoder({"success": False, "message": "Failed to set permission"})
  461. # set auto upgrade strategy
  462. set_auto_upgrade_strategy_result = PluginAutoUpgradeService.change_strategy(
  463. tenant_id,
  464. strategy_setting,
  465. upgrade_time_of_day,
  466. upgrade_mode,
  467. exclude_plugins,
  468. include_plugins,
  469. )
  470. if not set_auto_upgrade_strategy_result:
  471. return jsonable_encoder({"success": False, "message": "Failed to set auto upgrade strategy"})
  472. return jsonable_encoder({"success": True})
  473. class PluginFetchPreferencesApi(Resource):
  474. @setup_required
  475. @login_required
  476. @account_initialization_required
  477. def get(self):
  478. tenant_id = current_user.current_tenant_id
  479. permission = PluginPermissionService.get_permission(tenant_id)
  480. permission_dict = {
  481. "install_permission": TenantPluginPermission.InstallPermission.EVERYONE,
  482. "debug_permission": TenantPluginPermission.DebugPermission.EVERYONE,
  483. }
  484. if permission:
  485. permission_dict["install_permission"] = permission.install_permission
  486. permission_dict["debug_permission"] = permission.debug_permission
  487. auto_upgrade = PluginAutoUpgradeService.get_strategy(tenant_id)
  488. auto_upgrade_dict = {
  489. "strategy_setting": TenantPluginAutoUpgradeStrategy.StrategySetting.DISABLED,
  490. "upgrade_time_of_day": 0,
  491. "upgrade_mode": TenantPluginAutoUpgradeStrategy.UpgradeMode.EXCLUDE,
  492. "exclude_plugins": [],
  493. "include_plugins": [],
  494. }
  495. if auto_upgrade:
  496. auto_upgrade_dict = {
  497. "strategy_setting": auto_upgrade.strategy_setting,
  498. "upgrade_time_of_day": auto_upgrade.upgrade_time_of_day,
  499. "upgrade_mode": auto_upgrade.upgrade_mode,
  500. "exclude_plugins": auto_upgrade.exclude_plugins,
  501. "include_plugins": auto_upgrade.include_plugins,
  502. }
  503. return jsonable_encoder({"permission": permission_dict, "auto_upgrade": auto_upgrade_dict})
  504. class PluginAutoUpgradeExcludePluginApi(Resource):
  505. @setup_required
  506. @login_required
  507. @account_initialization_required
  508. def post(self):
  509. # exclude one single plugin
  510. tenant_id = current_user.current_tenant_id
  511. req = reqparse.RequestParser()
  512. req.add_argument("plugin_id", type=str, required=True, location="json")
  513. args = req.parse_args()
  514. return jsonable_encoder({"success": PluginAutoUpgradeService.exclude_plugin(tenant_id, args["plugin_id"])})
  515. api.add_resource(PluginDebuggingKeyApi, "/workspaces/current/plugin/debugging-key")
  516. api.add_resource(PluginListApi, "/workspaces/current/plugin/list")
  517. api.add_resource(PluginListLatestVersionsApi, "/workspaces/current/plugin/list/latest-versions")
  518. api.add_resource(PluginListInstallationsFromIdsApi, "/workspaces/current/plugin/list/installations/ids")
  519. api.add_resource(PluginIconApi, "/workspaces/current/plugin/icon")
  520. api.add_resource(PluginUploadFromPkgApi, "/workspaces/current/plugin/upload/pkg")
  521. api.add_resource(PluginUploadFromGithubApi, "/workspaces/current/plugin/upload/github")
  522. api.add_resource(PluginUploadFromBundleApi, "/workspaces/current/plugin/upload/bundle")
  523. api.add_resource(PluginInstallFromPkgApi, "/workspaces/current/plugin/install/pkg")
  524. api.add_resource(PluginInstallFromGithubApi, "/workspaces/current/plugin/install/github")
  525. api.add_resource(PluginUpgradeFromMarketplaceApi, "/workspaces/current/plugin/upgrade/marketplace")
  526. api.add_resource(PluginUpgradeFromGithubApi, "/workspaces/current/plugin/upgrade/github")
  527. api.add_resource(PluginInstallFromMarketplaceApi, "/workspaces/current/plugin/install/marketplace")
  528. api.add_resource(PluginFetchManifestApi, "/workspaces/current/plugin/fetch-manifest")
  529. api.add_resource(PluginFetchInstallTasksApi, "/workspaces/current/plugin/tasks")
  530. api.add_resource(PluginFetchInstallTaskApi, "/workspaces/current/plugin/tasks/<task_id>")
  531. api.add_resource(PluginDeleteInstallTaskApi, "/workspaces/current/plugin/tasks/<task_id>/delete")
  532. api.add_resource(PluginDeleteAllInstallTaskItemsApi, "/workspaces/current/plugin/tasks/delete_all")
  533. api.add_resource(PluginDeleteInstallTaskItemApi, "/workspaces/current/plugin/tasks/<task_id>/delete/<path:identifier>")
  534. api.add_resource(PluginUninstallApi, "/workspaces/current/plugin/uninstall")
  535. api.add_resource(PluginFetchMarketplacePkgApi, "/workspaces/current/plugin/marketplace/pkg")
  536. api.add_resource(PluginChangePermissionApi, "/workspaces/current/plugin/permission/change")
  537. api.add_resource(PluginFetchPermissionApi, "/workspaces/current/plugin/permission/fetch")
  538. api.add_resource(PluginFetchDynamicSelectOptionsApi, "/workspaces/current/plugin/parameters/dynamic-options")
  539. api.add_resource(PluginFetchPreferencesApi, "/workspaces/current/plugin/preferences/fetch")
  540. api.add_resource(PluginChangePreferencesApi, "/workspaces/current/plugin/preferences/change")
  541. api.add_resource(PluginAutoUpgradeExcludePluginApi, "/workspaces/current/plugin/preferences/autoupgrade/exclude")