Merge upstream/master into dev (76 commits)
Resolved 5 conflicts: - .github/workflows/docker.yml, release.yml: kept fork stubs (CI handled by build-prod/build-dev) - server/src/routes/secrets.ts: kept fork's /usages route alongside upstream's /usage, /access-events - server/src/services/secrets.ts: kept fork's usages() function and in-use deletion guard, layered before upstream's soft-delete + provider cleanup in remove() - ui/src/api/secrets.ts: kept fork's usages() method alongside upstream's vault methods Typechecks pass on @paperclipai/shared, @paperclipai/server, @paperclipai/ui.
This commit is contained in:
+25
-12
@@ -69,6 +69,15 @@ export interface AgentPermissionUpdate {
|
||||
canAssignTasks: boolean;
|
||||
}
|
||||
|
||||
export interface AgentWakeRequest {
|
||||
source?: "timer" | "assignment" | "on_demand" | "automation";
|
||||
triggerDetail?: "manual" | "ping" | "callback" | "system";
|
||||
reason?: string | null;
|
||||
payload?: Record<string, unknown> | null;
|
||||
idempotencyKey?: string | null;
|
||||
forceFreshSession?: boolean;
|
||||
}
|
||||
|
||||
function withCompanyScope(path: string, companyId?: string) {
|
||||
if (!companyId) return path;
|
||||
const separator = path.includes("?") ? "&" : "?";
|
||||
@@ -171,10 +180,19 @@ export const agentsApi = {
|
||||
api.get<AgentTaskSession[]>(agentPath(id, companyId, "/task-sessions")),
|
||||
resetSession: (id: string, taskKey?: string | null, companyId?: string) =>
|
||||
api.post<void>(agentPath(id, companyId, "/runtime-state/reset-session"), { taskKey: taskKey ?? null }),
|
||||
adapterModels: (companyId: string, type: string, options?: { refresh?: boolean }) =>
|
||||
api.get<AdapterModel[]>(
|
||||
`/companies/${encodeURIComponent(companyId)}/adapters/${encodeURIComponent(type)}/models${options?.refresh ? "?refresh=1" : ""}`,
|
||||
),
|
||||
adapterModels: (
|
||||
companyId: string,
|
||||
type: string,
|
||||
options?: { refresh?: boolean; environmentId?: string | null },
|
||||
) => {
|
||||
const params = new URLSearchParams();
|
||||
if (options?.refresh) params.set("refresh", "1");
|
||||
if (options?.environmentId) params.set("environmentId", options.environmentId);
|
||||
const query = params.size > 0 ? `?${params.toString()}` : "";
|
||||
return api.get<AdapterModel[]>(
|
||||
`/companies/${encodeURIComponent(companyId)}/adapters/${encodeURIComponent(type)}/models${query}`,
|
||||
);
|
||||
},
|
||||
detectModel: (companyId: string, type: string) =>
|
||||
api.get<DetectedAdapterModel | null>(
|
||||
`/companies/${encodeURIComponent(companyId)}/adapters/${encodeURIComponent(type)}/detect-model`,
|
||||
@@ -195,16 +213,11 @@ export const agentsApi = {
|
||||
`/companies/${companyId}/adapters/${type}/test-environment`,
|
||||
data,
|
||||
),
|
||||
invoke: (id: string, companyId?: string) => api.post<HeartbeatRun>(agentPath(id, companyId, "/heartbeat/invoke"), {}),
|
||||
invoke: (id: string, companyId?: string, data: AgentWakeRequest = {}) =>
|
||||
api.post<HeartbeatRun>(agentPath(id, companyId, "/heartbeat/invoke"), data),
|
||||
wakeup: (
|
||||
id: string,
|
||||
data: {
|
||||
source?: "timer" | "assignment" | "on_demand" | "automation";
|
||||
triggerDetail?: "manual" | "ping" | "callback" | "system";
|
||||
reason?: string | null;
|
||||
payload?: Record<string, unknown> | null;
|
||||
idempotencyKey?: string | null;
|
||||
},
|
||||
data: AgentWakeRequest,
|
||||
companyId?: string,
|
||||
) => api.post<AgentWakeupResponse>(agentPath(id, companyId, "/wakeup"), data),
|
||||
loginWithClaude: (id: string, companyId?: string) =>
|
||||
|
||||
+10
-1
@@ -12,6 +12,7 @@ import type {
|
||||
IssueComment,
|
||||
IssueDocument,
|
||||
IssueLabel,
|
||||
IssueRetryNowResponse,
|
||||
IssueThreadInteraction,
|
||||
IssueTreeControlPreview,
|
||||
IssueTreeHold,
|
||||
@@ -43,6 +44,7 @@ export const issuesApi = {
|
||||
workspaceId?: string;
|
||||
executionWorkspaceId?: string;
|
||||
originKind?: string;
|
||||
originKindPrefix?: string;
|
||||
originId?: string;
|
||||
descendantOf?: string;
|
||||
includeRoutineExecutions?: boolean;
|
||||
@@ -66,6 +68,7 @@ export const issuesApi = {
|
||||
if (filters?.workspaceId) params.set("workspaceId", filters.workspaceId);
|
||||
if (filters?.executionWorkspaceId) params.set("executionWorkspaceId", filters.executionWorkspaceId);
|
||||
if (filters?.originKind) params.set("originKind", filters.originKind);
|
||||
if (filters?.originKindPrefix) params.set("originKindPrefix", filters.originKindPrefix);
|
||||
if (filters?.originId) params.set("originId", filters.originId);
|
||||
if (filters?.descendantOf) params.set("descendantOf", filters.descendantOf);
|
||||
if (filters?.includeRoutineExecutions) params.set("includeRoutineExecutions", "true");
|
||||
@@ -126,6 +129,9 @@ export const issuesApi = {
|
||||
}>(`/issues/${id}/tree-control/state`),
|
||||
releaseTreeHold: (id: string, holdId: string, data: ReleaseIssueTreeHold) =>
|
||||
api.post<IssueTreeHold>(`/issues/${id}/tree-holds/${holdId}/release`, data),
|
||||
checkMonitorNow: (id: string) => api.post<{ ok: true }>(`/issues/${id}/monitor/check-now`, {}),
|
||||
retryScheduledRetryNow: (id: string) =>
|
||||
api.post<IssueRetryNowResponse>(`/issues/${id}/scheduled-retry/retry-now`, {}),
|
||||
remove: (id: string) => api.delete<Issue>(`/issues/${id}`),
|
||||
checkout: (id: string, agentId: string) =>
|
||||
api.post<Issue>(`/issues/${id}/checkout`, {
|
||||
@@ -171,7 +177,10 @@ export const issuesApi = {
|
||||
getComment: (id: string, commentId: string) =>
|
||||
api.get<IssueComment>(`/issues/${id}/comments/${commentId}`),
|
||||
listFeedbackVotes: (id: string) => api.get<FeedbackVote[]>(`/issues/${id}/feedback-votes`),
|
||||
getCostSummary: (id: string) => api.get<IssueCostSummary>(`/issues/${id}/cost-summary`),
|
||||
getCostSummary: (id: string, options: { excludeRoot?: boolean } = {}) => {
|
||||
const qs = options.excludeRoot ? "?excludeRoot=true" : "";
|
||||
return api.get<IssueCostSummary>(`/issues/${id}/cost-summary${qs}`);
|
||||
},
|
||||
listFeedbackTraces: (id: string, filters?: Record<string, string | boolean | undefined>) => {
|
||||
const params = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(filters ?? {})) {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mockApi = vi.hoisted(() => ({
|
||||
get: vi.fn(),
|
||||
post: vi.fn(),
|
||||
put: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./client", () => ({
|
||||
api: mockApi,
|
||||
}));
|
||||
|
||||
import { pluginsApi } from "./plugins";
|
||||
|
||||
describe("pluginsApi local folders", () => {
|
||||
beforeEach(() => {
|
||||
mockApi.get.mockReset();
|
||||
mockApi.post.mockReset();
|
||||
mockApi.put.mockReset();
|
||||
mockApi.get.mockResolvedValue({});
|
||||
mockApi.post.mockResolvedValue({});
|
||||
mockApi.put.mockResolvedValue({});
|
||||
});
|
||||
|
||||
it("lists company-scoped local folders for a plugin", async () => {
|
||||
await pluginsApi.listLocalFolders("plugin-1", "company-1");
|
||||
|
||||
expect(mockApi.get).toHaveBeenCalledWith(
|
||||
"/plugins/plugin-1/companies/company-1/local-folders",
|
||||
);
|
||||
});
|
||||
|
||||
it("validates a candidate folder path without saving", async () => {
|
||||
await pluginsApi.validateLocalFolder("plugin-1", "company-1", "wiki-root", {
|
||||
path: "/tmp/wiki",
|
||||
access: "readWrite",
|
||||
requiredFiles: ["WIKI.md"],
|
||||
});
|
||||
|
||||
expect(mockApi.post).toHaveBeenCalledWith(
|
||||
"/plugins/plugin-1/companies/company-1/local-folders/wiki-root/validate",
|
||||
{
|
||||
path: "/tmp/wiki",
|
||||
access: "readWrite",
|
||||
requiredFiles: ["WIKI.md"],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("saves through the local-folder PUT endpoint", async () => {
|
||||
await pluginsApi.configureLocalFolder("plugin-1", "company-1", "wiki-root", {
|
||||
path: "/tmp/wiki",
|
||||
requiredDirectories: ["wiki"],
|
||||
});
|
||||
|
||||
expect(mockApi.put).toHaveBeenCalledWith(
|
||||
"/plugins/plugin-1/companies/company-1/local-folders/wiki-root",
|
||||
{
|
||||
path: "/tmp/wiki",
|
||||
requiredDirectories: ["wiki"],
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -14,6 +14,7 @@ import type {
|
||||
PluginLauncherDeclaration,
|
||||
PluginLauncherRenderContextSnapshot,
|
||||
PluginUiSlotDeclaration,
|
||||
PluginLocalFolderDeclaration,
|
||||
PluginRecord,
|
||||
PluginConfig,
|
||||
PluginStatus,
|
||||
@@ -140,6 +141,54 @@ export interface AvailablePluginExample {
|
||||
tag: "example";
|
||||
}
|
||||
|
||||
export interface PluginLocalFolderProblem {
|
||||
code:
|
||||
| "not_configured"
|
||||
| "not_absolute"
|
||||
| "missing"
|
||||
| "not_directory"
|
||||
| "not_readable"
|
||||
| "not_writable"
|
||||
| "missing_directory"
|
||||
| "missing_file"
|
||||
| "path_traversal"
|
||||
| "symlink_escape"
|
||||
| "atomic_write_failed";
|
||||
message: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface PluginLocalFolderStatus {
|
||||
folderKey: string;
|
||||
configured: boolean;
|
||||
path: string | null;
|
||||
realPath: string | null;
|
||||
access: "read" | "readWrite";
|
||||
readable: boolean;
|
||||
writable: boolean;
|
||||
requiredDirectories: string[];
|
||||
requiredFiles: string[];
|
||||
missingDirectories: string[];
|
||||
missingFiles: string[];
|
||||
healthy: boolean;
|
||||
problems: PluginLocalFolderProblem[];
|
||||
checkedAt: string;
|
||||
}
|
||||
|
||||
export interface PluginLocalFoldersResponse {
|
||||
pluginId: string;
|
||||
companyId: string;
|
||||
declarations: PluginLocalFolderDeclaration[];
|
||||
folders: PluginLocalFolderStatus[];
|
||||
}
|
||||
|
||||
export interface PluginLocalFolderSaveInput {
|
||||
path: string;
|
||||
access?: "read" | "readWrite";
|
||||
requiredDirectories?: string[];
|
||||
requiredFiles?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin management API client.
|
||||
*
|
||||
@@ -337,6 +386,48 @@ export const pluginsApi = {
|
||||
testConfig: (pluginId: string, configJson: Record<string, unknown>) =>
|
||||
api.post<{ valid: boolean; message?: string }>(`/plugins/${pluginId}/config/test`, { configJson }),
|
||||
|
||||
/**
|
||||
* List manifest-declared and stored company-scoped local folders for a plugin.
|
||||
*/
|
||||
listLocalFolders: (pluginId: string, companyId: string) =>
|
||||
api.get<PluginLocalFoldersResponse>(`/plugins/${pluginId}/companies/${companyId}/local-folders`),
|
||||
|
||||
/**
|
||||
* Inspect a configured local folder without changing persisted settings.
|
||||
*/
|
||||
localFolderStatus: (pluginId: string, companyId: string, folderKey: string) =>
|
||||
api.get<PluginLocalFolderStatus>(
|
||||
`/plugins/${pluginId}/companies/${companyId}/local-folders/${encodeURIComponent(folderKey)}/status`,
|
||||
),
|
||||
|
||||
/**
|
||||
* Validate a candidate local folder path without saving it.
|
||||
*/
|
||||
validateLocalFolder: (
|
||||
pluginId: string,
|
||||
companyId: string,
|
||||
folderKey: string,
|
||||
input: PluginLocalFolderSaveInput,
|
||||
) =>
|
||||
api.post<PluginLocalFolderStatus>(
|
||||
`/plugins/${pluginId}/companies/${companyId}/local-folders/${encodeURIComponent(folderKey)}/validate`,
|
||||
input,
|
||||
),
|
||||
|
||||
/**
|
||||
* Persist a company-scoped local folder path and return its inspected status.
|
||||
*/
|
||||
configureLocalFolder: (
|
||||
pluginId: string,
|
||||
companyId: string,
|
||||
folderKey: string,
|
||||
input: PluginLocalFolderSaveInput,
|
||||
) =>
|
||||
api.put<PluginLocalFolderStatus>(
|
||||
`/plugins/${pluginId}/companies/${companyId}/local-folders/${encodeURIComponent(folderKey)}`,
|
||||
input,
|
||||
),
|
||||
|
||||
// ===========================================================================
|
||||
// Bridge proxy endpoints — used by the plugin UI bridge runtime
|
||||
// ===========================================================================
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
Routine,
|
||||
RoutineDetail,
|
||||
RoutineListItem,
|
||||
RoutineRevision,
|
||||
RoutineRun,
|
||||
RoutineRunSummary,
|
||||
RoutineTrigger,
|
||||
@@ -21,6 +22,18 @@ export interface RotateRoutineTriggerResponse {
|
||||
secretMaterial: RoutineTriggerSecretMaterial;
|
||||
}
|
||||
|
||||
export interface RestoreRoutineRevisionSecretMaterial extends RoutineTriggerSecretMaterial {
|
||||
triggerId: string;
|
||||
}
|
||||
|
||||
export interface RestoreRoutineRevisionResponse {
|
||||
routine: Routine;
|
||||
revision: RoutineRevision;
|
||||
restoredFromRevisionId: string;
|
||||
restoredFromRevisionNumber: number;
|
||||
secretMaterials: RestoreRoutineRevisionSecretMaterial[];
|
||||
}
|
||||
|
||||
export const routinesApi = {
|
||||
list: (companyId: string, filters?: { projectId?: string | null }) => {
|
||||
const params = new URLSearchParams();
|
||||
@@ -32,6 +45,16 @@ export const routinesApi = {
|
||||
api.post<Routine>(`/companies/${companyId}/routines`, data),
|
||||
get: (id: string) => api.get<RoutineDetail>(`/routines/${id}`),
|
||||
update: (id: string, data: Record<string, unknown>) => api.patch<Routine>(`/routines/${id}`, data),
|
||||
listRevisions: (id: string) => api.get<RoutineRevision[]>(`/routines/${id}/revisions`),
|
||||
restoreRevision: (
|
||||
id: string,
|
||||
revisionId: string,
|
||||
body: { changeSummary?: string | null } = {},
|
||||
) =>
|
||||
api.post<RestoreRoutineRevisionResponse>(
|
||||
`/routines/${id}/revisions/${revisionId}/restore`,
|
||||
body,
|
||||
),
|
||||
listRuns: (id: string, limit: number = 50) => api.get<RoutineRunSummary[]>(`/routines/${id}/runs?limit=${limit}`),
|
||||
createTrigger: (id: string, data: Record<string, unknown>) =>
|
||||
api.post<RoutineTriggerResponse>(`/routines/${id}/triggers`, data),
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { CompanySearchResponse, CompanySearchScope } from "@paperclipai/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export interface CompanySearchParams {
|
||||
q: string;
|
||||
scope?: CompanySearchScope;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export const searchApi = {
|
||||
search: (companyId: string, params: CompanySearchParams) => {
|
||||
const search = new URLSearchParams();
|
||||
search.set("q", params.q);
|
||||
if (params.scope) search.set("scope", params.scope);
|
||||
if (params.limit !== undefined) search.set("limit", String(params.limit));
|
||||
if (params.offset !== undefined) search.set("offset", String(params.offset));
|
||||
const qs = search.toString();
|
||||
return api.get<CompanySearchResponse>(
|
||||
`/companies/${companyId}/search${qs ? `?${qs}` : ""}`,
|
||||
);
|
||||
},
|
||||
};
|
||||
+129
-16
@@ -1,30 +1,143 @@
|
||||
import type { CompanySecret, SecretProviderDescriptor, SecretProvider } from "@paperclipai/shared";
|
||||
import type {
|
||||
CompanySecret,
|
||||
CompanySecretUsageBinding,
|
||||
CompanySecretProviderConfig,
|
||||
RemoteSecretImportPreviewResult,
|
||||
RemoteSecretImportResult,
|
||||
SecretAccessEvent,
|
||||
SecretManagedMode,
|
||||
SecretProvider,
|
||||
SecretProviderConfigStatus,
|
||||
SecretProviderConfigHealthResponse,
|
||||
SecretProviderDescriptor,
|
||||
SecretStatus,
|
||||
} from "@paperclipai/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export interface SecretUsageResponse {
|
||||
secretId: string;
|
||||
bindings: CompanySecretUsageBinding[];
|
||||
}
|
||||
|
||||
export interface CreateSecretInput {
|
||||
name: string;
|
||||
key?: string;
|
||||
provider?: SecretProvider;
|
||||
managedMode?: SecretManagedMode;
|
||||
value?: string | null;
|
||||
description?: string | null;
|
||||
externalRef?: string | null;
|
||||
providerVersionRef?: string | null;
|
||||
providerConfigId?: string | null;
|
||||
providerMetadata?: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface SecretProviderHealthResponse {
|
||||
providers: Array<{
|
||||
provider: SecretProvider;
|
||||
status: "ok" | "warn" | "error";
|
||||
message: string;
|
||||
warnings?: string[];
|
||||
backupGuidance?: string[];
|
||||
details?: Record<string, unknown>;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface UpdateSecretInput {
|
||||
name?: string;
|
||||
key?: string;
|
||||
status?: SecretStatus;
|
||||
description?: string | null;
|
||||
externalRef?: string | null;
|
||||
providerMetadata?: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface RotateSecretInput {
|
||||
value?: string | null;
|
||||
externalRef?: string | null;
|
||||
providerVersionRef?: string | null;
|
||||
providerConfigId?: string | null;
|
||||
}
|
||||
|
||||
export interface CreateSecretProviderConfigInput {
|
||||
provider: SecretProvider;
|
||||
displayName: string;
|
||||
status?: SecretProviderConfigStatus;
|
||||
isDefault?: boolean;
|
||||
config?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface UpdateSecretProviderConfigInput {
|
||||
displayName?: string;
|
||||
status?: SecretProviderConfigStatus;
|
||||
isDefault?: boolean;
|
||||
config?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface RemoteImportPreviewInput {
|
||||
providerConfigId: string;
|
||||
query?: string | null;
|
||||
nextToken?: string | null;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export interface RemoteImportSelectionInput {
|
||||
externalRef: string;
|
||||
name?: string | null;
|
||||
key?: string | null;
|
||||
description?: string | null;
|
||||
providerVersionRef?: string | null;
|
||||
providerMetadata?: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface RemoteImportInput {
|
||||
providerConfigId: string;
|
||||
secrets: RemoteImportSelectionInput[];
|
||||
}
|
||||
|
||||
export const secretsApi = {
|
||||
list: (companyId: string) => api.get<CompanySecret[]>(`/companies/${companyId}/secrets`),
|
||||
providers: (companyId: string) =>
|
||||
api.get<SecretProviderDescriptor[]>(`/companies/${companyId}/secret-providers`),
|
||||
create: (
|
||||
companyId: string,
|
||||
data: {
|
||||
name: string;
|
||||
value: string;
|
||||
provider?: SecretProvider;
|
||||
description?: string | null;
|
||||
externalRef?: string | null;
|
||||
},
|
||||
) => api.post<CompanySecret>(`/companies/${companyId}/secrets`, data),
|
||||
rotate: (id: string, data: { value: string; externalRef?: string | null }) =>
|
||||
providerHealth: (companyId: string) =>
|
||||
api.get<SecretProviderHealthResponse>(`/companies/${companyId}/secret-providers/health`),
|
||||
providerConfigs: (companyId: string) =>
|
||||
api.get<CompanySecretProviderConfig[]>(`/companies/${companyId}/secret-provider-configs`),
|
||||
createProviderConfig: (companyId: string, data: CreateSecretProviderConfigInput) =>
|
||||
api.post<CompanySecretProviderConfig>(`/companies/${companyId}/secret-provider-configs`, data),
|
||||
updateProviderConfig: (id: string, data: UpdateSecretProviderConfigInput) =>
|
||||
api.patch<CompanySecretProviderConfig>(`/secret-provider-configs/${id}`, data),
|
||||
disableProviderConfig: (id: string) =>
|
||||
api.delete<CompanySecretProviderConfig>(`/secret-provider-configs/${id}`),
|
||||
setDefaultProviderConfig: (id: string) =>
|
||||
api.post<CompanySecretProviderConfig>(`/secret-provider-configs/${id}/default`, {}),
|
||||
checkProviderConfigHealth: (id: string) =>
|
||||
api.post<SecretProviderConfigHealthResponse>(`/secret-provider-configs/${id}/health`, {}),
|
||||
create: (companyId: string, data: CreateSecretInput) =>
|
||||
api.post<CompanySecret>(`/companies/${companyId}/secrets`, data),
|
||||
update: (id: string, data: UpdateSecretInput) =>
|
||||
api.patch<CompanySecret>(`/secrets/${id}`, data),
|
||||
rotate: (id: string, data: RotateSecretInput) =>
|
||||
api.post<CompanySecret>(`/secrets/${id}/rotate`, data),
|
||||
update: (
|
||||
id: string,
|
||||
data: { name?: string; description?: string | null; externalRef?: string | null },
|
||||
) => api.patch<CompanySecret>(`/secrets/${id}`, data),
|
||||
disable: (id: string) =>
|
||||
api.patch<CompanySecret>(`/secrets/${id}`, { status: "disabled" satisfies SecretStatus }),
|
||||
enable: (id: string) =>
|
||||
api.patch<CompanySecret>(`/secrets/${id}`, { status: "active" satisfies SecretStatus }),
|
||||
archive: (id: string) =>
|
||||
api.patch<CompanySecret>(`/secrets/${id}`, { status: "archived" satisfies SecretStatus }),
|
||||
remove: (id: string) => api.delete<{ ok: true }>(`/secrets/${id}`),
|
||||
usages: (id: string) =>
|
||||
api.get<{
|
||||
agents: { id: string; name: string; envKeys: string[] }[];
|
||||
skills: { id: string; name: string; slug: string }[];
|
||||
}>(`/secrets/${id}/usages`),
|
||||
usage: (id: string) => api.get<SecretUsageResponse>(`/secrets/${id}/usage`),
|
||||
accessEvents: (id: string) => api.get<SecretAccessEvent[]>(`/secrets/${id}/access-events`),
|
||||
remoteImportPreview: (companyId: string, data: RemoteImportPreviewInput) =>
|
||||
api.post<RemoteSecretImportPreviewResult>(
|
||||
`/companies/${companyId}/secrets/remote-import/preview`,
|
||||
data,
|
||||
),
|
||||
remoteImport: (companyId: string, data: RemoteImportInput) =>
|
||||
api.post<RemoteSecretImportResult>(`/companies/${companyId}/secrets/remote-import`, data),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user