mirror of
https://github.com/twentyhq/twenty.git
synced 2026-08-03 03:08:53 -04:00
Add twenty-exa application to internal app ci (#21882)
renamed exa to twenty-exa add twenty-exa to ci check <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21882?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -29,6 +29,7 @@ runs:
|
||||
-p 2021:2021 \
|
||||
-e NODE_PORT=2021 \
|
||||
-e SERVER_URL=http://localhost:2021 \
|
||||
-e MARKETPLACE_CATALOG_SYNC_CRON_ENABLED=false \
|
||||
twentycrm/twenty-app-dev:${{ inputs.twenty-version }}
|
||||
|
||||
echo "Waiting for Twenty test instance to become healthy…"
|
||||
|
||||
2
.github/workflows/ci-internal-apps.yaml
vendored
2
.github/workflows/ci-internal-apps.yaml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
// keep this PR small while the remaining apps are made CI-ready. Remove an
|
||||
// app from this list once its checks pass, and delete the list entirely
|
||||
// once every application is covered.
|
||||
const CI_EXCLUDED_APPLICATIONS = ['call-recording', 'exa', 'people-data-labs', 'self-hosting', 'twenty-fireflies', 'twenty-for-twenty', 'twenty-linear', 'twenty-meeting-bot', 'twenty-partners'];
|
||||
const CI_EXCLUDED_APPLICATIONS = ['call-recording', 'people-data-labs', 'self-hosting', 'twenty-fireflies', 'twenty-for-twenty', 'twenty-linear', 'twenty-meeting-bot', 'twenty-partners'];
|
||||
const eventName = process.env.EVENT_NAME;
|
||||
const changedFiles = JSON.parse(process.env.CHANGED_FILES || '[]');
|
||||
const changedApps = new Set();
|
||||
|
||||
@@ -16,18 +16,22 @@
|
||||
"twenty": "twenty",
|
||||
"lint": "oxlint -c .oxlintrc.json .",
|
||||
"lint:fix": "oxlint --fix -c .oxlintrc.json .",
|
||||
"typecheck": "tsgo --noEmit -p tsconfig.spec.json",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
"test:watch": "vitest",
|
||||
"test:unit": "vitest run --config vitest.unit.config.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"exa-js": "^2.12.1",
|
||||
"twenty-client-sdk": "2.13.0",
|
||||
"twenty-sdk": "2.13.0"
|
||||
"exa-js": "^2.12.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.7.2",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260116.1",
|
||||
"oxlint": "^0.16.0",
|
||||
"twenty-client-sdk": "^2.14.0",
|
||||
"twenty-sdk": "^2.14.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite-tsconfig-paths": "^4.2.1",
|
||||
"vitest": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,87 @@
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
|
||||
|
||||
const APP_PATH = process.cwd();
|
||||
const CONFIG_DIR = path.join(os.homedir(), '.twenty');
|
||||
|
||||
function validateEnv(): { apiUrl: string; apiKey: string } {
|
||||
const apiUrl = process.env.TWENTY_API_URL;
|
||||
const apiKey = process.env.TWENTY_API_KEY;
|
||||
|
||||
if (!apiUrl || !apiKey) {
|
||||
throw new Error(
|
||||
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
|
||||
'Start a local server: yarn twenty docker:start\n' +
|
||||
'Or set them in vitest env config.',
|
||||
);
|
||||
}
|
||||
|
||||
return { apiUrl, apiKey };
|
||||
}
|
||||
|
||||
async function checkServer(apiUrl: string) {
|
||||
let response: Response;
|
||||
|
||||
try {
|
||||
response = await fetch(`${apiUrl}/healthz`);
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Twenty server is not reachable at ${apiUrl}. ` +
|
||||
'Make sure the server is running before executing integration tests.',
|
||||
);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Server at ${apiUrl} returned ${response.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
function writeConfig(apiUrl: string, apiKey: string) {
|
||||
const payload = JSON.stringify(
|
||||
{
|
||||
remotes: {
|
||||
local: { apiUrl, apiKey, accessToken: apiKey },
|
||||
},
|
||||
defaultRemote: 'local',
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
|
||||
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
||||
fs.writeFileSync(path.join(CONFIG_DIR, 'config.test.json'), payload);
|
||||
}
|
||||
|
||||
export async function setup() {
|
||||
const { apiUrl, apiKey } = validateEnv();
|
||||
|
||||
await checkServer(apiUrl);
|
||||
|
||||
writeConfig(apiUrl, apiKey);
|
||||
|
||||
await appUninstall({ appPath: APP_PATH }).catch(() => {});
|
||||
|
||||
const result = await appDevOnce({
|
||||
appPath: APP_PATH,
|
||||
onProgress: (message: string) => console.log(`[dev] ${message}`),
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(
|
||||
`Dev sync failed: ${result.error?.message ?? 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function teardown() {
|
||||
const uninstallResult = await appUninstall({ appPath: APP_PATH });
|
||||
|
||||
if (!uninstallResult.success) {
|
||||
console.warn(
|
||||
`App uninstall failed: ${uninstallResult.error?.message ?? 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application.config';
|
||||
|
||||
describe('App installation', () => {
|
||||
it('should find the installed Exa app in the applications list', async () => {
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const result = await client.query({
|
||||
findManyApplications: {
|
||||
id: true,
|
||||
name: true,
|
||||
universalIdentifier: true,
|
||||
},
|
||||
});
|
||||
|
||||
const matchingApplication = result.findManyApplications.find(
|
||||
(application: { universalIdentifier: string }) =>
|
||||
application.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
);
|
||||
|
||||
expect(matchingApplication).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -10,7 +10,6 @@ export default defineApplication({
|
||||
displayName: 'Exa',
|
||||
description:
|
||||
'Structured web search powered by Exa. Surfaces entity-aware results (companies, people, research, news) to Twenty AI agents.',
|
||||
icon: 'IconSearch',
|
||||
logoUrl: 'public/exa-logomark.svg',
|
||||
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
serverVariables: {
|
||||
@@ -0,0 +1,194 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { type ExaWebSearchInput } from '../types/exa-web-search-input.type';
|
||||
|
||||
const { exaConstructorMock, searchAndContentsMock, chargeCreditsMock } =
|
||||
vi.hoisted(() => ({
|
||||
exaConstructorMock: vi.fn(),
|
||||
searchAndContentsMock: vi.fn(),
|
||||
chargeCreditsMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('exa-js', () => ({
|
||||
default: vi.fn(function (apiKey: string) {
|
||||
exaConstructorMock(apiKey);
|
||||
|
||||
return { searchAndContents: searchAndContentsMock };
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('twenty-sdk/billing', () => ({
|
||||
chargeCredits: chargeCreditsMock,
|
||||
}));
|
||||
|
||||
import exaWebSearch from '../exa-web-search';
|
||||
|
||||
type ExaWebSearchResult = {
|
||||
success: boolean;
|
||||
message: string;
|
||||
result?: { title: string; url: string; snippet: string }[];
|
||||
error?: string;
|
||||
};
|
||||
|
||||
const handler = exaWebSearch.config.handler as (
|
||||
parameters: ExaWebSearchInput,
|
||||
) => Promise<ExaWebSearchResult>;
|
||||
|
||||
const API_KEY = 'exa-test-key';
|
||||
|
||||
describe('exa_web_search handler', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
process.env.EXA_API_KEY = API_KEY;
|
||||
chargeCreditsMock.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should return a configuration error and skip the search when EXA_API_KEY is not set', async () => {
|
||||
delete process.env.EXA_API_KEY;
|
||||
|
||||
const result = await handler({ query: 'twenty crm' });
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.message).toBe('Exa is not configured');
|
||||
expect(result.error).toContain('EXA_API_KEY is not set');
|
||||
expect(exaConstructorMock).not.toHaveBeenCalled();
|
||||
expect(searchAndContentsMock).not.toHaveBeenCalled();
|
||||
expect(chargeCreditsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should map Exa results to title/url/snippet and report success', async () => {
|
||||
searchAndContentsMock.mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
title: 'Twenty CRM',
|
||||
url: 'https://twenty.com',
|
||||
highlights: ['Open-source CRM', 'Built with modern tech'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = await handler({ query: 'twenty crm' });
|
||||
|
||||
expect(exaConstructorMock).toHaveBeenCalledWith(API_KEY);
|
||||
expect(result).toEqual({
|
||||
success: true,
|
||||
message: 'Found 1 results for "twenty crm"',
|
||||
result: [
|
||||
{
|
||||
title: 'Twenty CRM',
|
||||
url: 'https://twenty.com',
|
||||
snippet: 'Open-source CRM\nBuilt with modern tech',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should default to DEFAULT_NUM_RESULTS and search with type "auto" and highlights when numResults is omitted', async () => {
|
||||
searchAndContentsMock.mockResolvedValue({ results: [] });
|
||||
|
||||
await handler({ query: 'acme corp', category: 'company' });
|
||||
|
||||
expect(searchAndContentsMock).toHaveBeenCalledWith('acme corp', {
|
||||
type: 'auto',
|
||||
numResults: 10,
|
||||
category: 'company',
|
||||
highlights: { numSentences: 5 },
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward an explicit numResults to Exa', async () => {
|
||||
searchAndContentsMock.mockResolvedValue({ results: [] });
|
||||
|
||||
await handler({ query: 'acme corp', numResults: 3 });
|
||||
|
||||
expect(searchAndContentsMock).toHaveBeenCalledWith(
|
||||
'acme corp',
|
||||
expect.objectContaining({ numResults: 3 }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should include the category in the success message when one is provided', async () => {
|
||||
searchAndContentsMock.mockResolvedValue({
|
||||
results: [{ title: 'A', url: 'https://a.com', highlights: ['x'] }],
|
||||
});
|
||||
|
||||
const result = await handler({ query: 'openai', category: 'company' });
|
||||
|
||||
expect(result.message).toBe('Found 1 results for "openai" (category: company)');
|
||||
});
|
||||
|
||||
it('should fall back to empty strings for a missing title and missing highlights', async () => {
|
||||
searchAndContentsMock.mockResolvedValue({
|
||||
results: [{ url: 'https://no-title.com' }],
|
||||
});
|
||||
|
||||
const result = await handler({ query: 'edge case' });
|
||||
|
||||
expect(result.result).toEqual([
|
||||
{ title: '', url: 'https://no-title.com', snippet: '' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should charge the Exa base price (7000 micro-credits) for up to DEFAULT_NUM_RESULTS results', async () => {
|
||||
searchAndContentsMock.mockResolvedValue({
|
||||
results: [{ title: 'A', url: 'https://a.com', highlights: ['x'] }],
|
||||
});
|
||||
|
||||
await handler({ query: 'pricing base' });
|
||||
|
||||
expect(chargeCreditsMock).toHaveBeenCalledWith({
|
||||
creditsUsedMicro: 7000,
|
||||
operationType: 'WEB_SEARCH',
|
||||
resourceContext: 'exa',
|
||||
});
|
||||
});
|
||||
|
||||
it('should add 1000 micro-credits per result beyond DEFAULT_NUM_RESULTS', async () => {
|
||||
searchAndContentsMock.mockResolvedValue({
|
||||
results: Array.from({ length: 12 }, (_, index) => ({
|
||||
title: `Result ${index}`,
|
||||
url: `https://example.com/${index}`,
|
||||
highlights: ['snippet'],
|
||||
})),
|
||||
});
|
||||
|
||||
await handler({ query: 'pricing extra', numResults: 12 });
|
||||
|
||||
expect(chargeCreditsMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ creditsUsedMicro: 9000 }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return a failure result and not charge credits when the Exa search throws', async () => {
|
||||
searchAndContentsMock.mockRejectedValue(new Error('Exa rate limit exceeded'));
|
||||
|
||||
const result = await handler({ query: 'boom' });
|
||||
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
message: 'Web search failed for "boom"',
|
||||
error: 'Exa rate limit exceeded',
|
||||
});
|
||||
expect(chargeCreditsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should time out and fail when Exa does not respond within the inner timeout', async () => {
|
||||
vi.useFakeTimers();
|
||||
searchAndContentsMock.mockReturnValue(new Promise<never>(() => {}));
|
||||
|
||||
const resultPromise = handler({ query: 'slow query' });
|
||||
await vi.advanceTimersByTimeAsync(25_000);
|
||||
const result = await resultPromise;
|
||||
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
message: 'Web search failed for "slow query"',
|
||||
error: 'Exa search timed out',
|
||||
});
|
||||
expect(chargeCreditsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -21,5 +21,4 @@ export default defineRole({
|
||||
canBeAssignedToApiKeys: false,
|
||||
objectPermissions: [],
|
||||
fieldPermissions: [],
|
||||
permissionFlags: [],
|
||||
});
|
||||
@@ -5,6 +5,7 @@
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": ".",
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
@@ -17,7 +18,7 @@
|
||||
"strictBindCallApply": false,
|
||||
"target": "es2020",
|
||||
"module": "esnext",
|
||||
"lib": ["es2020"],
|
||||
"lib": ["es2020", "dom"],
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
@@ -26,5 +27,16 @@
|
||||
"~/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.integration-test.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["vitest/globals", "node"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
31
packages/twenty-apps/internal/twenty-exa/vitest.config.ts
Normal file
31
packages/twenty-apps/internal/twenty-exa/vitest.config.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
|
||||
const TWENTY_API_KEY =
|
||||
process.env.TWENTY_API_KEY ??
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjQ4OTE0NDk2MDAsImp0aSI6IjIwMjAyMDIwLWY0MDEtNGQ4YS1hNzMxLTY0ZDAwN2MyN2JhZCJ9.bfQjfyN0NEtTCLE_xPyNcwonDzlSXFoP8kdCQTdnuDc';
|
||||
|
||||
// Make env vars available to globalSetup (test.env only applies to workers)
|
||||
process.env.TWENTY_API_URL = TWENTY_API_URL;
|
||||
process.env.TWENTY_API_KEY = TWENTY_API_KEY;
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tsconfigPaths({
|
||||
projects: ['tsconfig.spec.json'],
|
||||
ignoreConfigErrors: true,
|
||||
}),
|
||||
],
|
||||
test: {
|
||||
testTimeout: 120_000,
|
||||
hookTimeout: 120_000,
|
||||
fileParallelism: false,
|
||||
include: ['src/**/*.integration-test.ts'],
|
||||
globalSetup: ['src/__tests__/global-setup.ts'],
|
||||
env: {
|
||||
TWENTY_API_URL,
|
||||
TWENTY_API_KEY,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tsconfigPaths({
|
||||
projects: ['tsconfig.spec.json'],
|
||||
ignoreConfigErrors: true,
|
||||
}),
|
||||
],
|
||||
test: {
|
||||
include: ['src/**/*.test.ts'],
|
||||
},
|
||||
});
|
||||
@@ -774,6 +774,87 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript/native-preview-darwin-arm64@npm:7.0.0-dev.20260616.1":
|
||||
version: 7.0.0-dev.20260616.1
|
||||
resolution: "@typescript/native-preview-darwin-arm64@npm:7.0.0-dev.20260616.1"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript/native-preview-darwin-x64@npm:7.0.0-dev.20260616.1":
|
||||
version: 7.0.0-dev.20260616.1
|
||||
resolution: "@typescript/native-preview-darwin-x64@npm:7.0.0-dev.20260616.1"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript/native-preview-linux-arm64@npm:7.0.0-dev.20260616.1":
|
||||
version: 7.0.0-dev.20260616.1
|
||||
resolution: "@typescript/native-preview-linux-arm64@npm:7.0.0-dev.20260616.1"
|
||||
conditions: os=linux & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript/native-preview-linux-arm@npm:7.0.0-dev.20260616.1":
|
||||
version: 7.0.0-dev.20260616.1
|
||||
resolution: "@typescript/native-preview-linux-arm@npm:7.0.0-dev.20260616.1"
|
||||
conditions: os=linux & cpu=arm
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript/native-preview-linux-x64@npm:7.0.0-dev.20260616.1":
|
||||
version: 7.0.0-dev.20260616.1
|
||||
resolution: "@typescript/native-preview-linux-x64@npm:7.0.0-dev.20260616.1"
|
||||
conditions: os=linux & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript/native-preview-win32-arm64@npm:7.0.0-dev.20260616.1":
|
||||
version: 7.0.0-dev.20260616.1
|
||||
resolution: "@typescript/native-preview-win32-arm64@npm:7.0.0-dev.20260616.1"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript/native-preview-win32-x64@npm:7.0.0-dev.20260616.1":
|
||||
version: 7.0.0-dev.20260616.1
|
||||
resolution: "@typescript/native-preview-win32-x64@npm:7.0.0-dev.20260616.1"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript/native-preview@npm:^7.0.0-dev.20260116.1":
|
||||
version: 7.0.0-dev.20260616.1
|
||||
resolution: "@typescript/native-preview@npm:7.0.0-dev.20260616.1"
|
||||
dependencies:
|
||||
"@typescript/native-preview-darwin-arm64": "npm:7.0.0-dev.20260616.1"
|
||||
"@typescript/native-preview-darwin-x64": "npm:7.0.0-dev.20260616.1"
|
||||
"@typescript/native-preview-linux-arm": "npm:7.0.0-dev.20260616.1"
|
||||
"@typescript/native-preview-linux-arm64": "npm:7.0.0-dev.20260616.1"
|
||||
"@typescript/native-preview-linux-x64": "npm:7.0.0-dev.20260616.1"
|
||||
"@typescript/native-preview-win32-arm64": "npm:7.0.0-dev.20260616.1"
|
||||
"@typescript/native-preview-win32-x64": "npm:7.0.0-dev.20260616.1"
|
||||
dependenciesMeta:
|
||||
"@typescript/native-preview-darwin-arm64":
|
||||
optional: true
|
||||
"@typescript/native-preview-darwin-x64":
|
||||
optional: true
|
||||
"@typescript/native-preview-linux-arm":
|
||||
optional: true
|
||||
"@typescript/native-preview-linux-arm64":
|
||||
optional: true
|
||||
"@typescript/native-preview-linux-x64":
|
||||
optional: true
|
||||
"@typescript/native-preview-win32-arm64":
|
||||
optional: true
|
||||
"@typescript/native-preview-win32-x64":
|
||||
optional: true
|
||||
bin:
|
||||
tsgo: bin/tsgo.js
|
||||
checksum: 10c0/e858f6a5bc41e615d3a87381ac33c938cbaa3553c47f4f1c3e06682fd96303f624ecd457542b7980a7eb29f73b54da19e8a52650246cc98a145c4a46aa743b40
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/expect@npm:4.1.8":
|
||||
version: 4.1.8
|
||||
resolution: "@vitest/expect@npm:4.1.8"
|
||||
@@ -1084,7 +1165,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"debug@npm:4":
|
||||
"debug@npm:4, debug@npm:^4.1.1":
|
||||
version: 4.4.3
|
||||
resolution: "debug@npm:4.4.3"
|
||||
dependencies:
|
||||
@@ -1473,6 +1554,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"globrex@npm:^0.1.2":
|
||||
version: 0.1.2
|
||||
resolution: "globrex@npm:0.1.2"
|
||||
checksum: 10c0/a54c029520cf58bda1d8884f72bd49b4cd74e977883268d931fd83bcbd1a9eb96d57c7dbd4ad80148fb9247467ebfb9b215630b2ed7563b2a8de02e1ff7f89d1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gopd@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "gopd@npm:1.2.0"
|
||||
@@ -2390,6 +2478,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsconfck@npm:^3.0.3":
|
||||
version: 3.1.6
|
||||
resolution: "tsconfck@npm:3.1.6"
|
||||
peerDependencies:
|
||||
typescript: ^5.0.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
bin:
|
||||
tsconfck: bin/tsconfck.js
|
||||
checksum: 10c0/269c3c513540be44844117bb9b9258fe6f8aeab026d32aeebf458d5299125f330711429dbb556dbf125a0bc25f4a81e6c24ac96de2740badd295c3fb400f66c4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:^1.9.3":
|
||||
version: 1.14.1
|
||||
resolution: "tslib@npm:1.14.1"
|
||||
@@ -2404,16 +2506,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"twenty-client-sdk@npm:2.13.0":
|
||||
version: 2.13.0
|
||||
resolution: "twenty-client-sdk@npm:2.13.0"
|
||||
"twenty-client-sdk@npm:2.14.0, twenty-client-sdk@npm:^2.14.0":
|
||||
version: 2.14.0
|
||||
resolution: "twenty-client-sdk@npm:2.14.0"
|
||||
dependencies:
|
||||
"@genql/runtime": "npm:^2.10.0"
|
||||
esbuild: "npm:^0.28.1"
|
||||
graphql: "npm:^16.8.1"
|
||||
lodash: "npm:^4.17.21"
|
||||
prettier: "npm:^3.8.3"
|
||||
checksum: 10c0/740464acec94c1d4cc5fa50ed6b190a7f1d1681963f61437a6c704835c05ffe0f5a13e254e57601a99b07bbe0f68483dee19211731853aafbcc15ec79f8039ce
|
||||
checksum: 10c0/eb222a726e21fc98f3a624f9acac7d47f0c769b989911889036665dac7c7c88698ec99beb844bf51a42e1696ea0a067bae8cf47fd751965af52a3720835242f0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2422,18 +2524,20 @@ __metadata:
|
||||
resolution: "twenty-exa@workspace:."
|
||||
dependencies:
|
||||
"@types/node": "npm:^24.7.2"
|
||||
"@typescript/native-preview": "npm:^7.0.0-dev.20260116.1"
|
||||
exa-js: "npm:^2.12.1"
|
||||
oxlint: "npm:^0.16.0"
|
||||
twenty-client-sdk: "npm:2.13.0"
|
||||
twenty-sdk: "npm:2.13.0"
|
||||
twenty-client-sdk: "npm:^2.14.0"
|
||||
twenty-sdk: "npm:^2.14.0"
|
||||
typescript: "npm:^5.9.3"
|
||||
vite-tsconfig-paths: "npm:^4.2.1"
|
||||
vitest: "npm:^4.0.0"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"twenty-sdk@npm:2.13.0":
|
||||
version: 2.13.0
|
||||
resolution: "twenty-sdk@npm:2.13.0"
|
||||
"twenty-sdk@npm:^2.14.0":
|
||||
version: 2.14.0
|
||||
resolution: "twenty-sdk@npm:2.14.0"
|
||||
dependencies:
|
||||
"@sniptt/guards": "npm:^0.2.0"
|
||||
axios: "npm:^1.16.0"
|
||||
@@ -2450,12 +2554,12 @@ __metadata:
|
||||
react: "npm:^19.2.0"
|
||||
react-dom: "npm:^19.2.0"
|
||||
tinyglobby: "npm:^0.2.15"
|
||||
twenty-client-sdk: "npm:2.13.0"
|
||||
twenty-client-sdk: "npm:2.14.0"
|
||||
typescript: "npm:^5.9.3"
|
||||
uuid: "npm:^13.0.2"
|
||||
bin:
|
||||
twenty: dist/cli.cjs
|
||||
checksum: 10c0/51c350abe5d344fcad3c961a77632fd3cb2d91e357d0562b3eb02f05f66dbc41221c5463d0f91c022316a16af03a05ad43f038d9534c0ae31c060008d0e48672
|
||||
checksum: 10c0/c4211772f8dd2ba81074a74be5f616c4c264d6bf8a174c5724be6d1c9a8cb3ca68a2674653bb58c059dae4bf1b0f43f91991e73d6a01395807206f0ac44afb08
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2532,6 +2636,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vite-tsconfig-paths@npm:^4.2.1":
|
||||
version: 4.3.2
|
||||
resolution: "vite-tsconfig-paths@npm:4.3.2"
|
||||
dependencies:
|
||||
debug: "npm:^4.1.1"
|
||||
globrex: "npm:^0.1.2"
|
||||
tsconfck: "npm:^3.0.3"
|
||||
peerDependencies:
|
||||
vite: "*"
|
||||
peerDependenciesMeta:
|
||||
vite:
|
||||
optional: true
|
||||
checksum: 10c0/f390ac1d1c3992fc5ac50f9274c1090f8b55ab34a89ea88893db9a6924a3b26c9f64bc1163615150ad100749db73b6b2cf1d57f6cd60df6e762ceb5b8ad30024
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vite@npm:^6.0.0 || ^7.0.0 || ^8.0.0":
|
||||
version: 8.0.16
|
||||
resolution: "vite@npm:8.0.16"
|
||||
@@ -76,6 +76,10 @@ export class CronRegisterAllCommand extends CommandRunner {
|
||||
this.twentyConfigService.get('SIGNING_KEY_ROTATION_DAYS'),
|
||||
);
|
||||
|
||||
const isMarketplaceCatalogSyncEnabled = this.twentyConfigService.get(
|
||||
'MARKETPLACE_CATALOG_SYNC_CRON_ENABLED',
|
||||
);
|
||||
|
||||
const allCommands = [
|
||||
{
|
||||
name: 'MessagingMessagesImport',
|
||||
@@ -156,6 +160,7 @@ export class CronRegisterAllCommand extends CommandRunner {
|
||||
{
|
||||
name: 'MarketplaceCatalogSync',
|
||||
command: this.marketplaceCatalogSyncCronCommand,
|
||||
isEnabled: isMarketplaceCatalogSyncEnabled,
|
||||
},
|
||||
{
|
||||
name: 'ApplicationVersionCheck',
|
||||
|
||||
@@ -1247,6 +1247,15 @@ export class ConfigVariables {
|
||||
@IsOptional()
|
||||
SIGNING_KEY_ROTATION_DAYS?: number;
|
||||
|
||||
@ConfigVariablesMetadata({
|
||||
group: ConfigVariablesGroup.ADVANCED_SETTINGS,
|
||||
description:
|
||||
'Register the cron job that syncs the marketplace catalog from the npm registry. Disable to stop the automatic catalog import.',
|
||||
type: ConfigVariableType.BOOLEAN,
|
||||
})
|
||||
@IsOptional()
|
||||
MARKETPLACE_CATALOG_SYNC_CRON_ENABLED = true;
|
||||
|
||||
@ConfigVariablesMetadata({
|
||||
group: ConfigVariablesGroup.RATE_LIMITING,
|
||||
description: 'Maximum number of records affected by mutations',
|
||||
|
||||
Reference in New Issue
Block a user