wizards 0.1.9
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/LICENSE +110 -0
- package/README.md +170 -0
- package/apps/runtime/dist/agents/architect.js +453 -0
- package/apps/runtime/dist/agents/project-assistant.js +354 -0
- package/apps/runtime/dist/agents/subscription.js +206 -0
- package/apps/runtime/dist/agents/thought.js +21 -0
- package/apps/runtime/dist/agents/website.js +197 -0
- package/apps/runtime/dist/app.js +153 -0
- package/apps/runtime/dist/auth/account.js +121 -0
- package/apps/runtime/dist/auth/index.js +329 -0
- package/apps/runtime/dist/auth/keys.js +55 -0
- package/apps/runtime/dist/auth/local-ticket.js +23 -0
- package/apps/runtime/dist/authoring/example.js +73 -0
- package/apps/runtime/dist/authoring/guide.js +259 -0
- package/apps/runtime/dist/authoring/ops.js +117 -0
- package/apps/runtime/dist/cli/autostart.js +134 -0
- package/apps/runtime/dist/cli/clients.js +44 -0
- package/apps/runtime/dist/cli/environment.js +81 -0
- package/apps/runtime/dist/cli/exec.js +45 -0
- package/apps/runtime/dist/cli/home.js +74 -0
- package/apps/runtime/dist/cli/machine.js +145 -0
- package/apps/runtime/dist/cli/main.js +125 -0
- package/apps/runtime/dist/cli/setup.js +273 -0
- package/apps/runtime/dist/cli/start.js +190 -0
- package/apps/runtime/dist/cli/status.js +120 -0
- package/apps/runtime/dist/cli/ui.js +12 -0
- package/apps/runtime/dist/cli/update.js +58 -0
- package/apps/runtime/dist/connectors/builtin.js +70 -0
- package/apps/runtime/dist/connectors/external.js +400 -0
- package/apps/runtime/dist/connectors/gmail.js +107 -0
- package/apps/runtime/dist/connectors/imap.js +217 -0
- package/apps/runtime/dist/connectors/index.js +266 -0
- package/apps/runtime/dist/connectors/mail.js +72 -0
- package/apps/runtime/dist/connectors/outlook.js +90 -0
- package/apps/runtime/dist/credits/credits.js +103 -0
- package/apps/runtime/dist/credits/estimate.js +132 -0
- package/apps/runtime/dist/db/adopt.js +149 -0
- package/apps/runtime/dist/db/client.js +146 -0
- package/apps/runtime/dist/db/control-schema.js +70 -0
- package/apps/runtime/dist/db/migrations/control/0000_last_absorbing_man.sql +47 -0
- package/apps/runtime/dist/db/migrations/control/0001_gorgeous_nicolaos.sql +41 -0
- package/apps/runtime/dist/db/migrations/control/0002_secret_penance.sql +1 -0
- package/apps/runtime/dist/db/migrations/control/0003_nasty_mordo.sql +3 -0
- package/apps/runtime/dist/db/migrations/control/0004_breezy_eddie_brock.sql +6 -0
- package/apps/runtime/dist/db/migrations/control/meta/0000_snapshot.json +311 -0
- package/apps/runtime/dist/db/migrations/control/meta/0001_snapshot.json +599 -0
- package/apps/runtime/dist/db/migrations/control/meta/0002_snapshot.json +607 -0
- package/apps/runtime/dist/db/migrations/control/meta/0003_snapshot.json +311 -0
- package/apps/runtime/dist/db/migrations/control/meta/0004_snapshot.json +350 -0
- package/apps/runtime/dist/db/migrations/control/meta/_journal.json +41 -0
- package/apps/runtime/dist/db/migrations/tenant/0000_eminent_freak.sql +188 -0
- package/apps/runtime/dist/db/migrations/tenant/0001_salty_master_chief.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0002_cloudy_firebird.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0003_brown_warbound.sql +52 -0
- package/apps/runtime/dist/db/migrations/tenant/0004_romantic_nocturne.sql +6 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0000_snapshot.json +1323 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0001_snapshot.json +1330 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0002_snapshot.json +1337 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0003_snapshot.json +1605 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0004_snapshot.json +1645 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +41 -0
- package/apps/runtime/dist/db/schema.js +278 -0
- package/apps/runtime/dist/documents/parse.js +230 -0
- package/apps/runtime/dist/engenty/connections-external/build-connector.js +193 -0
- package/apps/runtime/dist/engenty/connections-external/errors.js +11 -0
- package/apps/runtime/dist/engenty/connections-external/import-service.js +270 -0
- package/apps/runtime/dist/engenty/connections-external/importer/classify.js +34 -0
- package/apps/runtime/dist/engenty/connections-external/importer/map-auth.js +185 -0
- package/apps/runtime/dist/engenty/connections-external/importer/normalize-mcp.js +63 -0
- package/apps/runtime/dist/engenty/connections-external/importer/normalize-openapi.js +130 -0
- package/apps/runtime/dist/engenty/connections-external/invoke/http-invoker.js +143 -0
- package/apps/runtime/dist/engenty/connections-external/invoke/mcp-client.js +143 -0
- package/apps/runtime/dist/engenty/connections-external/net/guarded-fetch.js +119 -0
- package/apps/runtime/dist/engenty/connections-external/oauth-dcr.js +88 -0
- package/apps/runtime/dist/engenty/connections-external/registry-client.js +336 -0
- package/apps/runtime/dist/engenty/connections-external/registry-source.js +82 -0
- package/apps/runtime/dist/engenty/connections-external/types.js +7 -0
- package/apps/runtime/dist/engenty/connections-github/connector.js +209 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/calendar.js +359 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/contacts.js +94 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/drive.js +290 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/gmail.js +567 -0
- package/apps/runtime/dist/engenty/connections-google/definitions.js +11 -0
- package/apps/runtime/dist/engenty/connections-google/shared.js +73 -0
- package/apps/runtime/dist/engenty/connections-hubspot/connector.js +140 -0
- package/apps/runtime/dist/engenty/connections-microsoft/action.js +17 -0
- package/apps/runtime/dist/engenty/connections-microsoft/graph.js +114 -0
- package/apps/runtime/dist/engenty/connections-microsoft/onedrive.js +286 -0
- package/apps/runtime/dist/engenty/connections-microsoft/outlook.js +464 -0
- package/apps/runtime/dist/engenty/connections-s3/s3.js +328 -0
- package/apps/runtime/dist/engenty/connections-sdk/files-capability.js +86 -0
- package/apps/runtime/dist/engenty/connections-sdk/oauth2.js +150 -0
- package/apps/runtime/dist/engenty/connections-sdk/registry.js +89 -0
- package/apps/runtime/dist/engenty/connections-sdk/storage-capability.js +84 -0
- package/apps/runtime/dist/engenty/connections-sdk/types.js +30 -0
- package/apps/runtime/dist/engenty/connections-slack/connector.js +360 -0
- package/apps/runtime/dist/engenty/csv-import/csv-matrix.js +178 -0
- package/apps/runtime/dist/engenty/csv-import/import-sources.js +87 -0
- package/apps/runtime/dist/engenty/csv-import/parse-csv.js +129 -0
- package/apps/runtime/dist/engenty/csv-import/serialize-csv.js +20 -0
- package/apps/runtime/dist/engenty/csv-import/types.js +1 -0
- package/apps/runtime/dist/engenty/csv-import/xlsx-workbook.js +159 -0
- package/apps/runtime/dist/engenty/doc-converter/interface.js +9 -0
- package/apps/runtime/dist/engenty/doc-converter/page-break.js +92 -0
- package/apps/runtime/dist/engenty/doc-converter/providers/local/index.js +72 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/classifier.js +30 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/index.js +4 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/invoice.js +73 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/receipt.js +63 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/shared.js +40 -0
- package/apps/runtime/dist/engenty/search-index/chunks.js +164 -0
- package/apps/runtime/dist/engenty/search-index/contracts.js +9 -0
- package/apps/runtime/dist/engenty/shims/connections-sdk.js +39 -0
- package/apps/runtime/dist/engenty/shims/plugin-sdk.js +1 -0
- package/apps/runtime/dist/engenty/shims/web-ingest.js +3 -0
- package/apps/runtime/dist/engenty/web-ingest/ssrf.js +277 -0
- package/apps/runtime/dist/engine/asks.js +60 -0
- package/apps/runtime/dist/engine/events.js +46 -0
- package/apps/runtime/dist/engine/input.js +170 -0
- package/apps/runtime/dist/engine/prepare.js +51 -0
- package/apps/runtime/dist/engine/requirements.js +124 -0
- package/apps/runtime/dist/engine/resources.js +89 -0
- package/apps/runtime/dist/engine/runner.js +560 -0
- package/apps/runtime/dist/engine/steps.js +598 -0
- package/apps/runtime/dist/engine/template.js +183 -0
- package/apps/runtime/dist/engine/types.js +3 -0
- package/apps/runtime/dist/env.js +224 -0
- package/apps/runtime/dist/files/blobs.js +22 -0
- package/apps/runtime/dist/files/objects.js +86 -0
- package/apps/runtime/dist/files/storage.js +95 -0
- package/apps/runtime/dist/geocode.js +58 -0
- package/apps/runtime/dist/harness/claude.js +138 -0
- package/apps/runtime/dist/harness/codex.js +228 -0
- package/apps/runtime/dist/harness/cursor.js +93 -0
- package/apps/runtime/dist/harness/env.js +124 -0
- package/apps/runtime/dist/harness/gemini.js +126 -0
- package/apps/runtime/dist/harness/index.js +99 -0
- package/apps/runtime/dist/harness/model.js +267 -0
- package/apps/runtime/dist/harness/prompt.js +121 -0
- package/apps/runtime/dist/harness/terminal.js +193 -0
- package/apps/runtime/dist/harness/types.js +7 -0
- package/apps/runtime/dist/index.js +76 -0
- package/apps/runtime/dist/limits.js +57 -0
- package/apps/runtime/dist/link-preview.js +59 -0
- package/apps/runtime/dist/manage.js +151 -0
- package/apps/runtime/dist/mcp/auth.js +102 -0
- package/apps/runtime/dist/mcp/bridge.js +59 -0
- package/apps/runtime/dist/mcp/handler.js +42 -0
- package/apps/runtime/dist/mcp/scopes.js +5 -0
- package/apps/runtime/dist/mcp/tools.js +401 -0
- package/apps/runtime/dist/media/ffmpeg.js +121 -0
- package/apps/runtime/dist/media/generate.js +120 -0
- package/apps/runtime/dist/media/marking.js +121 -0
- package/apps/runtime/dist/media/transcribe.js +30 -0
- package/apps/runtime/dist/model-errors.js +3 -0
- package/apps/runtime/dist/models.js +489 -0
- package/apps/runtime/dist/plugin.js +61 -0
- package/apps/runtime/dist/render/chromium.js +85 -0
- package/apps/runtime/dist/render/convert.js +161 -0
- package/apps/runtime/dist/render/downloads.js +277 -0
- package/apps/runtime/dist/render/guard.js +25 -0
- package/apps/runtime/dist/routes/api.js +74 -0
- package/apps/runtime/dist/routes/delivery.js +52 -0
- package/apps/runtime/dist/routes/internal.js +49 -0
- package/apps/runtime/dist/routes/marketplace.js +36 -0
- package/apps/runtime/dist/routes/runs.js +805 -0
- package/apps/runtime/dist/routes/studio.js +702 -0
- package/apps/runtime/dist/routes/wizard-stream.js +30 -0
- package/apps/runtime/dist/running.js +39 -0
- package/apps/runtime/dist/sandbox/agentos.js +191 -0
- package/apps/runtime/dist/sandbox/docker.js +77 -0
- package/apps/runtime/dist/sandbox/index.js +30 -0
- package/apps/runtime/dist/sandbox/types.js +1 -0
- package/apps/runtime/dist/secrets/crypto.js +28 -0
- package/apps/runtime/dist/secrets/signing.js +34 -0
- package/apps/runtime/dist/secrets/vault.js +59 -0
- package/apps/runtime/dist/services/architect.js +92 -0
- package/apps/runtime/dist/services/brand.js +20 -0
- package/apps/runtime/dist/services/cloud.js +52 -0
- package/apps/runtime/dist/services/draft-events.js +27 -0
- package/apps/runtime/dist/services/errors.js +24 -0
- package/apps/runtime/dist/services/files.js +129 -0
- package/apps/runtime/dist/services/links.js +16 -0
- package/apps/runtime/dist/services/marketplace.js +292 -0
- package/apps/runtime/dist/services/package.js +202 -0
- package/apps/runtime/dist/services/profile.js +23 -0
- package/apps/runtime/dist/services/project-files.js +266 -0
- package/apps/runtime/dist/services/project-index.js +109 -0
- package/apps/runtime/dist/services/projects.js +181 -0
- package/apps/runtime/dist/services/runs.js +224 -0
- package/apps/runtime/dist/services/shares.js +129 -0
- package/apps/runtime/dist/services/widgets.js +34 -0
- package/apps/runtime/dist/services/wizards.js +342 -0
- package/apps/runtime/dist/settings.js +16 -0
- package/apps/runtime/dist/store/index.js +279 -0
- package/apps/runtime/dist/tenants/control.js +106 -0
- package/apps/runtime/dist/tenants/tenant.js +26 -0
- package/apps/runtime/dist/tools/browser.js +380 -0
- package/apps/runtime/dist/tools/connector.js +204 -0
- package/apps/runtime/dist/tools/index.js +234 -0
- package/apps/runtime/dist/tools/mail.js +104 -0
- package/apps/runtime/dist/tools/net-guard.js +59 -0
- package/apps/runtime/dist/tools/project.js +53 -0
- package/apps/runtime/dist/tools/shared.js +42 -0
- package/apps/runtime/dist/tools/store.js +238 -0
- package/apps/runtime/dist/update.js +118 -0
- package/apps/runtime/dist/widgets/bundle.js +99 -0
- package/apps/runtime/dist/widgets/render.js +312 -0
- package/apps/runtime/dist/widgets/runtime.js +84 -0
- package/apps/runtime/dist/widgets/script.js +30 -0
- package/apps/runtime/dist/widgets/step.js +192 -0
- package/apps/web/dist/ai-labels/ai-black.svg +20 -0
- package/apps/web/dist/ai-labels/ai-generated-black.svg +31 -0
- package/apps/web/dist/ai-labels/ai-generated-white.svg +32 -0
- package/apps/web/dist/ai-labels/ai-modified-black.svg +30 -0
- package/apps/web/dist/ai-labels/ai-modified-white.svg +31 -0
- package/apps/web/dist/ai-labels/ai-white.svg +21 -0
- package/apps/web/dist/assets/ChangelogDialog-O00vEnbm.js +1 -0
- package/apps/web/dist/assets/ChatPanel-CH9ry0Hp.js +1 -0
- package/apps/web/dist/assets/CreditsDialog-DRzecwUw.js +1 -0
- package/apps/web/dist/assets/EditorPage-BRnBf02Q.js +2 -0
- package/apps/web/dist/assets/FlowPreview-DDOj8IAH.js +1 -0
- package/apps/web/dist/assets/HomePage-CAGTTG3N.js +1 -0
- package/apps/web/dist/assets/ImportWizard-0RRrAnX3.js +1 -0
- package/apps/web/dist/assets/LocalRuntime-BrP-ENHg.css +1 -0
- package/apps/web/dist/assets/LocalRuntime-C-TBYrzs.js +16 -0
- package/apps/web/dist/assets/NewWizardPage-kI90h3_9.js +2 -0
- package/apps/web/dist/assets/PublicRunner-CKRDeIVd.js +1 -0
- package/apps/web/dist/assets/RunnerView-Rc4H8wYH.js +2 -0
- package/apps/web/dist/assets/SettingsPage-CESMuZxq.js +2 -0
- package/apps/web/dist/assets/SetupPage-D1rrYU14.js +1 -0
- package/apps/web/dist/assets/SharePage-COTAmOgh.js +1 -0
- package/apps/web/dist/assets/api-BY28cjGY.js +10 -0
- package/apps/web/dist/assets/arrow-left-UpjOBfQp.js +1 -0
- package/apps/web/dist/assets/arrow-right-CucQycEy.js +1 -0
- package/apps/web/dist/assets/changelog-p3wsaDDS.js +1 -0
- package/apps/web/dist/assets/check-Bjx41xbB.js +1 -0
- package/apps/web/dist/assets/createLucideIcon-DBu2Ovxj.js +1 -0
- package/apps/web/dist/assets/definition-cy7uvY5L.js +55 -0
- package/apps/web/dist/assets/engenty-CWIFpgKq.js +1 -0
- package/apps/web/dist/assets/esm-D1OEhjTK.js +7 -0
- package/apps/web/dist/assets/external-link-CRg6DVdO.js +1 -0
- package/apps/web/dist/assets/fluffy-engenty-CJ3wCQgT.js +456 -0
- package/apps/web/dist/assets/index-CQ_q9iMO.css +1 -0
- package/apps/web/dist/assets/index-D0gxh90t.js +10 -0
- package/apps/web/dist/assets/jsx-runtime-Dk72oS4N.js +1 -0
- package/apps/web/dist/assets/meta-BbSdtTUa.js +1 -0
- package/apps/web/dist/assets/mic-CeqQ89Dj.js +1 -0
- package/apps/web/dist/assets/oss-credits-cPuZUFHD.js +1 -0
- package/apps/web/dist/assets/outputs-D6pxH6ve.js +69 -0
- package/apps/web/dist/assets/plus-HA210wXA.js +1 -0
- package/apps/web/dist/assets/preload-helper-CNEI7N0N.js +1 -0
- package/apps/web/dist/assets/qr-scanner-worker.min-CJaXo3yu.js +98 -0
- package/apps/web/dist/assets/qr-scanner.min-C_UI0gH-.js +1 -0
- package/apps/web/dist/assets/type-BzYgVWKL.js +1 -0
- package/apps/web/dist/assets/ui-Ddwl35A6.js +1 -0
- package/apps/web/dist/assets/upload-DsgLfQ3U.js +1 -0
- package/apps/web/dist/assets/video-Dk1fuTDl.js +1 -0
- package/apps/web/dist/assets/wand-sparkles-Bf97fs8T.js +1 -0
- package/apps/web/dist/embed.js +102 -0
- package/apps/web/dist/favicon.svg +1 -0
- package/apps/web/dist/icons/apple-touch-icon.png +0 -0
- package/apps/web/dist/icons/icon-192.png +0 -0
- package/apps/web/dist/icons/icon-512.png +0 -0
- package/apps/web/dist/icons/icon-maskable-512.png +0 -0
- package/apps/web/dist/index.html +46 -0
- package/apps/web/dist/install.sh +532 -0
- package/apps/web/dist/manifest.webmanifest +20 -0
- package/apps/web/dist/sw.js +15 -0
- package/bin/wizards.mjs +13 -0
- package/node_modules/@engenty-wizards/shared/dist/connectors.d.ts +77 -0
- package/node_modules/@engenty-wizards/shared/dist/connectors.js +2 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +1415 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.js +762 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.d.ts +5 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.js +132 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.d.ts +11 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.js +209 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +532 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.js +216 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.d.ts +3 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.js +3 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.d.ts +14 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.js +166 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.d.ts +33 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.js +99 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.d.ts +35 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.js +86 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-search.d.ts +20 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-search.js +133 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +298 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace.js +211 -0
- package/node_modules/@engenty-wizards/shared/dist/merge.d.ts +7 -0
- package/node_modules/@engenty-wizards/shared/dist/merge.js +40 -0
- package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +74 -0
- package/node_modules/@engenty-wizards/shared/dist/projects.js +96 -0
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +219 -0
- package/node_modules/@engenty-wizards/shared/dist/run.js +1 -0
- package/node_modules/@engenty-wizards/shared/dist/store.d.ts +100 -0
- package/node_modules/@engenty-wizards/shared/dist/store.js +44 -0
- package/node_modules/@engenty-wizards/shared/dist/workspace.d.ts +16 -0
- package/node_modules/@engenty-wizards/shared/dist/workspace.js +51 -0
- package/node_modules/@engenty-wizards/shared/package.json +10 -0
- package/package.json +62 -0
- package/plugin/.claude-plugin/plugin.json +9 -0
- package/plugin/.mcp.json +8 -0
- package/plugin/commands/wizard.md +8 -0
- package/plugin/skills/build-wizard/SKILL.md +27 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createGuardedFetch } from "./net/guarded-fetch.js";
|
|
3
|
+
/**
|
|
4
|
+
* Client for the integrations.sh registry (payload `version: 3`). Contacted
|
|
5
|
+
* ONLY during admin search/preview/import — never at agent runtime.
|
|
6
|
+
*
|
|
7
|
+
* The registry's own wire schema is the contract: `/api/search` returns
|
|
8
|
+
* domain-level hits each carrying a `surfaces[]` summary, and
|
|
9
|
+
* `GET /api/{domain}/surface` returns the cached catalog document shown on the
|
|
10
|
+
* public page — `surfaces[]` as a discriminated union on `type` (`http`,
|
|
11
|
+
* `mcp`, `graphql`, `cli`) plus a `credentials` registry keyed by id. Parsing
|
|
12
|
+
* stays loose (unknown keys pass through) and the raw payload is persisted on
|
|
13
|
+
* the imported record for forensics, but every field this module *acts* on is
|
|
14
|
+
* validated here.
|
|
15
|
+
*
|
|
16
|
+
* `/surface` is preferred over `/discover` (live re-probe — often hangs past
|
|
17
|
+
* our request timeout while `/surface` returns in hundreds of ms).
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_REGISTRY_URL = "https://integrations.sh";
|
|
20
|
+
const REQUEST_TIMEOUT_MS = 20_000;
|
|
21
|
+
/** Remote MCP transports this module can actually speak. */
|
|
22
|
+
const SUPPORTED_MCP_TRANSPORTS = ["streamable-http", "sse"];
|
|
23
|
+
export function registryBaseUrl() {
|
|
24
|
+
return (process.env.ENGENTY_INTEGRATIONS_REGISTRY_URL ?? DEFAULT_REGISTRY_URL).replace(/\/+$/u, "");
|
|
25
|
+
}
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Wire schemas
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/** RFC 6902 operation, as the registry publishes spec corrections. */
|
|
30
|
+
const jsonPatchOperationSchema = z
|
|
31
|
+
.object({
|
|
32
|
+
from: z.string().optional(),
|
|
33
|
+
op: z.enum(["add", "remove", "replace", "move", "copy", "test"]),
|
|
34
|
+
path: z.string(),
|
|
35
|
+
value: z.unknown().optional(),
|
|
36
|
+
})
|
|
37
|
+
.loose();
|
|
38
|
+
/**
|
|
39
|
+
* How one credential rides the request. `source` decides what we can map:
|
|
40
|
+
* `http` (header/query placement) and `spec`/`well-known` (auth described by
|
|
41
|
+
* the spec or an OAuth discovery document) are actionable; `cli` and
|
|
42
|
+
* `unknown` are not.
|
|
43
|
+
*/
|
|
44
|
+
const authMechanicsSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
command: z.string().optional(),
|
|
47
|
+
env: z.array(z.string()).optional(),
|
|
48
|
+
headerName: z.string().optional(),
|
|
49
|
+
in: z.string().optional(),
|
|
50
|
+
scheme: z.string().optional(),
|
|
51
|
+
source: z.string(),
|
|
52
|
+
})
|
|
53
|
+
.loose();
|
|
54
|
+
/** One credential an entry needs; an entry's `use[]` are needed together (AND). */
|
|
55
|
+
const authUseSchema = z
|
|
56
|
+
.object({ id: z.string(), mechanics: authMechanicsSchema })
|
|
57
|
+
.loose();
|
|
58
|
+
/** `entries[]` are alternatives (OR) — any one of them authenticates. */
|
|
59
|
+
const authEntrySchema = z
|
|
60
|
+
.object({ use: z.array(authUseSchema).default([]) })
|
|
61
|
+
.loose();
|
|
62
|
+
const surfaceAuthSchema = z
|
|
63
|
+
.object({
|
|
64
|
+
entries: z.array(authEntrySchema).default([]),
|
|
65
|
+
status: z.string().default("unknown"),
|
|
66
|
+
})
|
|
67
|
+
.loose();
|
|
68
|
+
const requiredHeaderSchema = z
|
|
69
|
+
.object({
|
|
70
|
+
description: z.string().nullish(),
|
|
71
|
+
name: z.string(),
|
|
72
|
+
source: z
|
|
73
|
+
.object({ kind: z.string(), value: z.string().nullish() })
|
|
74
|
+
.loose()
|
|
75
|
+
.nullish(),
|
|
76
|
+
})
|
|
77
|
+
.loose();
|
|
78
|
+
const surfaceVariableSchema = z
|
|
79
|
+
.object({
|
|
80
|
+
description: z.string().nullish(),
|
|
81
|
+
in: z.string().nullish(),
|
|
82
|
+
name: z.string(),
|
|
83
|
+
resolveFrom: z.string().nullish(),
|
|
84
|
+
})
|
|
85
|
+
.loose();
|
|
86
|
+
const surfaceSchema = z
|
|
87
|
+
.object({
|
|
88
|
+
auth: surfaceAuthSchema.nullish(),
|
|
89
|
+
docs: z.string().nullish(),
|
|
90
|
+
name: z.string().nullish(),
|
|
91
|
+
requiredHeaders: z.array(requiredHeaderSchema).default([]),
|
|
92
|
+
slug: z.string().nullish(),
|
|
93
|
+
spec: z.string().nullish(),
|
|
94
|
+
specAlternates: z.array(z.string()).default([]),
|
|
95
|
+
specOverrides: z.array(jsonPatchOperationSchema).default([]),
|
|
96
|
+
transports: z.array(z.string()).nullish(),
|
|
97
|
+
type: z.string(),
|
|
98
|
+
url: z.string().nullish(),
|
|
99
|
+
variables: z.array(surfaceVariableSchema).default([]),
|
|
100
|
+
})
|
|
101
|
+
.loose();
|
|
102
|
+
const credentialSchema = z
|
|
103
|
+
.object({
|
|
104
|
+
generateUrl: z.string().nullish(),
|
|
105
|
+
label: z.string().nullish(),
|
|
106
|
+
setup: z.string().nullish(),
|
|
107
|
+
type: z.string(),
|
|
108
|
+
})
|
|
109
|
+
.loose();
|
|
110
|
+
/**
|
|
111
|
+
* OAuth facts probed from `/.well-known/oauth-authorization-server` &co. The
|
|
112
|
+
* `credentials` registry describes OAuth in prose only, so these endpoints are
|
|
113
|
+
* the sole machine-readable source for an authorization-code mapping.
|
|
114
|
+
*/
|
|
115
|
+
const oauthFactsSchema = z
|
|
116
|
+
.object({
|
|
117
|
+
authorizationEndpoint: z.string().optional(),
|
|
118
|
+
dcr: z.boolean().optional(),
|
|
119
|
+
grantTypes: z.array(z.string()).default([]),
|
|
120
|
+
registrationEndpoint: z.string().optional(),
|
|
121
|
+
scopes: z.array(z.string()).default([]),
|
|
122
|
+
tokenEndpoint: z.string().optional(),
|
|
123
|
+
})
|
|
124
|
+
.loose();
|
|
125
|
+
const discoverSchema = z
|
|
126
|
+
.object({
|
|
127
|
+
credentials: z.record(z.string(), credentialSchema).default({}),
|
|
128
|
+
description: z.string().nullish(),
|
|
129
|
+
detect: z
|
|
130
|
+
.object({
|
|
131
|
+
auth: z.object({ oauth: oauthFactsSchema.nullish() }).loose().nullish(),
|
|
132
|
+
})
|
|
133
|
+
.loose()
|
|
134
|
+
.nullish(),
|
|
135
|
+
domain: z.string(),
|
|
136
|
+
summary: z.string().nullish(),
|
|
137
|
+
surfaces: z.array(surfaceSchema).default([]),
|
|
138
|
+
version: z.number().optional(),
|
|
139
|
+
})
|
|
140
|
+
.loose();
|
|
141
|
+
/** Surface summary carried on a search hit (same identity, fewer facts). */
|
|
142
|
+
const searchSurfaceSchema = z
|
|
143
|
+
.object({
|
|
144
|
+
auth: z
|
|
145
|
+
.object({
|
|
146
|
+
header: z.string().nullish(),
|
|
147
|
+
kind: z.string().nullish(),
|
|
148
|
+
note: z.string().nullish(),
|
|
149
|
+
})
|
|
150
|
+
.loose()
|
|
151
|
+
.nullish(),
|
|
152
|
+
icon: z.string().nullish(),
|
|
153
|
+
kind: z.string(),
|
|
154
|
+
slug: z.string(),
|
|
155
|
+
specOverrides: z.array(jsonPatchOperationSchema).default([]),
|
|
156
|
+
url: z.string().nullish(),
|
|
157
|
+
})
|
|
158
|
+
.loose();
|
|
159
|
+
const searchResultSchema = z.object({
|
|
160
|
+
description: z.string().default(""),
|
|
161
|
+
domain: z.string(),
|
|
162
|
+
kinds: z.array(z.string()).default([]),
|
|
163
|
+
name: z.string(),
|
|
164
|
+
surfaces: z.array(searchSurfaceSchema).default([]),
|
|
165
|
+
url: z.string().default(""),
|
|
166
|
+
});
|
|
167
|
+
const searchResponseSchema = z.object({
|
|
168
|
+
results: z.array(searchResultSchema).default([]),
|
|
169
|
+
});
|
|
170
|
+
/** Parse a `/api/{domain}/surface` body. Exported so fixtures parse identically. */
|
|
171
|
+
export function parseSurfacePayload(data) {
|
|
172
|
+
return discoverSchema.parse(data);
|
|
173
|
+
}
|
|
174
|
+
/** Parse an `/api/search` body. */
|
|
175
|
+
export function parseSearchResponse(data) {
|
|
176
|
+
return searchResponseSchema.parse(data).results;
|
|
177
|
+
}
|
|
178
|
+
function normalizeSurface(surface) {
|
|
179
|
+
if (!surface.slug) {
|
|
180
|
+
// Without a slug the surface has no stable identity to persist against.
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
auth: surface.auth ?? { entries: [], status: "unknown" },
|
|
185
|
+
connect_url: surface.url ?? null,
|
|
186
|
+
docs: surface.docs ?? null,
|
|
187
|
+
kind: surface.type,
|
|
188
|
+
name: surface.name ?? null,
|
|
189
|
+
required_headers: surface.requiredHeaders,
|
|
190
|
+
slug: surface.slug,
|
|
191
|
+
spec: surface.spec ?? null,
|
|
192
|
+
spec_alternates: surface.specAlternates,
|
|
193
|
+
spec_overrides: surface.specOverrides,
|
|
194
|
+
transports: surface.transports ?? [],
|
|
195
|
+
variables: surface.variables,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/** Every slugged surface on the domain, in registry order. */
|
|
199
|
+
export function registrySurfaces(payload) {
|
|
200
|
+
return payload.surfaces
|
|
201
|
+
.map(normalizeSurface)
|
|
202
|
+
.filter((surface) => surface !== null);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Transport to speak to an MCP surface. A surface that declares transports but
|
|
206
|
+
* no remote one (stdio-only) is not importable; an undeclared transport means
|
|
207
|
+
* the registry never probed it, and streamable HTTP is the remote default the
|
|
208
|
+
* adapter falls back from anyway.
|
|
209
|
+
*/
|
|
210
|
+
export function resolveMcpTransport(surface) {
|
|
211
|
+
if (surface.transports.length === 0) {
|
|
212
|
+
return "streamable-http";
|
|
213
|
+
}
|
|
214
|
+
if (surface.transports.includes("streamable-http")) {
|
|
215
|
+
return "streamable-http";
|
|
216
|
+
}
|
|
217
|
+
return surface.transports.includes("sse") ? "sse" : null;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Registry surfaces as import candidates. HTTP surfaces need an OpenAPI spec
|
|
221
|
+
* (a bare base URL describes no operations); MCP surfaces need a supported
|
|
222
|
+
* remote transport. GraphQL and CLI surfaces are out of scope for this module
|
|
223
|
+
* and are listed with a reason rather than hidden.
|
|
224
|
+
*/
|
|
225
|
+
export function discoverImportableSources(payload) {
|
|
226
|
+
const sources = [];
|
|
227
|
+
for (const surface of registrySurfaces(payload)) {
|
|
228
|
+
if (surface.kind === "http") {
|
|
229
|
+
sources.push({
|
|
230
|
+
blocked_reason: surface.spec
|
|
231
|
+
? null
|
|
232
|
+
: "the registry lists no OpenAPI spec for this API — paste a spec URL on the Manual URL tab",
|
|
233
|
+
source_kind: "openapi",
|
|
234
|
+
source_url: surface.spec ?? surface.connect_url ?? "",
|
|
235
|
+
surface,
|
|
236
|
+
transport: null,
|
|
237
|
+
});
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (surface.kind === "mcp") {
|
|
241
|
+
const transport = resolveMcpTransport(surface);
|
|
242
|
+
// Transport first: a stdio-only server has no connect URL *because* it
|
|
243
|
+
// is not a remote server, and naming the transport says more.
|
|
244
|
+
let blocked = null;
|
|
245
|
+
if (!transport) {
|
|
246
|
+
blocked = `unsupported MCP transport (${surface.transports.join(", ")}) — only streamable-http and sse can be imported`;
|
|
247
|
+
}
|
|
248
|
+
else if (!surface.connect_url) {
|
|
249
|
+
blocked = "the registry lists no connect URL for this MCP server";
|
|
250
|
+
}
|
|
251
|
+
sources.push({
|
|
252
|
+
blocked_reason: blocked,
|
|
253
|
+
source_kind: "mcp",
|
|
254
|
+
source_url: surface.connect_url ?? "",
|
|
255
|
+
surface,
|
|
256
|
+
transport,
|
|
257
|
+
});
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
sources.push({
|
|
261
|
+
blocked_reason: `${surface.kind} surfaces cannot be imported — this module covers OpenAPI and MCP`,
|
|
262
|
+
source_kind: "openapi",
|
|
263
|
+
source_url: surface.connect_url ?? "",
|
|
264
|
+
surface,
|
|
265
|
+
transport: null,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return sources;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Re-derive the registry surface a source URL belongs to. Import calls this
|
|
272
|
+
* server-side from `domain` + `source_url` instead of trusting slug/override
|
|
273
|
+
* metadata echoed back by the client.
|
|
274
|
+
*/
|
|
275
|
+
export function findSurfaceForSource(payload, sourceUrl) {
|
|
276
|
+
const wanted = sourceUrl.trim();
|
|
277
|
+
for (const surface of registrySurfaces(payload)) {
|
|
278
|
+
const candidates = [
|
|
279
|
+
surface.spec,
|
|
280
|
+
...surface.spec_alternates,
|
|
281
|
+
surface.connect_url,
|
|
282
|
+
].filter((value) => Boolean(value));
|
|
283
|
+
if (candidates.some((value) => value === wanted)) {
|
|
284
|
+
return surface;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
/** Discovered OAuth facts (nested under `detect.auth`). */
|
|
290
|
+
export function discoverOAuthFacts(payload) {
|
|
291
|
+
return payload.detect?.auth?.oauth ?? null;
|
|
292
|
+
}
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
// Requests
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
async function fetchJson(url, fetchImpl) {
|
|
297
|
+
const response = await fetchImpl(url, {
|
|
298
|
+
headers: { accept: "application/json" },
|
|
299
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
300
|
+
});
|
|
301
|
+
if (!response.ok) {
|
|
302
|
+
throw new Error(`registry request failed (${response.status}): ${url}`);
|
|
303
|
+
}
|
|
304
|
+
return await response.json();
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* The registry host comes from `ENGENTY_INTEGRATIONS_REGISTRY_URL` — settings
|
|
308
|
+
* input, so it goes through the guard like every other outbound URL here.
|
|
309
|
+
*/
|
|
310
|
+
function defaultRegistryFetch() {
|
|
311
|
+
return createGuardedFetch();
|
|
312
|
+
}
|
|
313
|
+
export async function registrySearch(params, fetchImpl = defaultRegistryFetch()) {
|
|
314
|
+
const url = new URL(`${registryBaseUrl()}/api/search`);
|
|
315
|
+
url.searchParams.set("q", params.query);
|
|
316
|
+
if (params.kind) {
|
|
317
|
+
url.searchParams.set("kind", params.kind);
|
|
318
|
+
}
|
|
319
|
+
url.searchParams.set("limit", String(params.limit ?? 20));
|
|
320
|
+
const data = await fetchJson(url.toString(), fetchImpl);
|
|
321
|
+
return parseSearchResponse(data);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Load the registry's cached surface document for a domain (same facts as
|
|
325
|
+
* https://integrations.sh/{domain}/). Named "discover" for historical call
|
|
326
|
+
* sites; the upstream path is `/surface`, not the live `/discover` probe.
|
|
327
|
+
*/
|
|
328
|
+
export async function registryDiscover(domain, fetchImpl = defaultRegistryFetch()) {
|
|
329
|
+
const safeDomain = encodeURIComponent(domain.trim().toLowerCase());
|
|
330
|
+
const data = await fetchJson(`${registryBaseUrl()}/api/${safeDomain}/surface`, fetchImpl);
|
|
331
|
+
return {
|
|
332
|
+
parsed: parseSurfacePayload(data),
|
|
333
|
+
raw: (data ?? {}),
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
export { SUPPORTED_MCP_TRANSPORTS };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { findSurfaceForSource, registryDiscover, registrySearch, } from "./registry-client.js";
|
|
2
|
+
/**
|
|
3
|
+
* The registry publishes a service's surfaces twice: the per-domain catalog
|
|
4
|
+
* document (`/api/{domain}/surface`) carries the rich facts — required
|
|
5
|
+
* headers, URL variables, transports, credential mechanics — while the search
|
|
6
|
+
* catalog carries a summary per surface, and today it is the search view that
|
|
7
|
+
* carries the `specOverrides` corrections. Neither is a superset.
|
|
8
|
+
*
|
|
9
|
+
* `resolveRegistrySource` takes the two facts the caller is allowed to supply
|
|
10
|
+
* — domain and source URL — and re-derives one surface from both views by
|
|
11
|
+
* exact URL match. Nothing is matched fuzzily: a correction published against
|
|
12
|
+
* one spec URL is never applied to a different one.
|
|
13
|
+
*/
|
|
14
|
+
/** Catalog `kind` values mapped onto surface-document `type` values. */
|
|
15
|
+
function catalogKindToSurfaceKind(kind) {
|
|
16
|
+
return kind === "openapi" ? "http" : kind;
|
|
17
|
+
}
|
|
18
|
+
/** A search-catalog surface expressed in the surface-document shape. */
|
|
19
|
+
function surfaceFromCatalog(entry) {
|
|
20
|
+
if (!entry.url) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const kind = catalogKindToSurfaceKind(entry.kind);
|
|
24
|
+
return {
|
|
25
|
+
auth: {
|
|
26
|
+
entries: [],
|
|
27
|
+
// The catalog states only whether a credential is needed, never how it
|
|
28
|
+
// rides the request — `mapAuth` falls back to the spec and OAuth facts.
|
|
29
|
+
status: entry.auth?.kind === "none" ? "none" : "unknown",
|
|
30
|
+
},
|
|
31
|
+
connect_url: kind === "http" ? null : entry.url,
|
|
32
|
+
docs: null,
|
|
33
|
+
kind,
|
|
34
|
+
name: null,
|
|
35
|
+
required_headers: [],
|
|
36
|
+
slug: entry.slug,
|
|
37
|
+
spec: kind === "http" ? entry.url : null,
|
|
38
|
+
spec_alternates: [],
|
|
39
|
+
spec_overrides: entry.specOverrides,
|
|
40
|
+
transports: [],
|
|
41
|
+
variables: [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function catalogSurfacesForDomain(results, domain) {
|
|
45
|
+
const wanted = domain.trim().toLowerCase();
|
|
46
|
+
return results
|
|
47
|
+
.filter((result) => result.domain.trim().toLowerCase() === wanted)
|
|
48
|
+
.flatMap((result) => result.surfaces);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the registry surface behind `sourceUrl`. Registry lookups are best
|
|
52
|
+
* effort: a registry outage degrades an import to a plain URL import rather
|
|
53
|
+
* than failing it.
|
|
54
|
+
*/
|
|
55
|
+
export async function resolveRegistrySource(params) {
|
|
56
|
+
const [discover, searchResults] = await Promise.all([
|
|
57
|
+
registryDiscover(params.domain, params.fetchImpl).catch(() => null),
|
|
58
|
+
registrySearch({ limit: 10, query: params.domain }, params.fetchImpl).catch(() => []),
|
|
59
|
+
]);
|
|
60
|
+
const documentSurface = discover
|
|
61
|
+
? findSurfaceForSource(discover.parsed, params.sourceUrl)
|
|
62
|
+
: null;
|
|
63
|
+
const catalogEntry = catalogSurfacesForDomain(searchResults, params.domain).find((entry) => entry.url === params.sourceUrl.trim());
|
|
64
|
+
if (!documentSurface) {
|
|
65
|
+
return {
|
|
66
|
+
discover,
|
|
67
|
+
surface: catalogEntry ? surfaceFromCatalog(catalogEntry) : null,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (documentSurface.spec_overrides.length > 0 || !catalogEntry) {
|
|
71
|
+
return { discover, surface: documentSurface };
|
|
72
|
+
}
|
|
73
|
+
// Same URL in both views: take the document's facts and the catalog's
|
|
74
|
+
// corrections.
|
|
75
|
+
return {
|
|
76
|
+
discover,
|
|
77
|
+
surface: {
|
|
78
|
+
...documentSurface,
|
|
79
|
+
spec_overrides: catalogEntry.specOverrides,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared shapes for imported external connectors. The importer normalizes an
|
|
3
|
+
* OpenAPI spec or MCP tool list into `NormalizedAction[]` at import time; the
|
|
4
|
+
* stored record is the single source the boot path registers from — no spec
|
|
5
|
+
* re-parsing (and no executor/Effect code) runs at boot or execute time.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { defineConnector, } from "../shims/connections-sdk.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* The GitHub connector exposes the repository + pull-request surface the hosted
|
|
5
|
+
* coder needs. Deliberately NARROW in v1:
|
|
6
|
+
*
|
|
7
|
+
* - read actions (`repos_list`, `repo_get`, `pr_get`) are auto-allowed and,
|
|
8
|
+
* because they are low-risk, are reachable from Code Mode's read-only execute.
|
|
9
|
+
* - `pr_create` is the sole `write` action (approval-gated by the connections
|
|
10
|
+
* policy) — the human sign-off on delivering a coder's work as a PR.
|
|
11
|
+
*
|
|
12
|
+
* The clone/push CREDENTIAL is intentionally NOT a connector action: read
|
|
13
|
+
* actions are reachable from Code Mode (any low-risk catalog op can be invoked
|
|
14
|
+
* there), so returning a raw token as an action output would leak it into an
|
|
15
|
+
* agent-visible surface. The coder's repo checkout obtains its token through a
|
|
16
|
+
* dedicated, non-gateway core route instead (see PLAN-engenty-coder Phase 2).
|
|
17
|
+
*
|
|
18
|
+
* The single `repo` OAuth scope is requested up front (via `baseScopes`) so
|
|
19
|
+
* enabling `pr_create` later never forces a re-consent.
|
|
20
|
+
*/
|
|
21
|
+
const GITHUB_API_BASE = "https://api.github.com";
|
|
22
|
+
const GITHUB_API_VERSION = "2022-11-28";
|
|
23
|
+
/** GitHub returns application-level failures as non-2xx with a JSON `message`. */
|
|
24
|
+
async function githubApi(ctx, path, init) {
|
|
25
|
+
const response = await ctx.fetchImpl(`${GITHUB_API_BASE}${path}`, {
|
|
26
|
+
method: init?.method ?? "GET",
|
|
27
|
+
headers: {
|
|
28
|
+
accept: "application/vnd.github+json",
|
|
29
|
+
authorization: `Bearer ${ctx.accessToken}`,
|
|
30
|
+
"x-github-api-version": GITHUB_API_VERSION,
|
|
31
|
+
...(init?.body === undefined
|
|
32
|
+
? {}
|
|
33
|
+
: { "content-type": "application/json" }),
|
|
34
|
+
},
|
|
35
|
+
...(init?.body === undefined ? {} : { body: JSON.stringify(init.body) }),
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
let message = `${response.status}`;
|
|
39
|
+
try {
|
|
40
|
+
const data = (await response.json());
|
|
41
|
+
if (data.message) {
|
|
42
|
+
message = `${data.message} (${response.status})`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Non-JSON error body; fall back to the status code.
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`github_api_error: ${message}`);
|
|
49
|
+
}
|
|
50
|
+
return (await response.json());
|
|
51
|
+
}
|
|
52
|
+
function mapRepo(repo) {
|
|
53
|
+
return {
|
|
54
|
+
default_branch: repo.default_branch ?? "",
|
|
55
|
+
description: repo.description ?? null,
|
|
56
|
+
full_name: repo.full_name ?? "",
|
|
57
|
+
html_url: repo.html_url ?? "",
|
|
58
|
+
private: repo.private ?? false,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function mapPull(pull) {
|
|
62
|
+
return {
|
|
63
|
+
base: pull.base?.ref ?? "",
|
|
64
|
+
head: pull.head?.ref ?? "",
|
|
65
|
+
html_url: pull.html_url ?? "",
|
|
66
|
+
merged: pull.merged ?? false,
|
|
67
|
+
number: pull.number ?? 0,
|
|
68
|
+
state: pull.state ?? "",
|
|
69
|
+
title: pull.title ?? "",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const repoFullName = z
|
|
73
|
+
.string()
|
|
74
|
+
.regex(/^[^/\s]+\/[^/\s]+$/, "Expected 'owner/repo'.")
|
|
75
|
+
.describe("Repository in 'owner/repo' form (e.g. engenty/engenty).");
|
|
76
|
+
const listReposInput = z.object({
|
|
77
|
+
limit: z
|
|
78
|
+
.number()
|
|
79
|
+
.int()
|
|
80
|
+
.min(1)
|
|
81
|
+
.max(50)
|
|
82
|
+
.optional()
|
|
83
|
+
.describe("Maximum repositories to return (1-50, default 30)."),
|
|
84
|
+
});
|
|
85
|
+
const getRepoInput = z.object({ repo: repoFullName });
|
|
86
|
+
const getPullInput = z.object({
|
|
87
|
+
repo: repoFullName,
|
|
88
|
+
number: z.number().int().positive().describe("Pull request number."),
|
|
89
|
+
});
|
|
90
|
+
const createPullInput = z.object({
|
|
91
|
+
repo: repoFullName,
|
|
92
|
+
title: z.string().min(1).describe("Pull request title."),
|
|
93
|
+
head: z
|
|
94
|
+
.string()
|
|
95
|
+
.min(1)
|
|
96
|
+
.describe("Name of the branch with the changes (e.g. coder/task-123)."),
|
|
97
|
+
base: z
|
|
98
|
+
.string()
|
|
99
|
+
.min(1)
|
|
100
|
+
.describe("Branch to merge into (e.g. the repository default branch)."),
|
|
101
|
+
body: z.string().optional().describe("Pull request description (Markdown)."),
|
|
102
|
+
draft: z
|
|
103
|
+
.boolean()
|
|
104
|
+
.optional()
|
|
105
|
+
.describe("Open the pull request as a draft (default false)."),
|
|
106
|
+
});
|
|
107
|
+
const actions = [
|
|
108
|
+
{
|
|
109
|
+
description: "List repositories the connected GitHub account can access, most recently updated first.",
|
|
110
|
+
group: "read",
|
|
111
|
+
handler: async (input, ctx) => {
|
|
112
|
+
const args = listReposInput.parse(input);
|
|
113
|
+
const repos = await githubApi(ctx, `/user/repos?per_page=${args.limit ?? 30}&sort=updated&affiliation=owner,collaborator,organization_member`);
|
|
114
|
+
return { repositories: repos.map(mapRepo) };
|
|
115
|
+
},
|
|
116
|
+
id: "repos_list",
|
|
117
|
+
inputSchema: listReposInput,
|
|
118
|
+
providerScopes: [],
|
|
119
|
+
summary: "List GitHub repositories",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
description: "Fetch metadata for a single repository (default branch, visibility).",
|
|
123
|
+
group: "read",
|
|
124
|
+
handler: async (input, ctx) => {
|
|
125
|
+
const args = getRepoInput.parse(input);
|
|
126
|
+
const repo = await githubApi(ctx, `/repos/${args.repo}`);
|
|
127
|
+
return mapRepo(repo);
|
|
128
|
+
},
|
|
129
|
+
id: "repo_get",
|
|
130
|
+
inputSchema: getRepoInput,
|
|
131
|
+
providerScopes: [],
|
|
132
|
+
summary: "Get a GitHub repository",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
description: "Fetch a pull request by number (state, branches, merge status, URL).",
|
|
136
|
+
group: "read",
|
|
137
|
+
handler: async (input, ctx) => {
|
|
138
|
+
const args = getPullInput.parse(input);
|
|
139
|
+
const pull = await githubApi(ctx, `/repos/${args.repo}/pulls/${args.number}`);
|
|
140
|
+
return mapPull(pull);
|
|
141
|
+
},
|
|
142
|
+
id: "pr_get",
|
|
143
|
+
inputSchema: getPullInput,
|
|
144
|
+
providerScopes: [],
|
|
145
|
+
summary: "Get a GitHub pull request",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
description: "Open a pull request from an already-pushed head branch into a base branch.",
|
|
149
|
+
group: "write",
|
|
150
|
+
handler: async (input, ctx) => {
|
|
151
|
+
const args = createPullInput.parse(input);
|
|
152
|
+
const pull = await githubApi(ctx, `/repos/${args.repo}/pulls`, {
|
|
153
|
+
method: "POST",
|
|
154
|
+
body: {
|
|
155
|
+
title: args.title,
|
|
156
|
+
head: args.head,
|
|
157
|
+
base: args.base,
|
|
158
|
+
...(args.body === undefined ? {} : { body: args.body }),
|
|
159
|
+
...(args.draft === undefined ? {} : { draft: args.draft }),
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
return mapPull(pull);
|
|
163
|
+
},
|
|
164
|
+
id: "pr_create",
|
|
165
|
+
inputSchema: createPullInput,
|
|
166
|
+
providerScopes: [],
|
|
167
|
+
summary: "Create a GitHub pull request",
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
export const githubConnector = defineConnector({
|
|
171
|
+
actions,
|
|
172
|
+
auth: {
|
|
173
|
+
kind: "oauth2",
|
|
174
|
+
oauth2: {
|
|
175
|
+
authUrl: "https://github.com/login/oauth/authorize",
|
|
176
|
+
// `repo` up front so enabling pr_create later never forces re-consent.
|
|
177
|
+
baseScopes: ["repo"],
|
|
178
|
+
clientIdEnv: "GITHUB_OAUTH_CLIENT_ID",
|
|
179
|
+
clientSecretEnv: "GITHUB_OAUTH_CLIENT_SECRET",
|
|
180
|
+
resolveAccount: async (accessToken, fetchImpl) => {
|
|
181
|
+
const response = await fetchImpl(`${GITHUB_API_BASE}/user`, {
|
|
182
|
+
headers: {
|
|
183
|
+
accept: "application/vnd.github+json",
|
|
184
|
+
authorization: `Bearer ${accessToken}`,
|
|
185
|
+
"x-github-api-version": GITHUB_API_VERSION,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
if (!response.ok) {
|
|
189
|
+
throw new Error(`github_http_error: /user returned ${response.status}`);
|
|
190
|
+
}
|
|
191
|
+
const data = (await response.json());
|
|
192
|
+
return {
|
|
193
|
+
label: data.login ?? "GitHub",
|
|
194
|
+
...(data.id ? { externalId: String(data.id) } : {}),
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
scopeSeparator: " ",
|
|
198
|
+
// GitHub returns a form-encoded token body unless JSON is requested.
|
|
199
|
+
tokenRequestHeaders: { accept: "application/json" },
|
|
200
|
+
tokenUrl: "https://github.com/login/oauth/access_token",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
description: "GitHub access as the connected account: repositories and pull requests.",
|
|
204
|
+
icon: "logo:github",
|
|
205
|
+
id: "github",
|
|
206
|
+
moduleId: "connections-github",
|
|
207
|
+
name: "GitHub",
|
|
208
|
+
toolPrefix: "github",
|
|
209
|
+
});
|