Skip to content

Commit 9de2ec0

Browse files
committed
fix: restore installed composio cli flow
1 parent 438c971 commit 9de2ec0

4 files changed

Lines changed: 43 additions & 38 deletions

File tree

src/components/content/DirectoryHub.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,10 @@
231231
<div v-else-if="isLoadingComposio" class="directory-loading">Loading Composio connectors...</div>
232232
<div v-else-if="!composioStatus?.available" class="directory-empty">
233233
<div class="directory-empty-copy">
234-
<p class="directory-empty-text">Composio could not be started with npx in this environment.</p>
234+
<p class="directory-empty-text">Composio CLI is not installed in this environment.</p>
235235
<div class="directory-card-actions">
236236
<button class="directory-action primary" type="button" :disabled="isInstallingComposio" @click="installComposioCli">
237-
{{ isInstallingComposio ? 'Checking...' : 'Check npx' }}
237+
{{ isInstallingComposio ? 'Installing...' : 'Install Composio' }}
238238
</button>
239239
</div>
240240
</div>

src/server/codexAppServerBridge.ts

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,7 +1159,7 @@ function readNumber(value: unknown): number {
11591159

11601160
type ComposioCliInvocation = { command: string; args: string[]; displayCommand: string }
11611161

1162-
function buildComposioInvocation(args: string[]): ComposioCliInvocation {
1162+
function buildComposioInvocation(args: string[]): ComposioCliInvocation | null {
11631163
const overrideCommand = process.env.CODEXUI_COMPOSIO_COMMAND?.trim()
11641164
if (overrideCommand) {
11651165
const invocation = getSpawnInvocation(overrideCommand, args)
@@ -1169,12 +1169,7 @@ function buildComposioInvocation(args: string[]): ComposioCliInvocation {
11691169
displayCommand: `${overrideCommand} ${args.map(quoteShellTokenIfNeeded).join(' ')}`.trim(),
11701170
}
11711171
}
1172-
const invocation = getSpawnInvocation('npx', ['--yes', 'composio', ...args])
1173-
return {
1174-
command: invocation.command,
1175-
args: invocation.args,
1176-
displayCommand: `npx --yes composio ${args.map(quoteShellTokenIfNeeded).join(' ')}`.trim(),
1177-
}
1172+
return buildInstalledComposioInvocation(args)
11781173
}
11791174

11801175
function buildInstalledComposioInvocation(args: string[]): ComposioCliInvocation | null {
@@ -1209,14 +1204,9 @@ function probeComposioInvocation(invocation: ComposioCliInvocation): { available
12091204
}
12101205

12111206
function resolveComposioInvocation(args: string[]): ComposioCliInvocation | null {
1212-
const preferred = buildComposioInvocation(args)
1213-
if (probeComposioInvocation(buildComposioInvocation(['--version'])).available) {
1214-
return preferred
1215-
}
1216-
const installed = buildInstalledComposioInvocation(args)
1217-
if (!installed) return null
1218-
const installedVersion = buildInstalledComposioInvocation(['--version'])
1219-
if (installedVersion && probeComposioInvocation(installedVersion).available) return installed
1207+
const invocation = buildComposioInvocation(args)
1208+
const versionInvocation = buildComposioInvocation(['--version'])
1209+
if (invocation && versionInvocation && probeComposioInvocation(versionInvocation).available) return invocation
12201210
return null
12211211
}
12221212

@@ -1231,7 +1221,7 @@ function parseComposioJson<T>(stdout: string, fallback: string): T {
12311221
async function runComposioJson<T>(args: string[], fallback: string): Promise<T> {
12321222
const invocation = resolveComposioInvocation(args)
12331223
if (!invocation) {
1234-
throw new Error('Composio is not available through npx or an installed CLI')
1224+
throw new Error('Composio CLI is not installed')
12351225
}
12361226
const child = spawn(invocation.command, invocation.args, {
12371227
env: process.env,
@@ -1345,13 +1335,12 @@ async function readComposioConnectionsBySlug(): Promise<Map<string, ComposioConn
13451335
}
13461336

13471337
async function readComposioStatus(): Promise<ComposioStatusResponse> {
1348-
const preferredProbe = probeComposioInvocation(buildComposioInvocation(['--version']))
1349-
const installedVersion = buildInstalledComposioInvocation(['--version'])
1350-
const installedProbe = preferredProbe.available || !installedVersion
1351-
? { available: false, cliVersion: '', output: '' }
1352-
: probeComposioInvocation(installedVersion)
1353-
const available = preferredProbe.available || installedProbe.available
1354-
const cliVersion = preferredProbe.cliVersion || installedProbe.cliVersion
1338+
const versionInvocation = buildComposioInvocation(['--version'])
1339+
const probe = versionInvocation
1340+
? probeComposioInvocation(versionInvocation)
1341+
: { available: false, cliVersion: '', output: '' }
1342+
const available = probe.available
1343+
const cliVersion = probe.cliVersion
13551344
const userData = await readComposioUserData()
13561345
if (!available) {
13571346
return {
@@ -1481,7 +1470,7 @@ async function startComposioLink(slug: string): Promise<ComposioLinkResult> {
14811470
async function startComposioLogin(): Promise<ComposioLoginResult> {
14821471
const invocation = resolveComposioInvocation(['login', '--no-browser', '-y'])
14831472
if (!invocation) {
1484-
throw new Error('Composio is not available through npx or an installed CLI')
1473+
throw new Error('Composio CLI is not installed')
14851474
}
14861475
const proc = spawn(invocation.command, invocation.args, {
14871476
cwd: process.cwd(),
@@ -1535,15 +1524,27 @@ async function startComposioLogin(): Promise<ComposioLoginResult> {
15351524
}
15361525

15371526
async function installComposioCli(): Promise<ComposioInstallResult> {
1538-
const invocation = buildComposioInvocation(['--version'])
1539-
const result = probeComposioInvocation(invocation)
1540-
if (!result.available) {
1541-
throw new Error(result.output || 'Failed to run Composio through npx')
1527+
const command = 'bash'
1528+
const installScriptUrl = 'https://composio.dev/install'
1529+
const args = ['-lc', `curl -fsSL ${installScriptUrl} | bash`]
1530+
const invocation = getSpawnInvocation(command, args)
1531+
const env = {
1532+
...process.env,
1533+
COMPOSIO_INSTALL_DIR: process.env.COMPOSIO_INSTALL_DIR?.trim() || join(homedir(), '.composio'),
1534+
}
1535+
const result = spawnSync(invocation.command, invocation.args, {
1536+
encoding: 'utf8',
1537+
env,
1538+
windowsHide: true,
1539+
})
1540+
const output = `${result.stdout ?? ''}${result.stderr ?? ''}`.trim()
1541+
if (result.error || result.status !== 0) {
1542+
throw new Error(output || result.error?.message || 'Failed to install Composio CLI')
15421543
}
15431544
return {
15441545
ok: true,
1545-
command: invocation.displayCommand,
1546-
output: result.output,
1546+
command: `curl -fsSL ${installScriptUrl} | bash`,
1547+
output,
15471548
}
15481549
}
15491550

src/style.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,10 @@
13981398
@apply border-zinc-700 bg-zinc-800 text-zinc-100;
13991399
}
14001400

1401+
:root.dark .directory-action.primary {
1402+
@apply border-zinc-100 bg-zinc-100 text-zinc-950 hover:bg-white;
1403+
}
1404+
14011405
:root.dark .directory-sort-group {
14021406
@apply border-zinc-700 bg-zinc-950;
14031407
}

tests.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,9 +3290,9 @@ The `#/skills` route shows a full Skills & Apps directory with Plugins, Apps, Co
32903290
15. Open a plugin whose detail lists a required app that is absent from the Apps catalog for the current account, such as Gmail on an account without Gmail app access, and verify the footer shows a disabled `ChatGPT Plus` action instead of `Install`
32913291
16. Switch Apps sorting to `A-Z` and verify apps reorder alphabetically; switch to `Date` and verify app-server catalog order is restored; switch back to `Popular` and verify casual-user relevant apps are prioritized and capped to 100 when no search is active
32923292
17. Search Apps and verify matching results are not capped to the Popular top 100 list
3293-
18. Switch to `Composio` and verify the workspace summary card shows the current Composio login state from the preferred `npx --yes composio` path, or the installed CLI fallback when the npm package does not expose a runnable binary
3294-
19. If Composio cannot start through npx, click `Check npx` and verify the app probes `npx --yes composio --version` instead of installing a separate CLI binary
3295-
20. If Composio is available but not authenticated, click `Login` and verify the app opens a new tab, starts `npx --yes composio login --no-browser -y`, captures the returned auth URL, and navigates the new tab to that URL
3293+
18. Switch to `Composio` and verify the workspace summary card shows the current installed Composio CLI login state, or a clear not-installed / not-authenticated message appears
3294+
19. If Composio CLI is not installed, click `Install Composio` and verify the app installs the CLI to `~/.composio/composio` using the official Composio installer
3295+
20. If Composio is available but not authenticated, click `Login` and verify the app opens a new tab, starts the installed `composio login --no-browser -y`, captures the returned auth URL, and navigates the new tab to that URL
32963296
21. Verify Composio connector cards show real connector details such as tool counts, trigger counts, auth mode, and connection state instead of only aggregate totals
32973297
22. In Composio search, type `instagram` and verify the Instagram connector appears first when it is returned by the connector source, ahead of description-only matches such as Meta Ads
32983298
23. Open a disconnected Composio connector and click `Connect` or `Reconnect`; verify the returned `connect.composio.dev` authorization URL opens
@@ -3317,9 +3317,9 @@ The `#/skills` route shows a full Skills & Apps directory with Plugins, Apps, Co
33173317
- App and plugin enable/disable actions update their local card state after a successful config write
33183318
- Plugin detail shows bundled MCP login state and can launch MCP OAuth for `notLoggedIn` servers
33193319
- Disconnected apps are labeled `Login`; connected apps are labeled `Manage`
3320-
- The Composio tab tries `npx --yes composio` by default and falls back to the installed CLI only when the current npm package cannot be executed
3321-
- The Composio check action probes `npx --yes composio --version` instead of installing a separate CLI binary
3322-
- The Composio login action opens a new tab from the click, starts `npx --yes composio login --no-browser -y`, then navigates that tab to the returned auth URL
3320+
- The Composio tab uses the installed Composio CLI, preferring `CODEXUI_COMPOSIO_COMMAND` when set and otherwise `~/.composio/composio` or `composio` on `PATH`
3321+
- The Composio install action uses the official installer and produces a working `~/.composio/composio` binary
3322+
- The Composio login action opens a new tab from the click, starts the installed `composio login --no-browser -y`, then navigates that tab to the returned auth URL
33233323
- Composio connector cards and detail views show concrete connector details, connection rows, and useful tool samples
33243324
- Composio search prioritizes exact slug/name matches above connectors that only mention the query in their description
33253325
- Unit coverage verifies that Composio exact query matches outrank description-only matches and that gateway connector search sends `query`, `cursor`, and `limit` params expected by the server

0 commit comments

Comments
 (0)