@membranehq/cli 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -4
- package/.turbo/turbo-build.log +0 -9
- package/CHANGELOG.md +0 -7
- package/scripts/add-shebang.sh +0 -6
- package/scripts/prepare-package-json.ts +0 -29
- package/src/agent.tsx +0 -50
- package/src/cli.ts +0 -72
- package/src/commands/open.command.ts +0 -51
- package/src/commands/pull.command.ts +0 -75
- package/src/commands/push.command.ts +0 -79
- package/src/commands/test.command.ts +0 -99
- package/src/components/AddMcpServerScreen.tsx +0 -215
- package/src/components/AgentStatus.tsx +0 -15
- package/src/components/Main.tsx +0 -64
- package/src/components/OverviewSection.tsx +0 -24
- package/src/components/PersonalAccessTokenInput.tsx +0 -56
- package/src/components/RecentChanges.tsx +0 -65
- package/src/components/SelectWorkspace.tsx +0 -112
- package/src/components/Setup.tsx +0 -121
- package/src/components/WorkspaceStatus.tsx +0 -61
- package/src/contexts/FileWatcherContext.tsx +0 -81
- package/src/index.ts +0 -27
- package/src/legacy/commands/pullWorkspace.ts +0 -70
- package/src/legacy/commands/pushWorkspace.ts +0 -246
- package/src/legacy/integrationElements.ts +0 -78
- package/src/legacy/push/types.ts +0 -17
- package/src/legacy/reader/index.ts +0 -113
- package/src/legacy/types.ts +0 -17
- package/src/legacy/util.ts +0 -149
- package/src/legacy/workspace-elements/connectors.ts +0 -397
- package/src/legacy/workspace-elements/index.ts +0 -27
- package/src/legacy/workspace-tools/commands/pullWorkspace.ts +0 -70
- package/src/legacy/workspace-tools/integrationElements.ts +0 -78
- package/src/legacy/workspace-tools/util.ts +0 -149
- package/src/mcp/server-status.ts +0 -27
- package/src/mcp/server.ts +0 -36
- package/src/mcp/tools/getTestAccessToken.ts +0 -32
- package/src/modules/api/account-api-client.ts +0 -89
- package/src/modules/api/index.ts +0 -3
- package/src/modules/api/membrane-api-client.ts +0 -116
- package/src/modules/api/workspace-api-client.ts +0 -11
- package/src/modules/config/cwd-context.tsx +0 -11
- package/src/modules/config/project/getAgentVersion.ts +0 -16
- package/src/modules/config/project/index.ts +0 -8
- package/src/modules/config/project/paths.ts +0 -25
- package/src/modules/config/project/readProjectConfig.ts +0 -27
- package/src/modules/config/project/useProjectConfig.tsx +0 -103
- package/src/modules/config/system/index.ts +0 -35
- package/src/modules/file-watcher/index.ts +0 -166
- package/src/modules/file-watcher/types.ts +0 -14
- package/src/modules/setup/steps.ts +0 -9
- package/src/modules/setup/useSetup.ts +0 -16
- package/src/modules/status/useStatus.ts +0 -16
- package/src/modules/workspace-element-service/constants.ts +0 -121
- package/src/modules/workspace-element-service/getTypeAndKeyFromPath.ts +0 -69
- package/src/modules/workspace-element-service/index.ts +0 -304
- package/src/testing/environment.ts +0 -172
- package/src/testing/runners/base.runner.ts +0 -27
- package/src/testing/runners/test.runner.ts +0 -123
- package/src/testing/scripts/generate-test-report.ts +0 -757
- package/src/testing/test-suites/base.ts +0 -92
- package/src/testing/test-suites/data-collection.ts +0 -128
- package/src/testing/testers/base.ts +0 -115
- package/src/testing/testers/create.ts +0 -273
- package/src/testing/testers/delete.ts +0 -155
- package/src/testing/testers/find-by-id.ts +0 -135
- package/src/testing/testers/list.ts +0 -110
- package/src/testing/testers/match.ts +0 -149
- package/src/testing/testers/search.ts +0 -148
- package/src/testing/testers/spec.ts +0 -30
- package/src/testing/testers/update.ts +0 -284
- package/src/utils/auth.ts +0 -19
- package/src/utils/constants.ts +0 -27
- package/src/utils/fields.ts +0 -83
- package/src/utils/logger.ts +0 -106
- package/src/utils/templating.ts +0 -50
- package/tsconfig.json +0 -21
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { WorkspaceElementType } from '@integration-app/sdk'
|
|
2
|
-
|
|
3
|
-
export const INTEGRATION_ELEMENTS = {
|
|
4
|
-
[WorkspaceElementType.Integration]: {
|
|
5
|
-
element: 'integration',
|
|
6
|
-
elements: 'integrations',
|
|
7
|
-
exportable: false,
|
|
8
|
-
exportCleanup: (el) => {
|
|
9
|
-
return {
|
|
10
|
-
id: el.id,
|
|
11
|
-
key: el.key,
|
|
12
|
-
name: el.name,
|
|
13
|
-
connectorId: el.connectorId,
|
|
14
|
-
baseUri: el.baseUri,
|
|
15
|
-
connectorVersion: el.connectorVersion,
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
[WorkspaceElementType.Connector]: {
|
|
20
|
-
element: 'connector',
|
|
21
|
-
elements: 'connectors',
|
|
22
|
-
exportable: false,
|
|
23
|
-
},
|
|
24
|
-
[WorkspaceElementType.Action]: {
|
|
25
|
-
element: 'action',
|
|
26
|
-
elements: 'actions',
|
|
27
|
-
integrationSpecific: true,
|
|
28
|
-
exportCleanup: (el) => {
|
|
29
|
-
delete el.integration
|
|
30
|
-
return el
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
[WorkspaceElementType.AppDataSchema]: {
|
|
34
|
-
element: 'appDataSchema',
|
|
35
|
-
elements: 'appDataSchemas',
|
|
36
|
-
},
|
|
37
|
-
[WorkspaceElementType.AppEventType]: {
|
|
38
|
-
element: 'appEventType',
|
|
39
|
-
elements: 'appEventTypes',
|
|
40
|
-
},
|
|
41
|
-
[WorkspaceElementType.DataLinkTable]: {
|
|
42
|
-
element: 'dataLinkTable',
|
|
43
|
-
elements: 'dataLinkTables',
|
|
44
|
-
},
|
|
45
|
-
[WorkspaceElementType.DataSource]: {
|
|
46
|
-
element: 'dataSource',
|
|
47
|
-
elements: 'dataSources',
|
|
48
|
-
integrationSpecific: true,
|
|
49
|
-
},
|
|
50
|
-
[WorkspaceElementType.FieldMapping]: {
|
|
51
|
-
element: 'fieldMapping',
|
|
52
|
-
elements: 'fieldMappings',
|
|
53
|
-
integrationSpecific: true,
|
|
54
|
-
exportCleanup: (fieldMapping) => {
|
|
55
|
-
delete fieldMapping.dataSourceId
|
|
56
|
-
return fieldMapping
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
[WorkspaceElementType.Flow]: {
|
|
60
|
-
element: 'flow',
|
|
61
|
-
elements: 'flows',
|
|
62
|
-
integrationSpecific: true,
|
|
63
|
-
},
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function baseExportCleanup(element) {
|
|
67
|
-
delete element.workspaceId
|
|
68
|
-
delete element.createdAt
|
|
69
|
-
delete element.updatedAt
|
|
70
|
-
delete element.revision
|
|
71
|
-
delete element.parentRevision
|
|
72
|
-
Object.keys(element).map((key) => {
|
|
73
|
-
if (key.match(/universal.*Revision/g)) {
|
|
74
|
-
delete element[key]
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
return element
|
|
78
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { INTEGRATION_ELEMENTS, baseExportCleanup } from './integrationElements'
|
|
2
|
-
import { Logger } from '../../utils/logger'
|
|
3
|
-
import { WorkspaceData } from '../types'
|
|
4
|
-
|
|
5
|
-
async function getWorkspaceData(iApp, logs = 'full'): Promise<WorkspaceData> {
|
|
6
|
-
const workspaceData: any = {}
|
|
7
|
-
|
|
8
|
-
Logger.info('Getting remote workspace data')
|
|
9
|
-
|
|
10
|
-
const integrations = await iApp.integrations.findAll()
|
|
11
|
-
workspaceData.integration = integrations.map((item) => cleanUpForExport('integration', item))
|
|
12
|
-
|
|
13
|
-
for (const elementType of Object.keys(INTEGRATION_ELEMENTS) as any[]) {
|
|
14
|
-
const element = INTEGRATION_ELEMENTS[elementType]
|
|
15
|
-
if (element.exportable === false) continue
|
|
16
|
-
|
|
17
|
-
const elementEnteties = []
|
|
18
|
-
Logger.info(`Reading ${element.elements} from remote workspace`)
|
|
19
|
-
// Get all universal elements
|
|
20
|
-
const elements = await iApp[element.elements].findAll()
|
|
21
|
-
|
|
22
|
-
elementEnteties.push(
|
|
23
|
-
...elements.map((item) => {
|
|
24
|
-
return cleanUpForExport(elementType, item)
|
|
25
|
-
}),
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
// Get all integration specific elements
|
|
29
|
-
if (element.integrationSpecific) {
|
|
30
|
-
for (const integration of integrations) {
|
|
31
|
-
const integrationElements = await iApp[element.elements].findAll({
|
|
32
|
-
integrationId: integration.id,
|
|
33
|
-
})
|
|
34
|
-
elementEnteties.push(
|
|
35
|
-
...integrationElements.map((item) => {
|
|
36
|
-
item.integrationKey = integration.key
|
|
37
|
-
return cleanUpForExport(elementType, item)
|
|
38
|
-
}),
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (elementEnteties.length > 0) {
|
|
44
|
-
if (logs == 'full') {
|
|
45
|
-
// eslint-disable-next-line no-console
|
|
46
|
-
console.table(
|
|
47
|
-
elementEnteties
|
|
48
|
-
.map((item) => {
|
|
49
|
-
return {
|
|
50
|
-
key: item.key,
|
|
51
|
-
integrationKey: item.integrationKey,
|
|
52
|
-
universal: !item.integrationKey,
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
|
-
.reduce((acc, item) => {
|
|
56
|
-
if (!acc) {
|
|
57
|
-
acc = {}
|
|
58
|
-
}
|
|
59
|
-
if (!acc[item.key]) {
|
|
60
|
-
acc[item.key] = {}
|
|
61
|
-
}
|
|
62
|
-
if (item.universal) {
|
|
63
|
-
acc[item.key].universal = true
|
|
64
|
-
}
|
|
65
|
-
if (item.integrationKey) {
|
|
66
|
-
if (!acc[item.key].integration) {
|
|
67
|
-
acc[item.key].integration = item.integrationKey
|
|
68
|
-
} else {
|
|
69
|
-
acc[item.key].integration += ` ${item.integrationKey}`
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return acc
|
|
73
|
-
}, {}),
|
|
74
|
-
)
|
|
75
|
-
// eslint-disable-next-line no-console
|
|
76
|
-
console.groupEnd()
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
workspaceData[elementType] = elementEnteties
|
|
80
|
-
}
|
|
81
|
-
Logger.success('Remote workspace data retrieved successfully')
|
|
82
|
-
return workspaceData
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function splitWorkspaceData(data: WorkspaceData) {
|
|
86
|
-
const universalElements: any = {}
|
|
87
|
-
const integrationSpecificElements: any = {}
|
|
88
|
-
for (const elementType of Object.keys(data)) {
|
|
89
|
-
universalElements[elementType] = []
|
|
90
|
-
integrationSpecificElements[elementType] = []
|
|
91
|
-
|
|
92
|
-
const isIntegrationSpecific = INTEGRATION_ELEMENTS[elementType]?.integrationSpecific
|
|
93
|
-
|
|
94
|
-
if (isIntegrationSpecific) {
|
|
95
|
-
for (const element of data[elementType]) {
|
|
96
|
-
if (element.integrationKey || element.integration || element.integrationId) {
|
|
97
|
-
integrationSpecificElements[elementType].push(element)
|
|
98
|
-
} else {
|
|
99
|
-
universalElements[elementType].push(element)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
universalElements[elementType].push(...data[elementType])
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return { universalElements, integrationSpecificElements }
|
|
107
|
-
}
|
|
108
|
-
function cleanUpForExport(elementType, element) {
|
|
109
|
-
element = baseExportCleanup(element)
|
|
110
|
-
return INTEGRATION_ELEMENTS[elementType].exportCleanup
|
|
111
|
-
? INTEGRATION_ELEMENTS[elementType].exportCleanup(element)
|
|
112
|
-
: element
|
|
113
|
-
}
|
|
114
|
-
function hasParent(element) {
|
|
115
|
-
return Object.keys(element).some((key) => /universal.*Id/g.test(key) || /parentId/g.test(key))
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function coloredLog(message, color) {
|
|
119
|
-
const colors = {
|
|
120
|
-
Reset: '\x1b[0m',
|
|
121
|
-
Bright: '\x1b[1m',
|
|
122
|
-
Dim: '\x1b[2m',
|
|
123
|
-
Underscore: '\x1b[4m',
|
|
124
|
-
Blink: '\x1b[5m',
|
|
125
|
-
Reverse: '\x1b[7m',
|
|
126
|
-
Hidden: '\x1b[8m',
|
|
127
|
-
Black: '\x1b[30m',
|
|
128
|
-
Red: '\x1b[31m',
|
|
129
|
-
Green: '\x1b[32m',
|
|
130
|
-
Yellow: '\x1b[33m',
|
|
131
|
-
Blue: '\x1b[34m',
|
|
132
|
-
Magenta: '\x1b[35m',
|
|
133
|
-
Cyan: '\x1b[36m',
|
|
134
|
-
White: '\x1b[37m',
|
|
135
|
-
Gray: '\x1b[90m',
|
|
136
|
-
BgBlack: '\x1b[40m',
|
|
137
|
-
BgRed: '\x1b[41m',
|
|
138
|
-
BgGreen: '\x1b[42m',
|
|
139
|
-
BgYellow: '\x1b[43m',
|
|
140
|
-
BgBlue: '\x1b[44m',
|
|
141
|
-
BgMagenta: '\x1b[45m',
|
|
142
|
-
BgCyan: '\x1b[46m',
|
|
143
|
-
BgWhite: '\x1b[47m',
|
|
144
|
-
BgGray: '\x1b[100m',
|
|
145
|
-
}
|
|
146
|
-
console.debug('\x1b[0m', `\x1b[${colors[color]}`, message, '\x1b[0m')
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export { getWorkspaceData, splitWorkspaceData, hasParent, coloredLog }
|
package/src/mcp/server-status.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
type McpServerStatus = 'starting' | 'running' | 'error'
|
|
2
|
-
|
|
3
|
-
let status: McpServerStatus = 'starting'
|
|
4
|
-
let url = ''
|
|
5
|
-
const listeners = new Set<(status: McpServerStatus) => void>()
|
|
6
|
-
|
|
7
|
-
export function getMcpServerStatus(): McpServerStatus {
|
|
8
|
-
return status
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function setMcpServerStatus(newStatus: McpServerStatus): void {
|
|
12
|
-
status = newStatus
|
|
13
|
-
listeners.forEach((cb) => cb(status))
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function subscribeMcpServerStatus(cb: (status: McpServerStatus) => void): () => void {
|
|
17
|
-
listeners.add(cb)
|
|
18
|
-
return () => listeners.delete(cb)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function getMcpServerUrl(): string {
|
|
22
|
-
return url
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function setMcpServerUrl(newUrl: string): void {
|
|
26
|
-
url = newUrl
|
|
27
|
-
}
|
package/src/mcp/server.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { FastMCP } from 'fastmcp'
|
|
2
|
-
|
|
3
|
-
import { setMcpServerStatus, setMcpServerUrl } from './server-status'
|
|
4
|
-
import { getTestAccessTokenTool } from './tools/getTestAccessToken'
|
|
5
|
-
import { getAgentVersion } from '../modules/config/project/getAgentVersion'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Starts the FastMCP server with HTTP streaming transport.
|
|
9
|
-
* Exposes a simple 'hello' tool.
|
|
10
|
-
*/
|
|
11
|
-
export async function startMcpServer(): Promise<void> {
|
|
12
|
-
setMcpServerStatus('starting')
|
|
13
|
-
const mcpServer = new FastMCP({
|
|
14
|
-
name: 'Membrane',
|
|
15
|
-
instructions: `Use this server for anything related to building software integrations.
|
|
16
|
-
It provides tools for working with Membrane - a Universal Integration Layer for your product.
|
|
17
|
-
Full documentation (fetch it when building integrations) is here: https://docs.integration.app/llms.txt`,
|
|
18
|
-
version: getAgentVersion(),
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
// Register tools
|
|
22
|
-
mcpServer.addTool(getTestAccessTokenTool())
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
await mcpServer.start({
|
|
26
|
-
transportType: 'httpStream',
|
|
27
|
-
httpStream: {
|
|
28
|
-
port: 8001,
|
|
29
|
-
},
|
|
30
|
-
})
|
|
31
|
-
setMcpServerUrl('http://localhost:8001/mcp')
|
|
32
|
-
setMcpServerStatus('running')
|
|
33
|
-
} catch {
|
|
34
|
-
setMcpServerStatus('error')
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import jwt from 'jsonwebtoken'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
|
|
4
|
-
import { CONFIG_FILE_NAME, readProjectConfig } from '../../modules/config/project/readProjectConfig'
|
|
5
|
-
|
|
6
|
-
export function getTestAccessTokenTool() {
|
|
7
|
-
return {
|
|
8
|
-
name: 'getTestAccessToken',
|
|
9
|
-
description:
|
|
10
|
-
'Returns a JWT access token for your test customer in Membrane. Use this whenever you want to test any integration functionality. The token is used as bearer token in any API requests to Membrane or for initializing its SDK.',
|
|
11
|
-
parameters: z.object({}),
|
|
12
|
-
async execute() {
|
|
13
|
-
const config = readProjectConfig()
|
|
14
|
-
|
|
15
|
-
if (!config.workspaceKey || !config.workspaceSecret) {
|
|
16
|
-
throw new Error(`workspaceKey or workspaceSecret not found in ${CONFIG_FILE_NAME}`)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Generate JWT as per docs
|
|
20
|
-
const tokenData = {
|
|
21
|
-
id: config.testCustomerId || 'test-customer',
|
|
22
|
-
}
|
|
23
|
-
const options = {
|
|
24
|
-
issuer: config.workspaceKey,
|
|
25
|
-
expiresIn: 7200,
|
|
26
|
-
algorithm: 'HS512' as const,
|
|
27
|
-
}
|
|
28
|
-
const token = jwt.sign(tokenData, config.workspaceSecret, options)
|
|
29
|
-
return token
|
|
30
|
-
},
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { getPat } from '../config/system'
|
|
2
|
-
|
|
3
|
-
const DEFAULT_API_BASE_URL = 'https://api.integration.app'
|
|
4
|
-
|
|
5
|
-
export class AccountApiClient {
|
|
6
|
-
constructor(private readonly apiBaseUrl: string = DEFAULT_API_BASE_URL) {}
|
|
7
|
-
|
|
8
|
-
public async request<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|
9
|
-
// Start with headers from options, or new Headers
|
|
10
|
-
const headers = options.headers
|
|
11
|
-
? options.headers instanceof Headers
|
|
12
|
-
? new Headers(options.headers)
|
|
13
|
-
: new Headers(options.headers as Record<string, string>)
|
|
14
|
-
: new Headers()
|
|
15
|
-
|
|
16
|
-
// Set Authorization if not already set
|
|
17
|
-
if (!headers.has('Authorization')) {
|
|
18
|
-
const pat = getPat()
|
|
19
|
-
if (!pat) {
|
|
20
|
-
throw new Error('Personal Access Token not found.')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
headers.set('Authorization', `Bearer ${pat}`)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Set Content-Type if body is present and not already set
|
|
27
|
-
if (options.body && !headers.has('Content-Type')) {
|
|
28
|
-
headers.set('Content-Type', 'application/json')
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const response = await fetch(`${this.apiBaseUrl}${path}`, {
|
|
32
|
-
...options,
|
|
33
|
-
headers,
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
if (!response.ok) {
|
|
37
|
-
const errorBody = await response.text()
|
|
38
|
-
throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorBody}`)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (response.status === 204) {
|
|
42
|
-
return {} as T
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const responseText = await response.text()
|
|
46
|
-
|
|
47
|
-
return JSON.parse(responseText)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public get<T>(path: string): Promise<T> {
|
|
51
|
-
return this.request<T>(path, { method: 'GET' })
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public post<T>(path: string, body: unknown): Promise<T> {
|
|
55
|
-
return this.request<T>(path, {
|
|
56
|
-
method: 'POST',
|
|
57
|
-
body: JSON.stringify(body),
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public put<T>(path: string, body: unknown): Promise<T> {
|
|
62
|
-
return this.request<T>(path, {
|
|
63
|
-
method: 'PUT',
|
|
64
|
-
body: JSON.stringify(body),
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public patch<T>(path: string, body: unknown): Promise<T> {
|
|
69
|
-
return this.request<T>(path, {
|
|
70
|
-
method: 'PATCH',
|
|
71
|
-
body: JSON.stringify(body),
|
|
72
|
-
})
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
public delete<T>(path: string): Promise<T> {
|
|
76
|
-
return this.request<T>(path, { method: 'DELETE' })
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const apiClient = new AccountApiClient()
|
|
81
|
-
|
|
82
|
-
// Legacy export for backward compatibility
|
|
83
|
-
export const accountApiFetcher = (path: string) => apiClient.get(path)
|
|
84
|
-
|
|
85
|
-
// New centralized fetcher that can be configured with project settings
|
|
86
|
-
export const createAccountApiFetcher = (apiUri?: string) => {
|
|
87
|
-
const client = new AccountApiClient(apiUri || DEFAULT_API_BASE_URL)
|
|
88
|
-
return (path: string) => client.get(path)
|
|
89
|
-
}
|
package/src/modules/api/index.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { IntegrationAppClient } from '@integration-app/sdk'
|
|
2
|
-
import jwt from 'jsonwebtoken'
|
|
3
|
-
|
|
4
|
-
import { readProjectConfig } from '../config/project/readProjectConfig'
|
|
5
|
-
|
|
6
|
-
interface ClientConfig {
|
|
7
|
-
workspaceKey: string
|
|
8
|
-
workspaceSecret: string
|
|
9
|
-
apiUri?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class MembraneClient {
|
|
13
|
-
private client: IntegrationAppClient | null = null
|
|
14
|
-
private currentConfig: ClientConfig | null = null
|
|
15
|
-
private tokenExpiry: number = 0
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Generates an access token for the workspace
|
|
19
|
-
*/
|
|
20
|
-
private async generateAccessToken(key: string, secret: string): Promise<string> {
|
|
21
|
-
return jwt.sign(
|
|
22
|
-
{
|
|
23
|
-
name: 'Membrane Agent',
|
|
24
|
-
isAdmin: true,
|
|
25
|
-
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
26
|
-
iss: key,
|
|
27
|
-
},
|
|
28
|
-
secret,
|
|
29
|
-
{ algorithm: 'HS512' },
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Checks if the current token is still valid
|
|
35
|
-
*/
|
|
36
|
-
private isTokenValid(): boolean {
|
|
37
|
-
return Date.now() < this.tokenExpiry
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Gets the current workspace configuration
|
|
42
|
-
*/
|
|
43
|
-
private getCurrentConfig(cwd: string): ClientConfig | null {
|
|
44
|
-
const config = readProjectConfig(cwd)
|
|
45
|
-
|
|
46
|
-
if (!config?.workspaceKey || !config?.workspaceSecret) {
|
|
47
|
-
return null
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
workspaceKey: config.workspaceKey,
|
|
52
|
-
workspaceSecret: config.workspaceSecret,
|
|
53
|
-
apiUri: config.apiUri,
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Creates or updates the client with new configuration
|
|
59
|
-
*/
|
|
60
|
-
private async createClient(config: ClientConfig): Promise<IntegrationAppClient> {
|
|
61
|
-
const token = await this.generateAccessToken(config.workspaceKey, config.workspaceSecret)
|
|
62
|
-
|
|
63
|
-
this.tokenExpiry = Date.now() + 3600000 // 1 hour from now
|
|
64
|
-
this.currentConfig = config
|
|
65
|
-
|
|
66
|
-
return new IntegrationAppClient({
|
|
67
|
-
token,
|
|
68
|
-
apiUri: config.apiUri,
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Gets the IntegrationAppClient instance, creating or updating it as needed
|
|
74
|
-
*/
|
|
75
|
-
async getClient(cwd: string): Promise<IntegrationAppClient> {
|
|
76
|
-
const config = this.getCurrentConfig(cwd)
|
|
77
|
-
|
|
78
|
-
if (!config) {
|
|
79
|
-
throw new Error('Unable to create IntegrationAppClient: No workspace configuration found.')
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Check if we need to create a new client or update existing one
|
|
83
|
-
const configChanged =
|
|
84
|
-
!this.currentConfig ||
|
|
85
|
-
this.currentConfig.workspaceKey !== config.workspaceKey ||
|
|
86
|
-
this.currentConfig.workspaceSecret !== config.workspaceSecret ||
|
|
87
|
-
this.currentConfig.apiUri !== config.apiUri
|
|
88
|
-
|
|
89
|
-
// Check if token is expired or will expire soon (within 5 minutes)
|
|
90
|
-
const tokenExpired = !this.isTokenValid() || this.tokenExpiry - Date.now() < 300000
|
|
91
|
-
|
|
92
|
-
if (!this.client || configChanged || tokenExpired) {
|
|
93
|
-
this.client = await this.createClient(config)
|
|
94
|
-
}
|
|
95
|
-
return this.client
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Clears the current client (useful for testing or when config is invalid)
|
|
100
|
-
*/
|
|
101
|
-
clearClient(): void {
|
|
102
|
-
this.client = null
|
|
103
|
-
this.currentConfig = null
|
|
104
|
-
this.tokenExpiry = 0
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Gets the current configuration (for debugging)
|
|
109
|
-
*/
|
|
110
|
-
getCurrentConfigInfo(): ClientConfig | null {
|
|
111
|
-
return this.currentConfig
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Export singleton instance
|
|
116
|
-
export const membraneClient = new MembraneClient()
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { membraneClient } from './membrane-api-client'
|
|
2
|
-
|
|
3
|
-
export async function getWorkspaceId(cwd: string): Promise<string> {
|
|
4
|
-
const client = await membraneClient.getClient(cwd)
|
|
5
|
-
if (!client) {
|
|
6
|
-
throw new Error('No workspace configuration found')
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const response = await client.get('org-workspace-id')
|
|
10
|
-
return response.id
|
|
11
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useContext } from 'react'
|
|
2
|
-
|
|
3
|
-
const CwdContext = createContext<string>(process.cwd())
|
|
4
|
-
|
|
5
|
-
export function CwdProvider({ cwd, children }: { cwd: string; children: React.ReactNode }) {
|
|
6
|
-
return <CwdContext.Provider value={cwd}>{children}</CwdContext.Provider>
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function useCwd(): string {
|
|
10
|
-
return useContext(CwdContext)
|
|
11
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs'
|
|
2
|
-
import * as path from 'node:path'
|
|
3
|
-
|
|
4
|
-
export function getAgentVersion(): `${number}.${number}.${number}` {
|
|
5
|
-
try {
|
|
6
|
-
const pkgPath = path.join(__dirname, '../../../package.json')
|
|
7
|
-
const pkgRaw = fs.readFileSync(pkgPath, 'utf8')
|
|
8
|
-
const pkg = JSON.parse(pkgRaw)
|
|
9
|
-
if (typeof pkg.version === 'string' && /^\d+\.\d+\.\d+$/.test(pkg.version)) {
|
|
10
|
-
return pkg.version as `${number}.${number}.${number}`
|
|
11
|
-
}
|
|
12
|
-
return '0.0.0'
|
|
13
|
-
} catch {
|
|
14
|
-
return '0.0.0'
|
|
15
|
-
}
|
|
16
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import * as path from 'node:path'
|
|
2
|
-
|
|
3
|
-
const MEMBRANE_DIR_NAME = 'membrane'
|
|
4
|
-
|
|
5
|
-
export const IGNORED_FILES = [
|
|
6
|
-
'**/node_modules/**',
|
|
7
|
-
'**/.git/**',
|
|
8
|
-
'**/.DS_Store',
|
|
9
|
-
'**/Thumbs.db',
|
|
10
|
-
'**/*.tmp',
|
|
11
|
-
'**/*.swp',
|
|
12
|
-
'**/*.swo',
|
|
13
|
-
'**/*.log',
|
|
14
|
-
'**/*.lock',
|
|
15
|
-
'**/dist/**',
|
|
16
|
-
'**/build/**',
|
|
17
|
-
'**/.cache/**',
|
|
18
|
-
'**/.next/**',
|
|
19
|
-
'**/.vscode/**',
|
|
20
|
-
'**/.idea/**',
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
export function getMembraneDir(cwd: string): string {
|
|
24
|
-
return path.join(cwd, MEMBRANE_DIR_NAME)
|
|
25
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs'
|
|
2
|
-
import * as path from 'node:path'
|
|
3
|
-
|
|
4
|
-
import YAML from 'js-yaml'
|
|
5
|
-
|
|
6
|
-
import { configSchema, type ProjectConfig } from './useProjectConfig'
|
|
7
|
-
|
|
8
|
-
export const CONFIG_FILE_NAME = 'membrane.config.yml'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Reads and parses membrane.config.yml from the given cwd (or process.cwd() by default).
|
|
12
|
-
* Returns the validated ProjectConfig or null if not found/invalid.
|
|
13
|
-
*/
|
|
14
|
-
export function readProjectConfig(cwd?: string): ProjectConfig | null {
|
|
15
|
-
const configPath = path.join(cwd ?? process.cwd(), CONFIG_FILE_NAME)
|
|
16
|
-
try {
|
|
17
|
-
const configFile = fs.readFileSync(configPath, 'utf8')
|
|
18
|
-
const parsedConfig = YAML.load(configFile)
|
|
19
|
-
const validation = configSchema.safeParse(parsedConfig)
|
|
20
|
-
if (validation.success) {
|
|
21
|
-
return validation.data
|
|
22
|
-
}
|
|
23
|
-
return null
|
|
24
|
-
} catch (_error) {
|
|
25
|
-
return null
|
|
26
|
-
}
|
|
27
|
-
}
|