@commandable/mcp 0.1.2 → 0.2.0
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/{dist/app → .output}/nitro.json +1 -1
- package/{dist/app/public/_nuxt/-tOYwuj2.js → .output/public/_nuxt/B04gGCnx.js} +3 -3
- package/{dist/app/public/_nuxt/D-43HurL.js → .output/public/_nuxt/B2dAlp_u.js} +8 -8
- package/.output/public/_nuxt/Ba0BY0O0.js +1 -0
- package/.output/public/_nuxt/BvFUCPqA.js +1 -0
- package/{dist/app/public/_nuxt/BdctKXor.js → .output/public/_nuxt/Dm_hd4at.js} +1 -1
- package/{dist/app/public/_nuxt/BlP7Uu-5.js → .output/public/_nuxt/S2P9sd4n.js} +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -0
- package/.output/public/_nuxt/builds/meta/ee5097c4-b785-4b77-92d6-c16a7396d677.json +1 -0
- package/{dist/app/public/_nuxt/CsbkV5Bd.js → .output/public/_nuxt/d2XTSFt9.js} +1 -1
- package/{dist/app/server/chunks/build/_id_-DBwSV4AY.mjs → .output/server/chunks/build/_id_-Bnxenh08.mjs} +7 -7
- package/{dist/app/server/chunks/build/_id_-DBwSV4AY.mjs.map → .output/server/chunks/build/_id_-Bnxenh08.mjs.map} +1 -1
- package/{dist/app → .output}/server/chunks/build/client.precomputed.mjs +1 -1
- package/{dist/app/server/chunks/build/fetch-ZbqIFhDG.mjs → .output/server/chunks/build/fetch-BmYZnj75.mjs} +58 -6
- package/.output/server/chunks/build/fetch-BmYZnj75.mjs.map +1 -0
- package/{dist/app/server/chunks/build/index-C8flTcKI.mjs → .output/server/chunks/build/index-CL-Gkd-Y.mjs} +4 -4
- package/.output/server/chunks/build/index-CL-Gkd-Y.mjs.map +1 -0
- package/{dist/app → .output}/server/chunks/build/server.mjs +3 -3
- package/{dist/app → .output}/server/chunks/nitro/nitro.mjs +417 -204
- package/.output/server/chunks/nitro/nitro.mjs.map +1 -0
- package/{dist/app → .output}/server/chunks/routes/api/index.get.mjs +1 -1
- package/{dist/app → .output}/server/chunks/routes/api/index.post.mjs +1 -1
- package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/credentials-config.get.mjs +1 -1
- package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/credentials-status.get.mjs +1 -1
- package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/credentials.delete.mjs +1 -1
- package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/credentials.post.mjs +1 -1
- package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/permissions.post.mjs +1 -1
- package/.output/server/chunks/routes/api/integrations/_id/tools.delete.mjs +51 -0
- package/.output/server/chunks/routes/api/integrations/_id/tools.delete.mjs.map +1 -0
- package/.output/server/chunks/routes/api/integrations/_id/tools.get.mjs +57 -0
- package/.output/server/chunks/routes/api/integrations/_id/tools.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/integrations/_id/toolsets.get.mjs +53 -0
- package/.output/server/chunks/routes/api/integrations/_id/toolsets.get.mjs.map +1 -0
- package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/toolsets.post.mjs +1 -1
- package/.output/server/chunks/routes/api/integrations/_id_.delete.mjs +55 -0
- package/.output/server/chunks/routes/api/integrations/_id_.delete.mjs.map +1 -0
- package/{dist/app → .output}/server/chunks/routes/mcp/create.mjs +1 -1
- package/{dist/app → .output}/server/chunks/routes/mcp.mjs +1 -1
- package/{dist/app → .output}/server/chunks/routes/renderer.mjs +1 -1
- package/{dist/app → .output}/server/index.mjs +1 -1
- package/{dist/app → .output}/server/package.json +2 -2
- package/LICENSE +17 -6
- package/README.md +32 -32
- package/bin/cli.mjs +552 -0
- package/bin/commandable-mcp.mjs +8 -0
- package/package.json +30 -40
- package/dist/app/public/_nuxt/DU1mG77A.js +0 -1
- package/dist/app/public/_nuxt/builds/latest.json +0 -1
- package/dist/app/public/_nuxt/builds/meta/b13ec2b2-ddd3-4ead-abd4-4fba9dfc0061.json +0 -1
- package/dist/app/public/_nuxt/uS7FY2am.js +0 -1
- package/dist/app/server/chunks/build/fetch-ZbqIFhDG.mjs.map +0 -1
- package/dist/app/server/chunks/build/index-C8flTcKI.mjs.map +0 -1
- package/dist/app/server/chunks/nitro/nitro.mjs.map +0 -1
- package/dist/app/server/chunks/routes/api/integrations/_id_.delete.mjs +0 -44
- package/dist/app/server/chunks/routes/api/integrations/_id_.delete.mjs.map +0 -1
- package/dist/app/server/migrations/pg/0000_initial.sql +0 -74
- package/dist/app/server/migrations/pg/meta/_journal.json +0 -13
- package/dist/app/server/migrations/sqlite/0000_initial.sql +0 -74
- package/dist/app/server/migrations/sqlite/meta/_journal.json +0 -13
- package/dist/cli/bin.d.ts +0 -3
- package/dist/cli/bin.d.ts.map +0 -1
- package/dist/cli/bin.js +0 -7
- package/dist/cli/bin.js.map +0 -1
- package/dist/cli/credentialManager.d.ts +0 -19
- package/dist/cli/credentialManager.d.ts.map +0 -1
- package/dist/cli/credentialManager.js +0 -82
- package/dist/cli/credentialManager.js.map +0 -1
- package/dist/cli/index.d.ts +0 -17
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -818
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/setup.d.ts +0 -3
- package/dist/cli/setup.d.ts.map +0 -1
- package/dist/cli/setup.js +0 -353
- package/dist/cli/setup.js.map +0 -1
- package/dist/config/configApply.d.ts +0 -16
- package/dist/config/configApply.d.ts.map +0 -1
- package/dist/config/configApply.js +0 -77
- package/dist/config/configApply.js.map +0 -1
- package/dist/config/configLoader.d.ts +0 -9
- package/dist/config/configLoader.d.ts.map +0 -1
- package/dist/config/configLoader.js +0 -75
- package/dist/config/configLoader.js.map +0 -1
- package/dist/config/configSchema.d.ts +0 -45
- package/dist/config/configSchema.d.ts.map +0 -1
- package/dist/config/configSchema.js +0 -23
- package/dist/config/configSchema.js.map +0 -1
- package/dist/crypto/encryption.d.ts +0 -3
- package/dist/crypto/encryption.d.ts.map +0 -1
- package/dist/crypto/encryption.js +0 -29
- package/dist/crypto/encryption.js.map +0 -1
- package/dist/db/client.d.ts +0 -24
- package/dist/db/client.d.ts.map +0 -1
- package/dist/db/client.js +0 -50
- package/dist/db/client.js.map +0 -1
- package/dist/db/credentialStore.d.ts +0 -15
- package/dist/db/credentialStore.d.ts.map +0 -1
- package/dist/db/credentialStore.js +0 -56
- package/dist/db/credentialStore.js.map +0 -1
- package/dist/db/integrationStore.d.ts +0 -12
- package/dist/db/integrationStore.d.ts.map +0 -1
- package/dist/db/integrationStore.js +0 -111
- package/dist/db/integrationStore.js.map +0 -1
- package/dist/db/integrationTypeConfigStore.d.ts +0 -6
- package/dist/db/integrationTypeConfigStore.d.ts.map +0 -1
- package/dist/db/integrationTypeConfigStore.js +0 -94
- package/dist/db/integrationTypeConfigStore.js.map +0 -1
- package/dist/db/migrate.d.ts +0 -3
- package/dist/db/migrate.d.ts.map +0 -1
- package/dist/db/migrate.js +0 -11
- package/dist/db/migrate.js.map +0 -1
- package/dist/db/migrations/pg/0000_initial.sql +0 -74
- package/dist/db/migrations/pg/meta/_journal.json +0 -13
- package/dist/db/migrations/sqlite/0000_initial.sql +0 -74
- package/dist/db/migrations/sqlite/meta/_journal.json +0 -13
- package/dist/db/schema.d.ts +0 -1863
- package/dist/db/schema.d.ts.map +0 -1
- package/dist/db/schema.js +0 -133
- package/dist/db/schema.js.map +0 -1
- package/dist/db/toolDefinitionStore.d.ts +0 -6
- package/dist/db/toolDefinitionStore.d.ts.map +0 -1
- package/dist/db/toolDefinitionStore.js +0 -95
- package/dist/db/toolDefinitionStore.js.map +0 -1
- package/dist/errors/httpError.d.ts +0 -6
- package/dist/errors/httpError.d.ts.map +0 -1
- package/dist/errors/httpError.js +0 -11
- package/dist/errors/httpError.js.map +0 -1
- package/dist/index.d.ts +0 -32
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -32
- package/dist/index.js.map +0 -1
- package/dist/integrations/actionsFactory.d.ts +0 -16
- package/dist/integrations/actionsFactory.d.ts.map +0 -1
- package/dist/integrations/actionsFactory.js +0 -98
- package/dist/integrations/actionsFactory.js.map +0 -1
- package/dist/integrations/catalog.d.ts +0 -8
- package/dist/integrations/catalog.d.ts.map +0 -1
- package/dist/integrations/catalog.js +0 -45
- package/dist/integrations/catalog.js.map +0 -1
- package/dist/integrations/customToolFactory.d.ts +0 -13
- package/dist/integrations/customToolFactory.d.ts.map +0 -1
- package/dist/integrations/customToolFactory.js +0 -31
- package/dist/integrations/customToolFactory.js.map +0 -1
- package/dist/integrations/dataLoader.d.ts +0 -3
- package/dist/integrations/dataLoader.d.ts.map +0 -1
- package/dist/integrations/dataLoader.js +0 -2
- package/dist/integrations/dataLoader.js.map +0 -1
- package/dist/integrations/fileIntegrationTypeConfigStore.d.ts +0 -7
- package/dist/integrations/fileIntegrationTypeConfigStore.d.ts.map +0 -1
- package/dist/integrations/fileIntegrationTypeConfigStore.js +0 -34
- package/dist/integrations/fileIntegrationTypeConfigStore.js.map +0 -1
- package/dist/integrations/getIntegration.d.ts +0 -14
- package/dist/integrations/getIntegration.d.ts.map +0 -1
- package/dist/integrations/getIntegration.js +0 -30
- package/dist/integrations/getIntegration.js.map +0 -1
- package/dist/integrations/googleServiceAccount.d.ts +0 -6
- package/dist/integrations/googleServiceAccount.d.ts.map +0 -1
- package/dist/integrations/googleServiceAccount.js +0 -54
- package/dist/integrations/googleServiceAccount.js.map +0 -1
- package/dist/integrations/health.d.ts +0 -20
- package/dist/integrations/health.d.ts.map +0 -1
- package/dist/integrations/health.js +0 -43
- package/dist/integrations/health.js.map +0 -1
- package/dist/integrations/integrationTypeConfigLookup.d.ts +0 -12
- package/dist/integrations/integrationTypeConfigLookup.d.ts.map +0 -1
- package/dist/integrations/integrationTypeConfigLookup.js +0 -11
- package/dist/integrations/integrationTypeConfigLookup.js.map +0 -1
- package/dist/integrations/providerRegistry.d.ts +0 -2
- package/dist/integrations/providerRegistry.d.ts.map +0 -1
- package/dist/integrations/providerRegistry.js +0 -72
- package/dist/integrations/providerRegistry.js.map +0 -1
- package/dist/integrations/proxy.d.ts +0 -19
- package/dist/integrations/proxy.d.ts.map +0 -1
- package/dist/integrations/proxy.js +0 -377
- package/dist/integrations/proxy.js.map +0 -1
- package/dist/integrations/sandbox.d.ts +0 -8
- package/dist/integrations/sandbox.d.ts.map +0 -1
- package/dist/integrations/sandbox.js +0 -221
- package/dist/integrations/sandbox.js.map +0 -1
- package/dist/integrations/sandboxUtils.d.ts +0 -15
- package/dist/integrations/sandboxUtils.d.ts.map +0 -1
- package/dist/integrations/sandboxUtils.js +0 -489
- package/dist/integrations/sandboxUtils.js.map +0 -1
- package/dist/integrations/tools.d.ts +0 -3
- package/dist/integrations/tools.d.ts.map +0 -1
- package/dist/integrations/tools.js +0 -70
- package/dist/integrations/tools.js.map +0 -1
- package/dist/mcp/abilityCatalog.d.ts +0 -46
- package/dist/mcp/abilityCatalog.d.ts.map +0 -1
- package/dist/mcp/abilityCatalog.js +0 -275
- package/dist/mcp/abilityCatalog.js.map +0 -1
- package/dist/mcp/auth.d.ts +0 -18
- package/dist/mcp/auth.d.ts.map +0 -1
- package/dist/mcp/auth.js +0 -45
- package/dist/mcp/auth.js.map +0 -1
- package/dist/mcp/builder_guide.md +0 -441
- package/dist/mcp/commandable_readme.md +0 -29
- package/dist/mcp/handlers.d.ts +0 -21
- package/dist/mcp/handlers.d.ts.map +0 -1
- package/dist/mcp/handlers.js +0 -86
- package/dist/mcp/handlers.js.map +0 -1
- package/dist/mcp/metaTools.d.ts +0 -77
- package/dist/mcp/metaTools.d.ts.map +0 -1
- package/dist/mcp/metaTools.js +0 -753
- package/dist/mcp/metaTools.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -25
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -14
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/sessionState.d.ts +0 -18
- package/dist/mcp/sessionState.d.ts.map +0 -1
- package/dist/mcp/sessionState.js +0 -65
- package/dist/mcp/sessionState.js.map +0 -1
- package/dist/mcp/toolAdapter.d.ts +0 -34
- package/dist/mcp/toolAdapter.d.ts.map +0 -1
- package/dist/mcp/toolAdapter.js +0 -24
- package/dist/mcp/toolAdapter.js.map +0 -1
- package/dist/types.d.ts +0 -92
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -7
- package/dist/version.js.map +0 -1
- /package/{dist/app → .output}/public/_fonts/57NSSoFy1VLVs2gqly8Ls9awBnZMFyXGrefpmqvdqmc-zJfbBtpgM4cDmcXBsqZNW79_kFnlpPd62b48glgdydA.woff2 +0 -0
- /package/{dist/app → .output}/public/_fonts/8VR2wSMN-3U4NbWAVYXlkRV6hA0jFBXP-0RtL3X7fko-x2gYI4qfmkRdxyQQUPaBZdZdgl1TeVrquF_TxHeM4lM.woff2 +0 -0
- /package/{dist/app → .output}/public/_fonts/GsKUclqeNLJ96g5AU593ug6yanivOiwjW_7zESNPChw-jHA4tBeM1bjF7LATGUpfBuSTyomIFrWBTzjF7txVYfg.woff2 +0 -0
- /package/{dist/app → .output}/public/_fonts/Ld1FnTo3yTIwDyGfTQ5-Fws9AWsCbKfMvgxduXr7JcY-W25bL8NF1fjpLRSOgJb7RoZPHqGQNwMTM7S9tHVoxx8.woff2 +0 -0
- /package/{dist/app → .output}/public/_fonts/NdzqRASp2bovDUhQT1IRE_EMqKJ2KYQdTCfFcBvL8yw-KhwZiS86o3fErOe5GGMExHUemmI_dBfaEFxjISZrBd0.woff2 +0 -0
- /package/{dist/app → .output}/public/_fonts/iTkrULNFJJkTvihIg1Vqi5IODRH_9btXCioVF5l98I8-AndUyau2HR2felA_ra8V2mutQgschhasE5FD1dXGJX8.woff2 +0 -0
- /package/{dist/app → .output}/public/_nuxt/_id_.BKAjWkoP.css +0 -0
- /package/{dist/app → .output}/public/_nuxt/entry.Y3mA4bzA.css +0 -0
- /package/{dist/app → .output}/public/_nuxt/error-404.C7fg894-.css +0 -0
- /package/{dist/app → .output}/public/_nuxt/error-500.DjUK_N2Y.css +0 -0
- /package/{dist/app → .output}/public/favicon.ico +0 -0
- /package/{dist/app → .output}/server/chunks/_/error-500.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/_/error-500.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/_/icons.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/_/icons.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/_/icons2.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/_/icons2.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/build/IntegrationCredentials-styles.CULcCK6_.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/build/IntegrationCredentials-styles.CULcCK6_.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/build/client.precomputed.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/build/error-404-D2QibUBT.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/build/error-404-D2QibUBT.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/build/error-404-styles.Bvxdxqjk.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/build/error-404-styles.Bvxdxqjk.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/build/error-500-DYvawybF.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/build/error-500-DYvawybF.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/build/error-500-styles.BnYAAXSg.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/build/error-500-styles.BnYAAXSg.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/build/index-5H-nmhph.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/build/index-5H-nmhph.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/build/server.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/build/styles.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/build/styles.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/_commandable/status.get.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/_commandable/status.get.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/catalog/_type/tools.get.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/catalog/_type/tools.get.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/catalog/_type/toolsets.get.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/catalog/_type/toolsets.get.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/catalog.get.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/catalog.get.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/index.get.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/index.post.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/credentials-config.get.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/credentials-status.get.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/credentials.delete.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/credentials.post.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/permissions.post.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/api/integrations/_id/toolsets.post.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/health.get.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/routes/health.get.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/mcp/create.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/mcp.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/routes/renderer.mjs.map +0 -0
- /package/{dist/app → .output}/server/chunks/virtual/_virtual_spa-template.mjs +0 -0
- /package/{dist/app → .output}/server/chunks/virtual/_virtual_spa-template.mjs.map +0 -0
- /package/{dist/app → .output}/server/index.mjs.map +0 -0
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
import crypto from 'node:crypto'
|
|
2
|
+
import { spawn, spawnSync } from 'node:child_process'
|
|
3
|
+
import { existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { resolve } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import { createRequire } from 'node:module'
|
|
7
|
+
import { isCancel, note, select } from '@clack/prompts'
|
|
8
|
+
import picocolors from 'picocolors'
|
|
9
|
+
import {
|
|
10
|
+
SqlCredentialStore,
|
|
11
|
+
applyConfig,
|
|
12
|
+
createApiKey,
|
|
13
|
+
createDbFromEnv,
|
|
14
|
+
ensureSchema,
|
|
15
|
+
generateApiKey,
|
|
16
|
+
getCommandableDir,
|
|
17
|
+
getOrCreateEncryptionSecret,
|
|
18
|
+
loadConfig,
|
|
19
|
+
} from '@commandable/mcp-core'
|
|
20
|
+
|
|
21
|
+
const require = createRequire(import.meta.url)
|
|
22
|
+
const pkg = require('../package.json')
|
|
23
|
+
const COMMANDABLE_VERSION = String(pkg.version || '0.0.0')
|
|
24
|
+
const packageRoot = resolve(fileURLToPath(new URL('..', import.meta.url)))
|
|
25
|
+
const serverEntry = resolve(packageRoot, '.output', 'server', 'index.mjs')
|
|
26
|
+
const CLAUDE_CODE_STDIO_ENV_KEYS = [
|
|
27
|
+
'COMMANDABLE_SPACE_ID',
|
|
28
|
+
'COMMANDABLE_DATA_DIR',
|
|
29
|
+
'COMMANDABLE_MCP_SQLITE_PATH',
|
|
30
|
+
'COMMANDABLE_UI_PORT',
|
|
31
|
+
'DATABASE_URL',
|
|
32
|
+
'COMMANDABLE_CONFIG_FILE',
|
|
33
|
+
'COMMANDABLE_INTEGRATION_DATA_DIR',
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
function hasFlag(...flags) {
|
|
37
|
+
return flags.some(flag => process.argv.includes(flag))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getFlagValue(flag) {
|
|
41
|
+
const index = process.argv.indexOf(flag)
|
|
42
|
+
if (index === -1)
|
|
43
|
+
return null
|
|
44
|
+
const value = process.argv[index + 1]
|
|
45
|
+
if (!value || value.startsWith('-'))
|
|
46
|
+
return null
|
|
47
|
+
return value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getUiPort() {
|
|
51
|
+
const raw = process.env.COMMANDABLE_UI_PORT
|
|
52
|
+
return raw && /^\d+$/.test(raw) ? Number(raw) : 23432
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getBaseUrl() {
|
|
56
|
+
return `http://127.0.0.1:${getUiPort()}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function daemonPidPath() {
|
|
60
|
+
return resolve(getCommandableDir(), 'daemon.pid')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function daemonLogPath() {
|
|
64
|
+
return resolve(getCommandableDir(), 'daemon.log')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getSqlitePathForLocalState() {
|
|
68
|
+
const forced = process.env.COMMANDABLE_MCP_SQLITE_PATH
|
|
69
|
+
if (forced && forced.trim().length)
|
|
70
|
+
return resolve(forced.trim())
|
|
71
|
+
return resolve(getCommandableDir(), 'credentials.sqlite')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readDaemonPid() {
|
|
75
|
+
try {
|
|
76
|
+
const raw = readFileSync(daemonPidPath(), 'utf8')
|
|
77
|
+
const lines = raw.split('\n').map(line => line.trim()).filter(Boolean)
|
|
78
|
+
const pid = lines[0] && /^\d+$/.test(lines[0]) ? Number(lines[0]) : null
|
|
79
|
+
const version = lines[1] || null
|
|
80
|
+
return pid ? { pid, version } : null
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isProcessAlive(pid) {
|
|
88
|
+
try {
|
|
89
|
+
process.kill(pid, 0)
|
|
90
|
+
return true
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function stopDaemonProcess() {
|
|
98
|
+
const info = readDaemonPid()
|
|
99
|
+
if (info?.pid) {
|
|
100
|
+
try {
|
|
101
|
+
process.kill(info.pid, 'SIGTERM')
|
|
102
|
+
}
|
|
103
|
+
catch {}
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
unlinkSync(daemonPidPath())
|
|
107
|
+
}
|
|
108
|
+
catch {}
|
|
109
|
+
return { stopped: !!info?.pid, pid: info?.pid ?? null }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function fetchJsonWithTimeout(url, timeoutMs) {
|
|
113
|
+
const controller = new AbortController()
|
|
114
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs)
|
|
115
|
+
try {
|
|
116
|
+
const response = await fetch(url, { signal: controller.signal })
|
|
117
|
+
const text = await response.text()
|
|
118
|
+
let json = null
|
|
119
|
+
try {
|
|
120
|
+
json = text ? JSON.parse(text) : null
|
|
121
|
+
}
|
|
122
|
+
catch {}
|
|
123
|
+
return { ok: response.ok, status: response.status, json }
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
clearTimeout(timeout)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function waitForHttp(url, timeoutMs) {
|
|
131
|
+
const started = Date.now()
|
|
132
|
+
while (Date.now() - started < timeoutMs) {
|
|
133
|
+
const probe = await fetchJsonWithTimeout(url, 400).catch(() => null)
|
|
134
|
+
if (probe?.ok)
|
|
135
|
+
return probe
|
|
136
|
+
await new Promise(resolvePromise => setTimeout(resolvePromise, 250))
|
|
137
|
+
}
|
|
138
|
+
return null
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function assertLocalServerRunning() {
|
|
142
|
+
const baseUrl = getBaseUrl()
|
|
143
|
+
const probe = await fetchJsonWithTimeout(`${baseUrl}/api/_commandable/status`, 500).catch(() => null)
|
|
144
|
+
if (probe?.ok)
|
|
145
|
+
return probe.json
|
|
146
|
+
|
|
147
|
+
console.error('Commandable local mode requires the server to already be running.')
|
|
148
|
+
console.error('Start it first with: npx -y @commandable/mcp serve')
|
|
149
|
+
process.exit(1)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function makeClaudeCodeAddCommand() {
|
|
153
|
+
const envArgs = getClaudeCodeEnvEntries()
|
|
154
|
+
.map(value => `-e ${quoteShellArg(value)}`)
|
|
155
|
+
.join(' ')
|
|
156
|
+
return `claude mcp add commandable${envArgs ? ` ${envArgs}` : ''} -- npx -y @commandable/mcp-connect create-mode`
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function getClaudeCodeEnvEntries() {
|
|
160
|
+
return CLAUDE_CODE_STDIO_ENV_KEYS
|
|
161
|
+
.map((key) => {
|
|
162
|
+
const value = process.env[key]
|
|
163
|
+
return value && value.trim().length ? `${key}=${value}` : null
|
|
164
|
+
})
|
|
165
|
+
.filter(Boolean)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function quoteShellArg(value) {
|
|
169
|
+
if (/^[A-Za-z0-9_./:=@-]+$/.test(value))
|
|
170
|
+
return value
|
|
171
|
+
return `'${value.replace(/'/g, `'\"'\"'`)}'`
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function makeReadModeConfig() {
|
|
175
|
+
return {
|
|
176
|
+
mcpServers: {
|
|
177
|
+
commandable: {
|
|
178
|
+
command: 'npx',
|
|
179
|
+
args: ['-y', '@commandable/mcp-connect'],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function makeHttpConnectionDetails() {
|
|
186
|
+
const url = getFlagValue('--url') || 'https://your-host/mcp'
|
|
187
|
+
const apiKey = getFlagValue('--api-key') || '<api-key>'
|
|
188
|
+
return {
|
|
189
|
+
url,
|
|
190
|
+
headers: {
|
|
191
|
+
Authorization: `Bearer ${apiKey}`,
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function makeClaudeCodeHttpAddCommand() {
|
|
197
|
+
const details = makeHttpConnectionDetails()
|
|
198
|
+
return `claude mcp add --transport http commandable ${details.url} --header "Authorization: ${details.headers.Authorization}"`
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function printCreateInstructions(addCommand, instanceLabel) {
|
|
202
|
+
const line = picocolors.dim('─'.repeat(60))
|
|
203
|
+
console.error('')
|
|
204
|
+
console.error(picocolors.white('The current create experience requires an advanced MCP client. We recommend Claude Code.'))
|
|
205
|
+
console.error('')
|
|
206
|
+
console.error(picocolors.bold(`Connect Claude Code to your ${instanceLabel}:`))
|
|
207
|
+
console.error(line)
|
|
208
|
+
console.error(picocolors.cyan('claude mcp remove commandable'))
|
|
209
|
+
console.error(picocolors.cyan(addCommand))
|
|
210
|
+
console.error(line)
|
|
211
|
+
console.error(picocolors.dim('Paste the commands above into your terminal, then restart Claude Code.'))
|
|
212
|
+
console.error(picocolors.dim('Previous Commandable instances keep their own state and can be re-added later.'))
|
|
213
|
+
console.error('')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function execClaudeMcpAdd(args) {
|
|
217
|
+
const result = spawnSync('claude', args, { stdio: 'inherit' })
|
|
218
|
+
if (result.error) {
|
|
219
|
+
console.error(`claude ${args.join(' ')}`)
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
if (result.status !== 0)
|
|
223
|
+
process.exit(result.status ?? 1)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function startManagementUi({ restart }) {
|
|
227
|
+
const baseUrl = getBaseUrl()
|
|
228
|
+
const existingPid = readDaemonPid()
|
|
229
|
+
if (!restart && existingPid?.pid && isProcessAlive(existingPid.pid)) {
|
|
230
|
+
const probe = await fetchJsonWithTimeout(`${baseUrl}/api/_commandable/status`, 500).catch(() => null)
|
|
231
|
+
if (probe?.ok)
|
|
232
|
+
return { reused: true, baseUrl }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (restart)
|
|
236
|
+
stopDaemonProcess()
|
|
237
|
+
|
|
238
|
+
if (!existsSync(serverEntry)) {
|
|
239
|
+
console.error(`Missing built app server at ${serverEntry}`)
|
|
240
|
+
console.error('Run a package build first.')
|
|
241
|
+
process.exit(1)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
mkdirSync(getCommandableDir(), { recursive: true })
|
|
245
|
+
const logPath = daemonLogPath()
|
|
246
|
+
const fd = openSync(logPath, 'a')
|
|
247
|
+
const child = spawn(process.execPath, [serverEntry], {
|
|
248
|
+
cwd: packageRoot,
|
|
249
|
+
env: {
|
|
250
|
+
...process.env,
|
|
251
|
+
HOST: '127.0.0.1',
|
|
252
|
+
PORT: String(getUiPort()),
|
|
253
|
+
COMMANDABLE_UI_PORT: String(getUiPort()),
|
|
254
|
+
COMMANDABLE_VERSION,
|
|
255
|
+
},
|
|
256
|
+
detached: true,
|
|
257
|
+
stdio: ['ignore', fd, fd],
|
|
258
|
+
})
|
|
259
|
+
child.unref()
|
|
260
|
+
|
|
261
|
+
writeFileSync(daemonPidPath(), `${child.pid}\n${COMMANDABLE_VERSION}\n`)
|
|
262
|
+
|
|
263
|
+
const probe = await waitForHttp(`${baseUrl}/api/_commandable/status`, 12000)
|
|
264
|
+
if (!probe?.ok) {
|
|
265
|
+
try {
|
|
266
|
+
process.kill(child.pid, 'SIGTERM')
|
|
267
|
+
}
|
|
268
|
+
catch {}
|
|
269
|
+
try {
|
|
270
|
+
unlinkSync(daemonPidPath())
|
|
271
|
+
}
|
|
272
|
+
catch {}
|
|
273
|
+
console.error(`Commandable management UI failed to start at ${baseUrl}`)
|
|
274
|
+
console.error(`Check the daemon log at ${logPath}`)
|
|
275
|
+
process.exit(1)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return { reused: false, baseUrl }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function openEnvState() {
|
|
282
|
+
const db = createDbFromEnv()
|
|
283
|
+
await ensureSchema(db)
|
|
284
|
+
const secret = getOrCreateEncryptionSecret()
|
|
285
|
+
const credentialStore = new SqlCredentialStore(db, secret)
|
|
286
|
+
return {
|
|
287
|
+
db,
|
|
288
|
+
credentialStore,
|
|
289
|
+
close: async () => {
|
|
290
|
+
if (db.dialect === 'sqlite')
|
|
291
|
+
db.close()
|
|
292
|
+
else
|
|
293
|
+
await db.close()
|
|
294
|
+
},
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function runServe() {
|
|
299
|
+
const migrateClient = createDbFromEnv()
|
|
300
|
+
await ensureSchema(migrateClient)
|
|
301
|
+
if (migrateClient.dialect === 'sqlite')
|
|
302
|
+
migrateClient.close()
|
|
303
|
+
else
|
|
304
|
+
await migrateClient.close()
|
|
305
|
+
|
|
306
|
+
const ui = await startManagementUi({ restart: hasFlag('--restart') })
|
|
307
|
+
const baseUrl = ui.baseUrl
|
|
308
|
+
console.error(picocolors.green(`Commandable local instance ${ui.reused ? 'ready' : 'running'}.`))
|
|
309
|
+
console.error(`${picocolors.dim('Base URL:')} ${baseUrl}`)
|
|
310
|
+
console.error(`${picocolors.dim('Management UI:')} ${baseUrl}/`)
|
|
311
|
+
console.error(`${picocolors.dim('MCP endpoint:')} ${baseUrl}/mcp`)
|
|
312
|
+
console.error(`${picocolors.dim('Create endpoint:')} ${baseUrl}/mcp/create`)
|
|
313
|
+
console.error(`${picocolors.dim('Data dir:')} ${getCommandableDir()}`)
|
|
314
|
+
console.error(`${picocolors.dim('SQLite:')} ${getSqlitePathForLocalState()}`)
|
|
315
|
+
console.error(`Next: ${picocolors.cyan('npx -y @commandable/mcp create')}`)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function runCreate() {
|
|
319
|
+
const transport = (getFlagValue('--transport') || 'stdio').trim().toLowerCase()
|
|
320
|
+
const shouldApply = hasFlag('--apply')
|
|
321
|
+
|
|
322
|
+
if (transport === 'http') {
|
|
323
|
+
const command = makeClaudeCodeHttpAddCommand()
|
|
324
|
+
if (!shouldApply) {
|
|
325
|
+
printCreateInstructions(command, 'HTTP deployment')
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
const url = getFlagValue('--url')
|
|
329
|
+
const apiKey = getFlagValue('--api-key')
|
|
330
|
+
if (!url || !apiKey) {
|
|
331
|
+
console.error(`For ${picocolors.cyan('--apply')} with HTTP, pass ${picocolors.cyan('--url')} and ${picocolors.cyan('--api-key')}.`)
|
|
332
|
+
process.exit(1)
|
|
333
|
+
}
|
|
334
|
+
execClaudeMcpAdd(['mcp', 'add', '--transport', 'http', 'commandable', url, '--header', `Authorization: Bearer ${apiKey}`])
|
|
335
|
+
console.error(`${picocolors.green('Done.')} Restart Claude Code.`)
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return assertLocalServerRunning().then(() => {
|
|
340
|
+
const command = makeClaudeCodeAddCommand()
|
|
341
|
+
if (!shouldApply) {
|
|
342
|
+
printCreateInstructions(command, 'local instance')
|
|
343
|
+
return
|
|
344
|
+
}
|
|
345
|
+
const envArgs = getClaudeCodeEnvEntries().flatMap(value => ['-e', value])
|
|
346
|
+
execClaudeMcpAdd(['mcp', 'add', 'commandable', ...envArgs, '--', 'npx', '-y', '@commandable/mcp-connect', 'create-mode'])
|
|
347
|
+
console.error(`${picocolors.green('Done.')} Restart Claude Code.`)
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function runConnect() {
|
|
352
|
+
const transport = (getFlagValue('--transport') || 'stdio').trim().toLowerCase()
|
|
353
|
+
const client = (getFlagValue('--client') || 'claude-desktop').trim().toLowerCase()
|
|
354
|
+
if (client !== 'claude-desktop' && client !== 'cursor') {
|
|
355
|
+
console.error('Invalid --client value. Use claude-desktop or cursor.')
|
|
356
|
+
process.exit(1)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (transport === 'http') {
|
|
360
|
+
console.error(JSON.stringify(makeHttpConnectionDetails(), null, 2))
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
await assertLocalServerRunning()
|
|
365
|
+
console.error(JSON.stringify(makeReadModeConfig(), null, 2))
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async function runDoctor() {
|
|
369
|
+
const baseUrl = getBaseUrl()
|
|
370
|
+
const pid = readDaemonPid()
|
|
371
|
+
const pidAlive = pid ? isProcessAlive(pid.pid) : false
|
|
372
|
+
const probe = await fetchJsonWithTimeout(`${baseUrl}/api/_commandable/status`, 500).catch(() => null)
|
|
373
|
+
const runningVersion = typeof probe?.json?.version === 'string' && probe.json.version.trim().length
|
|
374
|
+
? probe.json.version.trim()
|
|
375
|
+
: (pid?.version || null)
|
|
376
|
+
const databaseUrl = process.env.DATABASE_URL
|
|
377
|
+
const sqlitePath = databaseUrl && databaseUrl.trim().length ? null : getSqlitePathForLocalState()
|
|
378
|
+
|
|
379
|
+
console.error(JSON.stringify({
|
|
380
|
+
ok: true,
|
|
381
|
+
installedVersion: COMMANDABLE_VERSION,
|
|
382
|
+
env: {
|
|
383
|
+
COMMANDABLE_SPACE_ID: (process.env.COMMANDABLE_SPACE_ID || 'local').trim() || 'local',
|
|
384
|
+
COMMANDABLE_DATA_DIR: process.env.COMMANDABLE_DATA_DIR || null,
|
|
385
|
+
COMMANDABLE_MCP_SQLITE_PATH: process.env.COMMANDABLE_MCP_SQLITE_PATH || null,
|
|
386
|
+
COMMANDABLE_UI_PORT: getUiPort(),
|
|
387
|
+
DATABASE_URL: databaseUrl && databaseUrl.trim().length ? '[set]' : null,
|
|
388
|
+
},
|
|
389
|
+
localState: {
|
|
390
|
+
dataDir: getCommandableDir(),
|
|
391
|
+
sqlitePath,
|
|
392
|
+
daemonPidPath: daemonPidPath(),
|
|
393
|
+
daemonLogPath: daemonLogPath(),
|
|
394
|
+
encryptionKeyPath: resolve(getCommandableDir(), 'encryption.key'),
|
|
395
|
+
},
|
|
396
|
+
daemon: {
|
|
397
|
+
running: !!(pidAlive && probe?.ok),
|
|
398
|
+
pid: pid ?? null,
|
|
399
|
+
baseUrl,
|
|
400
|
+
runningVersion,
|
|
401
|
+
versionMatch: !!runningVersion && runningVersion === COMMANDABLE_VERSION,
|
|
402
|
+
status: probe?.json ?? null,
|
|
403
|
+
},
|
|
404
|
+
}, null, 2))
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async function runDestroyLocal() {
|
|
408
|
+
const keepKey = hasFlag('--keep-key')
|
|
409
|
+
if (!hasFlag('--yes')) {
|
|
410
|
+
note(
|
|
411
|
+
[
|
|
412
|
+
picocolors.bold(picocolors.red('This will delete all of your local saved Commandable data.')),
|
|
413
|
+
'',
|
|
414
|
+
`${picocolors.white('•')} saved integrations and credentials`,
|
|
415
|
+
`${picocolors.white('•')} local SQLite state and daemon state`,
|
|
416
|
+
`${picocolors.white('•')} ${keepKey ? 'the encryption key will be kept (--keep-key is set)' : 'the local encryption key'}`,
|
|
417
|
+
'',
|
|
418
|
+
picocolors.yellow('Remote Postgres data will not be deleted.'),
|
|
419
|
+
].join('\n'),
|
|
420
|
+
picocolors.yellow('Warning'),
|
|
421
|
+
{ format: str => str },
|
|
422
|
+
)
|
|
423
|
+
const result = await select({
|
|
424
|
+
message: 'Do you want to continue?',
|
|
425
|
+
options: [
|
|
426
|
+
{ value: 'destroy', label: picocolors.red('Yes, destroy local data') },
|
|
427
|
+
{ value: 'cancel', label: picocolors.cyan('No, keep local data') },
|
|
428
|
+
],
|
|
429
|
+
initialValue: 'cancel',
|
|
430
|
+
})
|
|
431
|
+
if (isCancel(result) || result !== 'destroy') {
|
|
432
|
+
console.error(picocolors.yellow('Destroy cancelled.'))
|
|
433
|
+
return
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const dataDir = getCommandableDir()
|
|
438
|
+
const sqlitePath = getSqlitePathForLocalState()
|
|
439
|
+
const keyPath = resolve(dataDir, 'encryption.key')
|
|
440
|
+
const daemonPath = daemonPidPath()
|
|
441
|
+
const logPath = daemonLogPath()
|
|
442
|
+
const stopped = stopDaemonProcess()
|
|
443
|
+
const removed = []
|
|
444
|
+
const missing = []
|
|
445
|
+
|
|
446
|
+
for (const file of [sqlitePath, daemonPath, logPath, ...(keepKey ? [] : [keyPath])]) {
|
|
447
|
+
if (!existsSync(file)) {
|
|
448
|
+
missing.push(file)
|
|
449
|
+
continue
|
|
450
|
+
}
|
|
451
|
+
unlinkSync(file)
|
|
452
|
+
removed.push(file)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
console.error(picocolors.green('Local destroy complete.'))
|
|
456
|
+
console.error(`${picocolors.dim('Data dir:')} ${dataDir}`)
|
|
457
|
+
console.error(`${picocolors.dim('Daemon stopped:')} ${stopped.stopped ? 'yes' : 'no'}`)
|
|
458
|
+
if (removed.length)
|
|
459
|
+
console.error(`${picocolors.dim('Removed:')} ${removed.join(', ')}`)
|
|
460
|
+
if (missing.length)
|
|
461
|
+
console.error(`${picocolors.dim('Already absent:')} ${missing.join(', ')}`)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function runApply() {
|
|
465
|
+
const cfgPath = getFlagValue('--config') || process.env.COMMANDABLE_CONFIG_FILE || null
|
|
466
|
+
const { config, path } = loadConfig(cfgPath || undefined)
|
|
467
|
+
const spaceId = process.env.COMMANDABLE_SPACE_ID || config.spaceId || 'local'
|
|
468
|
+
const { db, credentialStore, close } = await openEnvState()
|
|
469
|
+
try {
|
|
470
|
+
const result = await applyConfig({ config, db, credentialStore, defaultSpaceId: spaceId })
|
|
471
|
+
console.error(`${picocolors.green('Config applied.')}`)
|
|
472
|
+
console.error(`${picocolors.dim('File:')} ${path}`)
|
|
473
|
+
console.error(`${picocolors.dim('Space:')} ${result.spaceId}`)
|
|
474
|
+
console.error(`${picocolors.dim('Integrations upserted:')} ${result.integrationsUpserted}`)
|
|
475
|
+
console.error(`${picocolors.dim('Credentials written:')} ${result.credentialsWritten}`)
|
|
476
|
+
console.error(`${picocolors.dim('Credentials unchanged:')} ${result.credentialsUnchanged}`)
|
|
477
|
+
}
|
|
478
|
+
finally {
|
|
479
|
+
await close()
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
async function runCreateApiKey() {
|
|
484
|
+
const name = process.argv[3] && !process.argv[3].startsWith('-') ? String(process.argv[3]) : 'default'
|
|
485
|
+
const rawKey = generateApiKey()
|
|
486
|
+
const id = crypto.randomUUID ? crypto.randomUUID() : crypto.randomBytes(16).toString('hex')
|
|
487
|
+
const { db, close } = await openEnvState()
|
|
488
|
+
try {
|
|
489
|
+
await createApiKey(db, { id, name, rawKey })
|
|
490
|
+
console.error(`${picocolors.green('API key created.')}`)
|
|
491
|
+
console.error(`${picocolors.dim('Name:')} ${name}`)
|
|
492
|
+
console.error(`${picocolors.dim('ID:')} ${id}`)
|
|
493
|
+
console.error(`${picocolors.dim('Key (store this now):')} ${rawKey}`)
|
|
494
|
+
}
|
|
495
|
+
finally {
|
|
496
|
+
await close()
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function help(exitCode = 0) {
|
|
501
|
+
console.error([
|
|
502
|
+
'',
|
|
503
|
+
picocolors.bold('Commandable MCP'),
|
|
504
|
+
picocolors.dim(`v${COMMANDABLE_VERSION}`),
|
|
505
|
+
'',
|
|
506
|
+
picocolors.bold('Usage'),
|
|
507
|
+
` ${picocolors.cyan('commandable-mcp serve')} ${picocolors.dim('[--restart]')}`,
|
|
508
|
+
` ${picocolors.cyan('commandable-mcp create')} ${picocolors.dim('[--transport stdio|http] [--apply] [--url] [--api-key]')}`,
|
|
509
|
+
` ${picocolors.cyan('commandable-mcp connect')} ${picocolors.dim('[--client claude-desktop|cursor] [--transport stdio|http] [--url] [--api-key]')}`,
|
|
510
|
+
` ${picocolors.cyan('commandable-mcp doctor')}`,
|
|
511
|
+
` ${picocolors.cyan('commandable-mcp destroy local')} ${picocolors.dim('[--yes] [--keep-key]')}`,
|
|
512
|
+
` ${picocolors.cyan('commandable-mcp apply')} ${picocolors.dim('[--config ./commandable.config.yaml]')}`,
|
|
513
|
+
` ${picocolors.cyan('commandable-mcp create-api-key')} ${picocolors.dim('[name]')}`,
|
|
514
|
+
` ${picocolors.cyan('commandable-mcp --version')}`,
|
|
515
|
+
'',
|
|
516
|
+
picocolors.bold('Notes'),
|
|
517
|
+
`- ${picocolors.bold('Serve')}: starts or reuses the local Commandable instance.`,
|
|
518
|
+
`- ${picocolors.bold('Create')}: Claude Code authoring flow. Prints or applies ${picocolors.cyan('claude mcp add')}.`,
|
|
519
|
+
`- ${picocolors.bold('Connect')}: prints read-client connection details.`,
|
|
520
|
+
'',
|
|
521
|
+
].join('\n'))
|
|
522
|
+
process.exit(exitCode)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export async function main() {
|
|
526
|
+
const cmd = process.argv[2]
|
|
527
|
+
|
|
528
|
+
if (hasFlag('--version', '-v')) {
|
|
529
|
+
console.error(COMMANDABLE_VERSION)
|
|
530
|
+
process.exit(0)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (hasFlag('--help', '-h'))
|
|
534
|
+
help(0)
|
|
535
|
+
|
|
536
|
+
if (cmd === 'serve')
|
|
537
|
+
return runServe()
|
|
538
|
+
if (cmd === 'create')
|
|
539
|
+
return runCreate()
|
|
540
|
+
if (cmd === 'connect')
|
|
541
|
+
return runConnect()
|
|
542
|
+
if (cmd === 'doctor')
|
|
543
|
+
return runDoctor()
|
|
544
|
+
if (cmd === 'apply')
|
|
545
|
+
return runApply()
|
|
546
|
+
if (cmd === 'create-api-key')
|
|
547
|
+
return runCreateApiKey()
|
|
548
|
+
if (cmd === 'destroy' && (process.argv[3] || '').trim().toLowerCase() === 'local')
|
|
549
|
+
return runDestroyLocal()
|
|
550
|
+
|
|
551
|
+
help(cmd ? 1 : 0)
|
|
552
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commandable/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Commandable app/server package for local management UI, HTTP endpoints, and human-facing CLI flows.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/commandable/commandable-mcp.git",
|
|
9
|
-
"directory": "
|
|
9
|
+
"directory": "app"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://github.com/commandable/commandable-mcp#readme",
|
|
12
12
|
"bugs": {
|
|
@@ -16,53 +16,43 @@
|
|
|
16
16
|
"node": ">=18"
|
|
17
17
|
},
|
|
18
18
|
"type": "module",
|
|
19
|
-
"main": "./dist/index.js",
|
|
20
|
-
"types": "./dist/index.d.ts",
|
|
21
19
|
"bin": {
|
|
22
|
-
"commandable-mcp": "./
|
|
23
|
-
},
|
|
24
|
-
"exports": {
|
|
25
|
-
".": {
|
|
26
|
-
"types": "./dist/index.d.ts",
|
|
27
|
-
"import": "./dist/index.js"
|
|
28
|
-
}
|
|
20
|
+
"commandable-mcp": "./bin/commandable-mcp.mjs"
|
|
29
21
|
},
|
|
30
22
|
"publishConfig": {
|
|
31
23
|
"access": "public"
|
|
32
24
|
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "nuxt build",
|
|
27
|
+
"dev": "nuxt dev",
|
|
28
|
+
"preview": "nuxt preview",
|
|
29
|
+
"postinstall": "nuxt prepare",
|
|
30
|
+
"lint": "eslint .",
|
|
31
|
+
"test": "echo \"(no app tests)\"",
|
|
32
|
+
"typecheck": "nuxt typecheck",
|
|
33
|
+
"prepack": "npm run build"
|
|
34
|
+
},
|
|
33
35
|
"dependencies": {
|
|
36
|
+
"@commandable/mcp-core": "0.2.0",
|
|
34
37
|
"@clack/prompts": "^1.0.1",
|
|
35
|
-
"@
|
|
36
|
-
"@
|
|
37
|
-
"
|
|
38
|
-
"drizzle-orm": "^0.44.5",
|
|
39
|
-
"fastest-levenshtein": "^1.0.16",
|
|
40
|
-
"google-auth-library": "^10.3.0",
|
|
41
|
-
"js-yaml": "^4.1.0",
|
|
42
|
-
"json-schema": "^0.4.0",
|
|
38
|
+
"@iconify-json/lucide": "^1.2.90",
|
|
39
|
+
"@iconify-json/simple-icons": "^1.2.70",
|
|
40
|
+
"@nuxt/ui": "^4.4.0",
|
|
43
41
|
"marked": "^17.0.3",
|
|
44
|
-
"
|
|
42
|
+
"nuxt": "^4.3.1",
|
|
45
43
|
"picocolors": "^1.1.1",
|
|
46
|
-
"
|
|
47
|
-
"zod": "^4.1.5"
|
|
44
|
+
"tailwindcss": "^4.1.18"
|
|
48
45
|
},
|
|
49
46
|
"devDependencies": {
|
|
50
|
-
"@
|
|
51
|
-
"
|
|
52
|
-
"@types/node": "^24.3.0",
|
|
53
|
-
"@types/pg": "^8.15.5",
|
|
54
|
-
"dotenv": "^17.3.1",
|
|
55
|
-
"drizzle-kit": "^0.31.9",
|
|
47
|
+
"@nuxt/eslint": "^1.15.1",
|
|
48
|
+
"eslint": "^10.0.0",
|
|
56
49
|
"typescript": "^5.9.3",
|
|
57
|
-
"
|
|
58
|
-
},
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"db:generate": "drizzle-kit generate",
|
|
66
|
-
"test": "vitest run"
|
|
67
|
-
}
|
|
50
|
+
"vue-tsc": "^3.2.4"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
".output",
|
|
54
|
+
"bin",
|
|
55
|
+
"README.md"
|
|
56
|
+
],
|
|
57
|
+
"packageManager": "yarn@4.12.0"
|
|
68
58
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{f as z,O as E,F as R,o as a,r as V,w as p,a as n,b as m,G as K,d as v,v as s,I as F,c as r,y as B,B as $,C as j,L as k,K as G,R as J,Q as P,H as S,J as W,e as H,t as w,S as Q,z as X,M as Y,n as q}from"./-tOYwuj2.js";import{u as L,_ as Z,a as ee,d as te,b as oe,c as se}from"./uS7FY2am.js";const ne={class:"space-y-4"},ae={key:0,class:"space-y-2"},le={class:"flex gap-2"},de={key:0,class:"text-xs text-amber-600 dark:text-amber-400"},re={key:1,class:"border border-[var(--ui-border)] rounded-md"},ie={class:"px-3 pb-3 pt-1 space-y-3"},ce={class:"flex justify-end gap-2"},ue=z({__name:"AddIntegrationModal",props:{open:{type:Boolean}},emits:["update:open","created"],async setup(O,{emit:T}){let y,x;const M=O,A=T,f=j({get:()=>M.open,set:c=>A("update:open",c)}),{data:i}=([y,x]=E(()=>L("/api/catalog","$vYIVj-kaX8")),y=await y,x(),y),g=j(()=>(i.value||[]).map(c=>({label:c.type,value:c.type}))),d=k(void 0),u=k(null),l=k([]),t=k([]),h=k(null),b=k(!1);R(f,c=>{c&&(d.value=void 0,u.value=null,l.value=[],t.value=[])}),R(d,()=>{u.value=null,l.value=[],t.value=[]}),R(()=>h.value?.toolsets,c=>{!d.value||!c?.length||l.value.length||(l.value=c.map(o=>o.key))},{deep:!0});async function N(){if(d.value){b.value=!0;try{const o=(h.value?.toolsets||[]).map(e=>e.key).filter(e=>e!=="__all__"),C=o.length&&l.value.length<o.length?l.value.filter(e=>e!=="__all__"):void 0,U=await $fetch("/api/integrations",{method:"POST",body:{type:d.value,maxScope:u.value||void 0,enabledToolsets:C,disabledTools:t.value.length?t.value:void 0}});f.value=!1,A("created",U.id)}finally{b.value=!1}}}return(c,o)=>{const C=ee,U=Z,e=te,I=K,D=G;return a(),V(D,{open:s(f),"onUpdate:open":o[6]||(o[6]=_=>F(f)?f.value=_:null),title:"Add integration",description:"Choose a service to connect."},{body:p(()=>[n("div",ne,[m(U,{label:"Integration type"},{default:p(()=>[m(C,{modelValue:s(d),"onUpdate:modelValue":o[0]||(o[0]=_=>F(d)?d.value=_:null),items:s(g),placeholder:"Select a service...",class:"w-full"},null,8,["modelValue","items"])]),_:1}),s(d)?(a(),r("div",ae,[o[7]||(o[7]=n("div",{class:"text-sm font-medium"}," Access level ",-1)),n("div",le,[n("button",{type:"button",class:B(["px-3 py-1.5 text-sm rounded-md border transition-colors",s(u)!=="read"?"border-green-500 bg-green-50 text-green-700 font-medium dark:bg-green-950 dark:text-green-300":"border-slate-200 text-slate-600 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-400"]),onClick:o[1]||(o[1]=_=>u.value=null)}," Read + Write ",2),n("button",{type:"button",class:B(["px-3 py-1.5 text-sm rounded-md border transition-colors",s(u)==="read"?"border-green-500 bg-green-50 text-green-700 font-medium dark:bg-green-950 dark:text-green-300":"border-slate-200 text-slate-600 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-400"]),onClick:o[2]||(o[2]=_=>u.value="read")}," Read-only ",2)]),s(u)==="read"?(a(),r("p",de," Write/admin tools are greyed out automatically. ")):$("",!0)])):$("",!0),s(d)?(a(),r("details",re,[o[8]||(o[8]=n("summary",{class:"cursor-pointer select-none px-3 py-2 text-sm font-medium"}," Advanced tool controls (optional) ",-1)),n("div",ie,[m(e,{ref_key:"toolsTreeRef",ref:h,"integration-type":s(d),"max-scope":s(u),"enabled-toolsets":s(l),"disabled-tools":s(t),"onUpdate:enabledToolsets":o[3]||(o[3]=_=>l.value=_),"onUpdate:disabledTools":o[4]||(o[4]=_=>t.value=_)},null,8,["integration-type","max-scope","enabled-toolsets","disabled-tools"])])])):$("",!0)])]),footer:p(()=>[n("div",ce,[m(I,{variant:"soft",color:"neutral",onClick:o[5]||(o[5]=_=>f.value=!1)},{default:p(()=>[...o[9]||(o[9]=[v(" Cancel ",-1)])]),_:1}),m(I,{disabled:!s(d)||s(b),loading:s(b),onClick:N},{default:p(()=>[...o[10]||(o[10]=[v(" Add ",-1)])]),_:1},8,["disabled","loading"])])]),_:1},8,["open"])}}}),pe=Object.assign(ue,{__name:"AddIntegrationModal"}),me={class:"flex items-center justify-between gap-4"},_e={key:0,class:"text-sm text-muted py-8 text-center"},ve={key:1,class:"text-sm text-red-600 py-8 text-center"},fe={key:2,class:"py-16 text-center space-y-4"},ye={key:3,class:"space-y-2"},xe={class:"min-w-0 flex-1"},ge={class:"flex items-center gap-2"},be={class:"font-medium truncate"},ke={class:"mt-1 flex items-center gap-2 flex-wrap"},$e={key:1,class:"text-xs text-muted"},Te={key:2,class:"text-xs text-muted"},Ce=z({__name:"index",async setup(O){let T,y;const{data:x,pending:M,error:A,refresh:f}=([T,y]=E(()=>L("/api/integrations","$3JxGA1emOo")),T=await T,y(),T),i=Y({}),g=k(!1);J(async()=>{if(x.value)for(const l of x.value){if(i[l.id]!==void 0)continue;const t=await $fetch(`/api/integrations/${l.id}/credentials-status`).catch(()=>null);i[l.id]=t?.health_status??"disconnected"}});async function d(l,t){await $fetch("/api/integrations",{method:"POST",body:{...l,enabled:t}}),await f()}function u(l){q(`/integrations/${l}`)}return(l,t)=>{const h=K,b=oe,N=se,c=X,o=H,C=pe,U=P;return a(),V(U,{class:"py-10 space-y-6"},{default:p(()=>[n("div",me,[t[5]||(t[5]=n("div",{class:"space-y-1"},[n("h1",{class:"text-2xl font-semibold"}," Integrations "),n("p",{class:"text-sm text-muted"}," Manage your connected services. ")],-1)),m(h,{icon:"i-lucide-plus",onClick:t[0]||(t[0]=e=>g.value=!0)},{default:p(()=>[...t[4]||(t[4]=[v(" Add Integration ",-1)])]),_:1})]),s(M)?(a(),r("div",_e," Loading… ")):s(A)?(a(),r("div",ve," Failed to load integrations. ")):s(x)?.length?(a(),r("div",ye,[(a(!0),r(S,null,W(s(x),e=>(a(),V(o,{key:e.id,to:`/integrations/${e.id}`,class:"flex items-center gap-4 p-4 rounded-lg border border-[var(--ui-border)] hover:bg-[var(--ui-bg-elevated)] transition-colors group"},{default:p(()=>[n("div",xe,[n("div",ge,[n("span",be,w(e.label),1),m(b,{size:"xs",color:"neutral",variant:"subtle"},{default:p(()=>[v(w(e.type),1)]),_:2},1024),e.maxScope==="read"?(a(),V(b,{key:0,size:"xs",color:"warning",variant:"soft"},{default:p(()=>[...t[8]||(t[8]=[v(" Read-only ",-1)])]),_:1})):$("",!0)]),n("div",ke,[s(i)[e.id]!==void 0?(a(),r("span",{key:0,class:B(["inline-flex items-center gap-1 text-xs font-medium px-1.5 py-0.5 rounded",{"bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-400":s(i)[e.id]==="connected","bg-red-50 text-red-700 dark:bg-red-950 dark:text-red-400":s(i)[e.id]==="invalid_credentials"||s(i)[e.id]==="disconnected"}])},[n("span",{class:B(["inline-block w-1.5 h-1.5 rounded-full",{"bg-green-500":s(i)[e.id]==="connected","bg-red-500":s(i)[e.id]==="invalid_credentials"||s(i)[e.id]==="disconnected"}])},null,2),s(i)[e.id]==="connected"?(a(),r(S,{key:0},[v("Connected")],64)):s(i)[e.id]==="invalid_credentials"?(a(),r(S,{key:1},[v("Invalid credentials")],64)):(a(),r(S,{key:2},[v("Not connected")],64))],2)):$("",!0),e.enabledToolsets?.length?(a(),r("span",$e,w(e.enabledToolsets.length)+" toolset"+w(e.enabledToolsets.length===1?"":"s"),1)):$("",!0),e.disabledTools?.length?(a(),r("span",Te,w(e.disabledTools.length)+" tool"+w(e.disabledTools.length===1?"":"s")+" blocked ",1)):$("",!0)])]),n("div",{class:"flex items-center gap-3",onClick:t[2]||(t[2]=Q(()=>{},["prevent","stop"]))},[m(N,{"model-value":e.enabled!==!1,"onUpdate:modelValue":I=>d(e,I)},null,8,["model-value","onUpdate:modelValue"])]),m(c,{name:"i-lucide-chevron-right",class:"text-muted opacity-0 group-hover:opacity-100 transition-opacity shrink-0"})]),_:2},1032,["to"]))),128))])):(a(),r("div",fe,[t[7]||(t[7]=n("div",{class:"text-muted text-sm"}," No integrations yet. ",-1)),m(h,{icon:"i-lucide-plus",variant:"soft",onClick:t[1]||(t[1]=e=>g.value=!0)},{default:p(()=>[...t[6]||(t[6]=[v(" Add your first integration ",-1)])]),_:1})])),m(C,{open:s(g),"onUpdate:open":t[3]||(t[3]=e=>F(g)?g.value=e:null),onCreated:u},null,8,["open"])]),_:1})}}});export{Ce as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"id":"b13ec2b2-ddd3-4ead-abd4-4fba9dfc0061","timestamp":1772957882894}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"id":"b13ec2b2-ddd3-4ead-abd4-4fba9dfc0061","timestamp":1772957882894,"prerendered":[]}
|