Skip to content

Commit 970a7bd

Browse files
authored
build @inkeep/agents-manage-ui with Turbopack (#1180)
* use turbopack in build * use turbopack in build * upd * fix server actions * format * update Next.js to 16.1.0, use Turbopack for build * update Next.js to 16.1.0, use Turbopack for build * update Next.js to 16.1.0, use Turbopack for build * fix * upd * move types to utils * rm * Add additional error handling for Cypress tests * format * format
1 parent a31a497 commit 970a7bd

File tree

13 files changed

+339
-357
lines changed

13 files changed

+339
-357
lines changed

.changeset/vast-stars-kick.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@inkeep/agents-manage-ui": patch
3+
---
4+
5+
update Next.js to 16.1.0, use Turbopack for build

agents-docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"fumadocs-ui": "^16.1.0",
3939
"hast-util-to-jsx-runtime": "^2.3.6",
4040
"lucide-react": "^0.503.0",
41-
"next": "16.0.10",
41+
"next": "16.1.0",
4242
"posthog-js": "^1.308.0",
4343
"posthog-node": "^5.17.3",
4444
"react": "19.2.0",

agents-manage-ui/cypress/support/e2e.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ Cypress.on('uncaught:exception', (err) => {
2020
if (
2121
// Promise from monaco-editor
2222
err.message.includes(' > Canceled') ||
23-
err.message.includes(' > ResizeObserver loop completed with undelivered notifications.')
23+
err.message.includes(' > ResizeObserver loop completed with undelivered notifications.') ||
24+
err.message.includes(
25+
" > Uncaught NetworkError: Failed to execute 'importScripts' on 'WorkerGlobalScope': The script at"
26+
)
2427
) {
2528
return false;
2629
}

agents-manage-ui/next.config.ts

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,39 +25,11 @@ const nextConfig: NextConfig = {
2525
},
2626
},
2727
},
28-
webpack(config) {
29-
const { test: _test, ...imageLoaderOptions } = config.module.rules.find(
30-
// @ts-expect-error -- fixme
31-
(rule) => rule.test?.test?.('.svg')
32-
);
33-
config.module.rules.push({
34-
test: /\.svg$/,
35-
oneOf: [
36-
{
37-
// to avoid conflicts with default Next.js svg loader we only match images with resourceQuery ?svgr
38-
resourceQuery: /svgr/,
39-
use: ['@svgr/webpack'],
40-
},
41-
imageLoaderOptions,
42-
],
43-
});
44-
45-
return config;
46-
},
47-
typescript: {
48-
ignoreBuildErrors: process.env.NEXTJS_IGNORE_TYPECHECK === 'true',
49-
},
5028
images: {
5129
// Allow all external image domains since users can provide any URL
5230
remotePatterns: [
53-
{
54-
protocol: 'https',
55-
hostname: '**',
56-
},
57-
{
58-
protocol: 'http',
59-
hostname: '**',
60-
},
31+
{ protocol: 'https', hostname: '**' },
32+
{ protocol: 'http', hostname: '**' },
6133
],
6234
},
6335
};

agents-manage-ui/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"scripts": {
3535
"dev": "next -p 3000",
3636
"build:sync": "cp -r public .next/standalone/agents-manage-ui && mkdir -p .next/standalone/agents-manage-ui/.next/ && cp -r .next/static .next/standalone/agents-manage-ui/.next/static",
37-
"build": "next build --webpack && pnpm build:sync",
37+
"build": "next build && pnpm build:sync",
3838
"start": "node .next/standalone/agents-manage-ui/server.js",
3939
"lint": "biome lint --error-on-warnings",
4040
"lint:fix": "biome check --write",
@@ -98,7 +98,7 @@
9898
"lucide-react": "^0.555.0",
9999
"monaco-editor": "0.55.1",
100100
"nanoid": "^5.1.5",
101-
"next": "16.0.10",
101+
"next": "16.1.0",
102102
"next-themes": "^0.4.6",
103103
"nuqs": "^2.4.3",
104104
"pino": "^9.9.0",

agents-manage-ui/src/hooks/use-oauth-login.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import {
66
import { useRouter } from 'next/navigation';
77
import { useCallback, useRef } from 'react';
88
import { toast } from 'sonner';
9-
import type { OAuthLoginHandler, OAuthLoginParams } from '@/components/agent/copilot/components/connect-tool-card';
9+
import type {
10+
OAuthLoginHandler,
11+
OAuthLoginParams,
12+
} from '@/components/agent/copilot/components/connect-tool-card';
1013
import { useRuntimeConfig } from '@/contexts/runtime-config-context';
1114
import { listCredentialStores } from '@/lib/api/credentialStores';
1215
import { updateMCPTool } from '@/lib/api/tools';

agents-manage-ui/src/lib/actions/__tests__/tool-approval.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { beforeEach, describe, expect, it, vi } from 'vitest';
22
import * as apiConfig from '../../api/api-config';
3-
import { computeDiff, extractFieldsToUpdate, fetchCurrentEntityState } from '../tool-approval';
3+
import {
4+
computeDiff,
5+
extractFieldsToUpdate,
6+
fetchCurrentEntityState,
7+
} from '../tool-approval.utils';
48

59
vi.mock('../../api/api-config', () => ({
610
makeManagementApiRequest: vi.fn(),

agents-manage-ui/src/lib/actions/tool-approval.ts

Lines changed: 1 addition & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,9 @@
1010
*/
1111
'use server';
1212

13-
import { makeManagementApiRequest } from '../api/api-config';
13+
import { computeDiff, extractFieldsToUpdate, fetchCurrentEntityState } from './tool-approval.utils';
1414
import type { ActionResult } from './types';
1515

16-
interface ToolMetadata {
17-
resource: string;
18-
action: string;
19-
entity: string;
20-
}
21-
22-
interface GetEntityParams {
23-
toolName: string;
24-
input: Record<string, any>;
25-
tenantId: string;
26-
projectId: string;
27-
}
28-
2916
interface FieldDiff {
3017
field: string;
3118
oldValue: any;
@@ -39,178 +26,6 @@ interface FetchToolApprovalDiffParams {
3926
projectId: string;
4027
}
4128

42-
function parseToolName(toolName: string): ToolMetadata {
43-
const parts = toolName.split('-');
44-
45-
const actions = ['create', 'update', 'delete', 'get', 'list'];
46-
const actionIndex = parts.findIndex((part) => actions.includes(part));
47-
48-
if (actionIndex === -1) {
49-
throw new Error(`Unable to parse tool name: ${toolName}`);
50-
}
51-
52-
return {
53-
resource: parts.slice(0, actionIndex).join('-'),
54-
action: parts[actionIndex],
55-
entity: parts.slice(actionIndex + 1).join('-'),
56-
};
57-
}
58-
59-
function extractEntityId(input: Record<string, any>, metadata: ToolMetadata): string | null {
60-
const { entity, resource } = metadata;
61-
62-
const request = input.request || input;
63-
64-
const specificIdFields = ['id', `${entity}Id`, `${entity.replace(/-/g, '')}Id`];
65-
66-
for (const field of specificIdFields) {
67-
if (request[field]) {
68-
return request[field];
69-
}
70-
}
71-
72-
const commonIdFields = ['agentId', 'subAgentId', 'toolId'];
73-
74-
if ((resource === 'projects' || resource === 'project') && request.id) {
75-
return request.id;
76-
}
77-
78-
for (const field of commonIdFields) {
79-
if (request[field]) {
80-
return request[field];
81-
}
82-
}
83-
84-
return null;
85-
}
86-
87-
function pluralizeResource(resource: string): string {
88-
const pluralMappings: Record<string, string> = {
89-
agent: 'agents',
90-
'sub-agent': 'sub-agents',
91-
credential: 'credentials',
92-
function: 'functions',
93-
};
94-
95-
if (pluralMappings[resource]) {
96-
return pluralMappings[resource];
97-
}
98-
99-
if (!resource.endsWith('s')) {
100-
return `${resource}s`;
101-
}
102-
103-
return resource;
104-
}
105-
106-
function buildApiPath(
107-
metadata: ToolMetadata,
108-
tenantId: string,
109-
projectId: string,
110-
entityId: string,
111-
additionalParams?: { agentId?: string }
112-
): string {
113-
const { resource } = metadata;
114-
115-
const pathMappings: Record<
116-
string,
117-
(tid: string, pid: string, eid: string, params?: any) => string
118-
> = {
119-
projects: (tid, _pid, eid) => `tenants/${tid}/projects/${eid}`,
120-
project: (tid, _pid, eid) => `tenants/${tid}/projects/${eid}`,
121-
agent: (tid, pid, eid) => `tenants/${tid}/projects/${pid}/agent/${eid}`,
122-
'sub-agent': (tid, pid, eid, params) => {
123-
if (!params?.agentId) {
124-
console.warn('sub-agent path requires agentId but none provided');
125-
return `tenants/${tid}/projects/${pid}/agents/MISSING_AGENT_ID/sub-agents/${eid}`;
126-
}
127-
return `tenants/${tid}/projects/${pid}/agents/${params.agentId}/sub-agents/${eid}`;
128-
},
129-
};
130-
131-
if (pathMappings[resource]) {
132-
return pathMappings[resource](tenantId, projectId, entityId, additionalParams);
133-
}
134-
135-
const pluralEntity = pluralizeResource(resource);
136-
137-
if (resource.includes('-relations')) {
138-
return `tenants/${tenantId}/projects/${projectId}/${pluralEntity}/${entityId}`;
139-
}
140-
141-
return `tenants/${tenantId}/projects/${projectId}/${pluralEntity}/${entityId}`;
142-
}
143-
144-
async function fetchCurrentEntityState(
145-
params: GetEntityParams
146-
): Promise<Record<string, any> | null> {
147-
const { toolName, input, tenantId, projectId } = params;
148-
149-
if (
150-
!toolName.includes('update') &&
151-
!toolName.includes('create') &&
152-
!toolName.includes('delete')
153-
) {
154-
return null;
155-
}
156-
157-
const metadata = parseToolName(toolName);
158-
159-
if (metadata.action === 'create') {
160-
return {};
161-
}
162-
163-
const entityId = extractEntityId(input, metadata);
164-
165-
if (!entityId) {
166-
console.warn(`Could not extract entity ID from input for tool: ${toolName}`);
167-
return null;
168-
}
169-
170-
try {
171-
// Extract agentId for sub-agents (they're nested under agents)
172-
const request = input.request || input;
173-
const agentId = request.agentId;
174-
175-
const apiPath = buildApiPath(metadata, tenantId, projectId, entityId, { agentId });
176-
const response = await makeManagementApiRequest<any>(apiPath, {
177-
method: 'GET',
178-
});
179-
180-
const currentState = response.data || response;
181-
return currentState;
182-
} catch (error) {
183-
console.error(`Failed to fetch current state for ${toolName}:`, error);
184-
return null;
185-
}
186-
}
187-
188-
function extractFieldsToUpdate(input: Record<string, any>): Record<string, any> {
189-
const request = input.request || input;
190-
return request.body || {};
191-
}
192-
193-
function computeDiff(
194-
currentState: Record<string, any> | null,
195-
newValues: Record<string, any>
196-
): Array<{ field: string; oldValue: any; newValue: any }> {
197-
const diffs: Array<{ field: string; oldValue: any; newValue: any }> = [];
198-
199-
for (const [field, newValue] of Object.entries(newValues)) {
200-
const oldValue = currentState?.[field] ?? '';
201-
202-
if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
203-
diffs.push({
204-
field,
205-
oldValue,
206-
newValue,
207-
});
208-
}
209-
}
210-
211-
return diffs;
212-
}
213-
21429
export async function fetchToolApprovalDiff(
21530
params: FetchToolApprovalDiffParams
21631
): Promise<ActionResult<FieldDiff[]> & { entityData?: Record<string, any> }> {
@@ -248,23 +63,3 @@ export async function fetchToolApprovalDiff(
24863
};
24964
}
25065
}
251-
252-
/**
253-
* ⚠️ TESTING EXPORTS ONLY
254-
*
255-
* These functions are exported for testing purposes only.
256-
* Do NOT import these directly from client components - they will still execute server-side,
257-
* but importing them from client code breaks the architectural boundary.
258-
*
259-
* Client components should only import: fetchToolApprovalDiff()
260-
*/
261-
export {
262-
fetchCurrentEntityState,
263-
extractFieldsToUpdate,
264-
computeDiff,
265-
parseToolName,
266-
extractEntityId,
267-
buildApiPath,
268-
pluralizeResource,
269-
};
270-
export type { ToolMetadata, GetEntityParams, FieldDiff };

0 commit comments

Comments
 (0)