feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,233 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
createContext,
type PropsWithChildren,
type FC,
useRef,
useContext,
} from 'react';
import { useStoreWithEqualityFn } from 'zustand/traditional';
import { shallow } from 'zustand/shallow';
import { useCreation } from 'ahooks';
import { REPORT_EVENTS } from '@coze-arch/report-events';
import { CustomError } from '@coze-arch/bot-error';
import { type BotPluginStateAction } from './types/store/plugin';
import { type BotPluginStore } from './types/store';
import {
createPluginHistoryPanelUIStore,
type PluginHistoryPanelUIStore,
} from './store/plugin-history-panel-ui';
import { createPluginStore } from './store/plugin';
interface StoreRef {
plugin?: BotPluginStore;
}
export interface PluginNavType {
toResource?: (
res: string,
resID?: string,
query?: Record<string, string>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options?: Record<string, any>,
) => void;
// submodule
tool?: (
toolID: string,
query?: Record<string, string>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options?: Record<string, any>,
) => void;
mocksetList?: (
toolID: string,
query?: Record<string, string>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options?: Record<string, any>,
) => void;
mocksetDetail?: (
toolID: string,
mocksetID: string,
query?: Record<string, string>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options?: Record<string, any>,
) => void;
cloudIDE?: (
query?: Record<string, string>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options?: Record<string, any>,
) => void;
}
export interface CallbacksType {
onUpdateDisplayName?: (displayName: string) => void;
onStatusChange?: (status: WidgetUIState) => void;
}
const BotPluginStoreContext = createContext<{
storeRef: StoreRef;
callbacks: CallbacksType;
resourceNavigate: PluginNavType;
memorizedStoreSet: null | {
usePluginHistoryPanelUIStore: PluginHistoryPanelUIStore;
};
}>({
storeRef: {
plugin: undefined,
},
callbacks: {},
resourceNavigate: {},
memorizedStoreSet: null,
});
interface PluginHistoryControllerType {
reload: () => void;
}
const BotPluginControllerContext = createContext<{
pluginHistoryController: { current: PluginHistoryControllerType | null };
registryPluginHistoryController: (
controller: PluginHistoryControllerType,
) => void;
}>({
pluginHistoryController: { current: null },
registryPluginHistoryController: () => 0,
});
export type WidgetUIState = 'loading' | 'saving' | 'error' | 'normal';
export const BotPluginStoreProvider: FC<
PropsWithChildren<{
pluginID: string;
spaceID: string;
projectID?: string;
version?: string;
onUpdateDisplayName?: (displayName: string) => void;
onStatusChange?: (status: WidgetUIState) => void;
resourceNavigate: PluginNavType;
}>
> = ({
pluginID,
spaceID,
projectID,
version,
children,
onUpdateDisplayName,
onStatusChange,
resourceNavigate,
}) => {
const store = useRef<StoreRef>({});
const usePluginHistoryPanelUIStore = useCreation(
() => createPluginHistoryPanelUIStore(),
[],
);
const pluginHistoryControllerRef = useRef<PluginHistoryControllerType | null>(
null,
);
if (!store.current?.plugin) {
store.current.plugin = createPluginStore({
pluginID,
spaceID,
projectID,
version,
});
}
return (
<BotPluginStoreContext.Provider
value={{
storeRef: store.current,
memorizedStoreSet: { usePluginHistoryPanelUIStore },
callbacks: {
onUpdateDisplayName,
onStatusChange,
},
resourceNavigate,
}}
>
<BotPluginControllerContext.Provider
value={{
pluginHistoryController: pluginHistoryControllerRef,
registryPluginHistoryController: inputController => {
pluginHistoryControllerRef.current = inputController;
},
}}
>
{children}
</BotPluginControllerContext.Provider>
</BotPluginStoreContext.Provider>
);
};
export const usePluginStore: <T>(
selector: (store: BotPluginStateAction) => T,
) => T = selector => {
const ctx = useContext(BotPluginStoreContext);
if (!ctx.storeRef.plugin) {
throw new CustomError(REPORT_EVENTS.normalError, 'plugin store context');
}
return useStoreWithEqualityFn(ctx.storeRef.plugin, selector, shallow);
};
export const usePluginStoreInstance = () => {
const ctx = useContext(BotPluginStoreContext);
if (!ctx.storeRef.plugin) {
throw new CustomError(REPORT_EVENTS.normalError, 'plugin store context');
}
return ctx.storeRef.plugin;
};
export const usePluginCallbacks: () => CallbacksType = () => {
const {
callbacks: { onStatusChange, onUpdateDisplayName },
} = useContext(BotPluginStoreContext);
return { onStatusChange, onUpdateDisplayName };
};
export const usePluginNavigate: () => PluginNavType = () => {
const { resourceNavigate } = useContext(BotPluginStoreContext);
return resourceNavigate;
};
export const useMemorizedPluginStoreSet = () => {
const { memorizedStoreSet } = useContext(BotPluginStoreContext);
if (!memorizedStoreSet) {
throw new Error('plugin memorized store set not provided');
}
return memorizedStoreSet;
};
export const usePluginHistoryController = () => {
const { pluginHistoryController } = useContext(BotPluginControllerContext);
return pluginHistoryController;
};
export const usePluginHistoryControllerRegistry = () => {
const { registryPluginHistoryController } = useContext(
BotPluginControllerContext,
);
return registryPluginHistoryController;
};

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useEffect } from 'react';
import { unlockByFetch } from '../utils/fetch';
export const useUnmountUnlock = (pluginId: string) => {
useEffect(() => {
const unlock = () => unlockByFetch(pluginId);
window.addEventListener('beforeunload', unlock);
return () => {
window.removeEventListener('beforeunload', unlock);
};
}, []);
};

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
BotPluginStoreProvider,
usePluginStore,
usePluginCallbacks,
usePluginNavigate,
useMemorizedPluginStoreSet,
usePluginStoreInstance,
usePluginHistoryController,
usePluginHistoryControllerRegistry,
} from './context';
export { ROLE_TAG_TEXT_MAP } from './types/auth';
export { useUnmountUnlock } from './hook/unlock';
export { checkOutPluginContext, unlockOutPluginContext } from './utils/api';

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { devtools } from 'zustand/middleware';
import { create } from 'zustand';
export interface PluginHistoryPanelUIState {
isVisible: boolean;
}
export interface PluginHistoryPanelUIAction {
setVisible: (action: ((prevVisible: boolean) => boolean) | boolean) => void;
}
export const createPluginHistoryPanelUIStore = () =>
create<PluginHistoryPanelUIState & PluginHistoryPanelUIAction>()(
devtools(
(set, get) => ({
isVisible: false,
setVisible: action =>
set(
{
isVisible:
typeof action === 'boolean' ? action : action(get().isVisible),
},
false,
'setVisible',
),
}),
{
enabled: IS_DEV_MODE,
name: 'botStudio.plugin-history-panel-ui',
},
),
);
export type PluginHistoryPanelUIStore = ReturnType<
typeof createPluginHistoryPanelUIStore
>;

View File

@@ -0,0 +1,264 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @coze-arch/max-line-per-function -- store */
/* eslint-disable max-lines-per-function -- store */
import { devtools, subscribeWithSelector } from 'zustand/middleware';
import { create } from 'zustand';
import { produce } from 'immer';
import { REPORT_EVENTS } from '@coze-arch/report-events';
import { I18n } from '@coze-arch/i18n';
import { UIModal } from '@coze-arch/bot-semi';
import { CustomError } from '@coze-arch/bot-error';
import { PluginDevelopApi } from '@coze-arch/bot-api';
import { getPluginErrorMessage } from '../utils/error';
import { type BotPluginStateAction } from '../types/store/plugin';
import { CreationMethod, ROLE_TAG_TEXT_MAP } from '../types';
export const createPluginStore = (options: {
pluginID: string;
spaceID: string;
projectID?: string;
version?: string;
}) => {
const { pluginID, spaceID, projectID, version } = options;
return create<BotPluginStateAction>()(
devtools(
subscribeWithSelector((set, get) => ({
version,
canEdit: !version,
isUnlocking: false,
pluginId: pluginID,
spaceID,
projectID,
pluginInfo: {},
initSuccessed: false,
getIsIdePlugin: () => {
const { pluginInfo } = get();
return pluginInfo?.creation_method === CreationMethod.IDE;
},
setInitSuccessed: (v: boolean) =>
set(
produce<BotPluginStateAction>(s => {
s.initSuccessed = v;
}),
false,
'setInitSuccessed',
),
setPluginInfo: info =>
set(
produce<BotPluginStateAction>(s => {
s.pluginInfo = info;
}),
false,
'setPluginInfo',
),
setUpdatedInfo: info =>
set(
produce<BotPluginStateAction>(s => {
s.updatedInfo = info;
}),
false,
'setUpdatedInfo',
),
initUserPluginAuth: async () => {
const { setCanEdit } = get();
const resp = await PluginDevelopApi.GetUserAuthority({
project_id: projectID,
plugin_id: pluginID,
creation_method: CreationMethod.COZE,
});
if (resp.code !== 0) {
throw new CustomError(
REPORT_EVENTS.normalError,
getPluginErrorMessage('auth init'),
);
}
set(
produce<BotPluginStateAction>(s => {
s.auth = resp.data;
}),
false,
'initUserPluginAuth',
);
setCanEdit(!!resp.data?.can_edit && !version);
},
setCanEdit(can) {
set(
produce<BotPluginStateAction>(s => {
s.canEdit = can;
}),
false,
'setCanEdit',
);
},
wrapWithCheckLock: fn => async () => {
const { checkPluginIsLockedByOthers } = get();
const isLocked = await checkPluginIsLockedByOthers();
if (isLocked) {
return;
}
fn();
},
initPlugin: async () => {
const { pluginId, setPluginInfo } = get();
const res = await PluginDevelopApi.GetPluginInfo({
plugin_id: pluginId || '',
preview_version_ts: version,
});
if (res?.code !== 0) {
throw new CustomError(
REPORT_EVENTS.normalError,
getPluginErrorMessage('getPluginInfo error'),
);
}
setPluginInfo({ ...res, plugin_id: pluginId || '' });
},
initTool: async () => {
const { pluginId, setUpdatedInfo } = get();
const res = await PluginDevelopApi.GetUpdatedAPIs(
{
plugin_id: pluginId || '',
},
{ __disableErrorToast: true },
);
if (res?.code !== 0) {
throw new CustomError(
REPORT_EVENTS.normalError,
getPluginErrorMessage('getToolInfo error'),
);
}
setUpdatedInfo({
updated_api_names: res?.updated_api_names || [],
created_api_names: res?.created_api_names || [], //暂时只判断新增的tool的mockset权限
});
},
init: async () => {
const { initPlugin, initTool, initUserPluginAuth, setInitSuccessed } =
get();
await Promise.all([initPlugin(), initTool(), initUserPluginAuth()]);
setInitSuccessed(true);
},
checkPluginIsLockedByOthers: async () => {
const { pluginId, getIsIdePlugin } = get();
if (getIsIdePlugin()) {
return false;
}
const resp = await PluginDevelopApi.CheckAndLockPluginEdit({
plugin_id: pluginId,
});
if (resp.code !== 0) {
return false;
}
const { data } = resp;
const user = data?.user;
/**
* 有人占用 & 不是自己
*/
if (data?.Occupied && user && !user.self) {
UIModal.info({
okText: I18n.t('guidance_got_it'),
title: I18n.t('plugin_team_edit_tip_unable_to_edit'),
content: `${user.name}(${
// @ts-expect-error -- linter-disable-autofix
ROLE_TAG_TEXT_MAP[user.space_roly_type]
}) ${I18n.t('plugin_team_edit_tip_another_user_is_editing')}`,
hasCancel: false,
});
return true;
}
return false;
},
unlockPlugin: async () => {
const { pluginId, canEdit, isUnlocking, getIsIdePlugin } = get();
if (getIsIdePlugin()) {
return;
}
if (!canEdit) {
return;
}
if (isUnlocking) {
return;
}
try {
set(
produce<BotPluginStateAction>(s => {
s.isUnlocking = true;
}),
false,
'unlocking-true',
);
const resp = await PluginDevelopApi.UnlockPluginEdit({
plugin_id: pluginId,
});
if (resp.code !== 0) {
throw new CustomError(
REPORT_EVENTS.normalError,
getPluginErrorMessage('unlock'),
);
}
} finally {
set(
produce<BotPluginStateAction>(s => {
s.isUnlocking = false;
}),
false,
'unlocking-false',
);
}
},
updatePluginInfoByImmer: updateFn =>
set(
produce<BotPluginStateAction>(s => {
updateFn(s.pluginInfo);
}),
false,
'updatePluginInfoByImmer',
),
})),
{
enabled: IS_DEV_MODE,
name: 'botStudio.botPlugin',
},
),
);
};

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { SpaceRoleType } from '@coze-arch/bot-api/plugin_develop';
import { I18n } from '@coze-arch/i18n';
export { type GetUserAuthorityData } from '@coze-arch/bot-api/plugin_develop';
export { CreationMethod } from '@coze-arch/bot-api/plugin_develop';
export const ROLE_TAG_TEXT_MAP = {
[SpaceRoleType.Owner]: I18n.t('team_management_role_owner', {}, 'Owner'),
[SpaceRoleType.Admin]: I18n.t('team_management_role_admin', {}, 'Admin'),
[SpaceRoleType.Member]: I18n.t('team_management_role_member', {}, 'Member'),
[SpaceRoleType.Default]: '-',
} as const;
export { SpaceRoleType };

View File

@@ -0,0 +1,18 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types='@coze-arch/bot-typings' />
/// <reference types='@coze-arch/bot-env/typings' />

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @coze-arch/no-batch-import-or-export -- 内部均为具名export */
export * from './auth';
export * from './user';
export * from './tool';

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type createPluginStore } from '@/store/plugin';
export type BotPluginStore = ReturnType<typeof createPluginStore>;

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
type GetUserAuthorityData,
type GetUpdatedAPIsResponse,
type GetPluginInfoResponse,
} from '@/types';
interface PluginState {
readonly pluginId: string;
readonly spaceID: string;
projectID?: string;
isUnlocking: boolean;
auth?: GetUserAuthorityData;
canEdit: boolean;
updatedInfo?: GetUpdatedAPIsResponse;
pluginInfo?: GetPluginInfoResponse & { plugin_id?: string };
initSuccessed: boolean;
version?: string;
}
interface PluginGetter {
getIsIdePlugin: () => boolean;
}
interface PluginAction {
initUserPluginAuth: () => void;
checkPluginIsLockedByOthers: () => Promise<boolean>;
wrapWithCheckLock: (fn: () => void) => () => Promise<void>;
unlockPlugin: () => Promise<void>;
setPluginInfo: (info: PluginState['pluginInfo']) => void;
setUpdatedInfo: (info: GetUpdatedAPIsResponse) => void;
initPlugin: () => Promise<void>;
initTool: () => Promise<void>;
init: () => Promise<void>;
setCanEdit: (can: boolean) => void;
setInitSuccessed: (v: boolean) => void;
updatePluginInfoByImmer: (
updateFn: (pluginInfo: PluginState['pluginInfo']) => void,
) => void;
}
export type BotPluginStateAction = PluginState & PluginGetter & PluginAction;

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
type GetUpdatedAPIsResponse,
type GetPluginInfoResponse,
} from '@coze-arch/bot-api/plugin_develop';

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { type Creator } from '@coze-arch/bot-api/plugin_develop';

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { REPORT_EVENTS } from '@coze-arch/report-events';
import { I18n } from '@coze-arch/i18n';
import { UIModal } from '@coze-arch/bot-semi';
import { CustomError } from '@coze-arch/bot-error';
import { PluginDevelopApi } from '@coze-arch/bot-api';
import { getPluginErrorMessage } from '../utils/error';
import { ROLE_TAG_TEXT_MAP } from '../types';
export const checkOutPluginContext = async (pluginId: string) => {
const resp = await PluginDevelopApi.CheckAndLockPluginEdit({
plugin_id: pluginId,
});
if (resp.code !== 0) {
return false;
}
const { data } = resp;
const user = data?.user;
/**
* 有人占用 & 不是自己
*/
if (data?.Occupied && user && !user.self) {
UIModal.info({
okText: I18n.t('guidance_got_it'),
title: I18n.t('plugin_team_edit_tip_unable_to_edit'),
content: `${user.name}(${
// @ts-expect-error -- linter-disable-autofix
ROLE_TAG_TEXT_MAP[user.space_roly_type]
}) ${I18n.t('plugin_team_edit_tip_another_user_is_editing')}`,
hasCancel: false,
});
return true;
}
return false;
};
export const unlockOutPluginContext = async (pluginId: string) => {
const resp = await PluginDevelopApi.UnlockPluginEdit({
plugin_id: pluginId,
});
if (resp.code !== 0) {
throw new CustomError(
REPORT_EVENTS.normalError,
getPluginErrorMessage('unlock out'),
);
}
};

View File

@@ -0,0 +1,18 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const getPluginErrorMessage = (msg: string) =>
`[PluginStore.Error]: ${msg}`;

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const unlockByFetch = (pluginId: string) =>
fetch('/api/plugin_api/unlock_plugin_edit', {
keepalive: true,
method: 'POST',
body: JSON.stringify({
plugin_id: pluginId,
}),
headers: {
'Content-Type': 'application/json',
},
});