@zackbart/connecta 0.28.0 → 0.29.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/CHANGELOG.md +961 -5
- package/README.md +62 -39
- package/bin/connecta.mjs +110 -139
- package/bin/meta-tool-names.d.mts +2 -0
- package/bin/meta-tool-names.mjs +9 -0
- package/bin/version.mjs +3 -0
- package/dist/access-tokens.js +53 -63
- package/dist/activity-request.js +8 -0
- package/dist/activity.d.ts +76 -42
- package/dist/activity.js +110 -42
- package/dist/auth/clerk-sdk/client.d.ts +65 -0
- package/dist/auth/clerk-sdk/client.js +5 -0
- package/dist/auth/clerk-sdk/generated.js +1845 -0
- package/dist/auth/clerk-transport.js +19 -0
- package/dist/auth/clerk.d.ts +17 -7
- package/dist/auth/clerk.js +189 -107
- package/dist/auth/cloudflare-access.js +25 -26
- package/dist/auth/downstream-client-metadata.d.ts +29 -0
- package/dist/auth/downstream-client-metadata.js +133 -0
- package/dist/auth/downstream-oauth.js +1632 -1509
- package/dist/auth/downstream-revocation.js +106 -0
- package/dist/auth/oauth-token-response.js +231 -0
- package/dist/auth/oauth-v2-migration.js +116 -0
- package/dist/auth/static-oauth.js +526 -0
- package/dist/auth-elicitation.js +283 -0
- package/dist/branding.d.ts +8 -16
- package/dist/branding.js +26 -59
- package/dist/byte-read-response.js +39 -0
- package/dist/call-admission.js +12 -16
- package/dist/catalog-cache.js +739 -0
- package/dist/catalog-drift.d.ts +50 -15
- package/dist/catalog-drift.js +365 -128
- package/dist/catalog-fingerprint.js +2 -4
- package/dist/catalog-freshness.js +13 -0
- package/dist/catalog-limits.js +2 -2
- package/dist/catalog-service.js +368 -224
- package/dist/catalog.js +43 -84
- package/dist/concurrency.js +3 -4
- package/dist/config-defaults.d.ts +39 -0
- package/dist/config-defaults.js +46 -0
- package/dist/config-schema.d.ts +181 -0
- package/dist/config-schema.js +435 -0
- package/dist/config-value-sources.js +102 -0
- package/dist/config.d.ts +631 -0
- package/dist/config.js +503 -0
- package/dist/connector-access.d.ts +1 -0
- package/dist/connector-access.js +13 -15
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +15 -6
- package/dist/connector-scope.js +1 -1
- package/dist/connectors/api-connector.d.ts +220 -0
- package/dist/connectors/api-connector.js +214 -0
- package/dist/connectors/api.d.ts +8 -71
- package/dist/connectors/api.js +13 -99
- package/dist/connectors/guarded-fetch.d.ts +144 -0
- package/dist/connectors/guarded-fetch.js +108 -25
- package/dist/connectors/negotiation-cache.js +46 -0
- package/dist/connectors/option-shapes.js +27 -0
- package/dist/connectors/remote-mcp.d.ts +30 -4
- package/dist/connectors/remote-mcp.js +1780 -351
- package/dist/connectors/resource-uri.js +335 -0
- package/dist/credential-contract.d.ts +10 -0
- package/dist/credential-rules.js +1 -3
- package/dist/credential-url.js +108 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +27 -18
- package/dist/d1.d.ts +130 -0
- package/dist/d1.js +290 -0
- package/dist/describe-config.d.ts +165 -0
- package/dist/describe-config.js +315 -0
- package/dist/described.js +106 -0
- package/dist/downstream-input-context.js +119 -0
- package/dist/downstream-input.js +323 -0
- package/dist/errors.d.ts +97 -20
- package/dist/errors.js +158 -80
- package/dist/execute.js +453 -305
- package/dist/executor-admission.d.ts +1 -0
- package/dist/executor-admission.js +11 -15
- package/dist/executor-contract.d.ts +20 -0
- package/dist/executor-contract.js +48 -0
- package/dist/executor-result.js +24 -9
- package/dist/executors/quickjs-child.js +3 -4
- package/dist/executors/quickjs-runtime.js +147 -59
- package/dist/executors/quickjs.js +29 -35
- package/dist/favicon.js +1 -2
- package/dist/fix-prompt.js +1 -3
- package/dist/guest-runtime.js +188 -0
- package/dist/guest-types.d.ts +176 -0
- package/dist/guest-types.js +1 -0
- package/dist/html-security.js +24 -0
- package/dist/identity.js +1 -3
- package/dist/inbound-credential.js +52 -0
- package/dist/index.d.ts +25 -257
- package/dist/index.js +65 -478
- package/dist/invocation-auth.js +69 -0
- package/dist/invocation.js +230 -73
- package/dist/json-schema.d.ts +1 -1
- package/dist/mcp-client-context.js +35 -0
- package/dist/mcp-result.js +11 -7
- package/dist/meta-output.js +76 -0
- package/dist/meta-tool-names.d.ts +2 -0
- package/dist/meta-tool-names.js +3 -0
- package/dist/meta-tools.js +285 -209
- package/dist/module-contracts.d.ts +8 -23
- package/dist/node.d.ts +0 -2
- package/dist/node.js +2 -2
- package/dist/oauth-callback-outcome.js +1 -3
- package/dist/oauth-handoff.js +117 -0
- package/dist/oauth-partition.js +17 -0
- package/dist/oauth-sealing.js +20 -0
- package/dist/operator-record.d.ts +137 -0
- package/dist/operator-record.js +420 -0
- package/dist/operator-ui/assets.js +31 -0
- package/dist/operator-ui/contract.d.ts +72 -0
- package/dist/operator-ui/contract.js +1 -0
- package/dist/operator-ui/fix-prompts.d.ts +18 -0
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/model.d.ts +12 -87
- package/dist/operator-ui/model.js +2 -9
- package/dist/operator-ui/setup-commands.js +1 -3
- package/dist/operator-ui/view.d.ts +323 -0
- package/dist/operator-ui/view.js +62 -189
- package/dist/page-styles.js +2 -3
- package/dist/program-source.js +14 -2
- package/dist/{exempt-writes.js → program-writes.js} +55 -18
- package/dist/provider.d.ts +142 -0
- package/dist/provider.js +187 -0
- package/dist/providers/_shared/google/result-size.js +57 -0
- package/dist/providers/_shared/google/service-account.d.ts +58 -0
- package/dist/providers/_shared/google/service-account.js +500 -0
- package/dist/providers/_shared/google/workspace.d.ts +276 -0
- package/dist/providers/_shared/google/workspace.js +518 -0
- package/dist/providers/basecamp/index.d.ts +43 -0
- package/dist/providers/basecamp/index.js +997 -0
- package/dist/providers/basecamp/skill.generated.js +9 -0
- package/dist/providers/breeze/index.d.ts +34 -0
- package/dist/providers/breeze/index.js +1446 -0
- package/dist/providers/breeze/skill.generated.js +9 -0
- package/dist/providers/ccb/index.d.ts +103 -0
- package/dist/providers/ccb/index.js +1725 -0
- package/dist/providers/ccb/skill.generated.js +9 -0
- package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
- package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
- package/dist/providers/cloudflare/skill.generated.js +10 -0
- package/dist/providers/docs/index.d.ts +54 -0
- package/dist/providers/docs/index.js +1237 -0
- package/dist/providers/docs/skill.generated.js +9 -0
- package/dist/providers/drive/index.d.ts +53 -0
- package/dist/providers/drive/index.js +1523 -0
- package/dist/providers/drive/skill.generated.js +9 -0
- package/dist/providers/forms/index.d.ts +54 -0
- package/dist/providers/forms/index.js +1217 -0
- package/dist/providers/forms/skill.generated.js +11 -0
- package/dist/providers/github/auth.d.ts +32 -0
- package/dist/providers/github/auth.js +361 -0
- package/dist/providers/github/hosted.js +218 -0
- package/dist/providers/github/index.d.ts +13 -0
- package/dist/providers/github/index.js +140 -0
- package/dist/providers/github/key.js +100 -0
- package/dist/providers/github/rest.js +321 -0
- package/dist/providers/github/scope.d.ts +29 -0
- package/dist/providers/github/scope.js +124 -0
- package/dist/providers/github/skill.generated.js +8 -0
- package/dist/providers/gmail/index.d.ts +51 -0
- package/dist/providers/gmail/index.js +1436 -0
- package/dist/providers/gmail/skill.generated.js +9 -0
- package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
- package/dist/providers/linear/index.js +352 -0
- package/dist/providers/linear/skill.generated.js +8 -0
- package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
- package/dist/providers/mixpanel/index.js +416 -0
- package/dist/providers/mixpanel/skill.generated.js +9 -0
- package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
- package/dist/providers/{notion.js → notion/index.js} +226 -239
- package/dist/providers/notion/skill.generated.js +10 -0
- package/dist/providers/overflow/index.d.ts +60 -0
- package/dist/providers/overflow/index.js +1535 -0
- package/dist/providers/overflow/skill.generated.js +8 -0
- package/dist/providers/planning-center/index.d.ts +59 -0
- package/dist/providers/planning-center/index.js +2199 -0
- package/dist/providers/planning-center/skill.generated.js +10 -0
- package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
- package/dist/providers/revenuecat/index.js +557 -0
- package/dist/providers/revenuecat/skill.generated.js +11 -0
- package/dist/providers/sheets/index.d.ts +54 -0
- package/dist/providers/sheets/index.js +1237 -0
- package/dist/providers/sheets/skill.generated.js +10 -0
- package/dist/providers/slides/index.d.ts +53 -0
- package/dist/providers/slides/index.js +2645 -0
- package/dist/providers/slides/skill.generated.js +9 -0
- package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
- package/dist/providers/stripe/index.js +266 -0
- package/dist/providers/stripe/skill.generated.js +11 -0
- package/dist/providers/tithely/index.d.ts +49 -0
- package/dist/providers/tithely/index.js +1003 -0
- package/dist/providers/tithely/skill.generated.js +8 -0
- package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
- package/dist/providers/vercel/index.js +1038 -0
- package/dist/providers/vercel/mcp-ownership.js +212 -0
- package/dist/providers/vercel/skill.generated.js +11 -0
- package/dist/registry.d.ts +108 -171
- package/dist/registry.js +513 -925
- package/dist/request-state.js +21 -0
- package/dist/resource-read.js +100 -0
- package/dist/result-shapes.js +17 -49
- package/dist/retired-toolkits.js +1 -1
- package/dist/routes/access-tokens.js +3 -6
- package/dist/routes/activity-context.js +21 -0
- package/dist/routes/activity-disclosure.js +40 -0
- package/dist/routes/activity.js +59 -21
- package/dist/routes/actor-labels.js +3 -50
- package/dist/routes/connect.js +221 -0
- package/dist/routes/credentials.js +15 -27
- package/dist/routes/mcp.js +356 -130
- package/dist/routes/oauth-client-metadata.js +15 -0
- package/dist/routes/oauth-management.js +45 -107
- package/dist/routes/oauth.js +136 -57
- package/dist/routes/operator.js +3 -3
- package/dist/routes/shared.d.ts +25 -64
- package/dist/routes/shared.js +160 -80
- package/dist/routes/skills.js +30 -0
- package/dist/routes/ui-config.js +225 -0
- package/dist/routes/ui-data.js +40 -35
- package/dist/routes/ui.js +66 -56
- package/dist/runtime/admission.js +4 -2
- package/dist/runtime/call-admission.js +3 -1
- package/dist/runtime/connector-scope.js +1 -4
- package/dist/runtime/run.js +10 -39
- package/dist/runtime/services.js +12 -117
- package/dist/runtime/shared-read.js +1 -3
- package/dist/runtime/storage.js +8 -1
- package/dist/sent-secrets.d.ts +52 -0
- package/dist/sent-secrets.js +474 -0
- package/dist/server.js +28 -71
- package/dist/skills.js +406 -174
- package/dist/sqlite.d.ts +48 -0
- package/dist/sqlite.js +166 -0
- package/dist/storage/keys.js +428 -0
- package/dist/storage/memory.js +7 -0
- package/dist/storage/sql.d.ts +66 -0
- package/dist/storage/sql.js +534 -0
- package/dist/timeout.js +4 -2
- package/dist/tool-name.js +17 -0
- package/dist/tool-safety.d.ts +9 -35
- package/dist/tool-safety.js +20 -40
- package/dist/types.d.ts +320 -54
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +7 -19
- package/dist/ui.js +76 -90
- package/dist/url-safety.js +3 -6
- package/dist/usage-guide.js +137 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +309 -47
- package/dist/version.d.ts +1 -7
- package/dist/version.js +3 -7
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +434 -0
- package/documentation/architecture.md +665 -230
- package/documentation/auth.md +962 -458
- package/documentation/code-mode.md +501 -312
- package/documentation/meta-tools.md +449 -200
- package/documentation/operator-tests.md +93 -0
- package/documentation/provider-migration-0.29.md +60 -0
- package/examples/worker/AGENTS.md +34 -12
- package/examples/worker/README.md +286 -255
- package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
- package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
- package/examples/worker/src/connecta.config.ts +151 -0
- package/examples/worker/src/index.ts +14 -197
- package/examples/worker/wrangler.jsonc +15 -35
- package/package.json +125 -35
- package/templates/node/.dockerignore +1 -2
- package/templates/node/.env.example +20 -21
- package/templates/node/AGENTS.md +21 -17
- package/templates/node/Dockerfile +3 -5
- package/templates/node/README.md +133 -58
- package/templates/node/docker-compose.yml +11 -12
- package/templates/node/package.json +4 -2
- package/templates/node/src/connecta.config.ts +119 -0
- package/templates/node/src/index.ts +9 -154
- package/templates/node/src/provision-token.ts +17 -0
- package/AGENTS.md +0 -170
- package/dist/artifacts/connector.d.ts +0 -58
- package/dist/artifacts/connector.js +0 -730
- package/dist/artifacts/document.js +0 -86
- package/dist/artifacts/guide.js +0 -140
- package/dist/artifacts/html-scan.js +0 -329
- package/dist/artifacts/json.js +0 -78
- package/dist/artifacts/kv-store.d.ts +0 -20
- package/dist/artifacts/kv-store.js +0 -140
- package/dist/artifacts/markdown.js +0 -478
- package/dist/artifacts/module.d.ts +0 -37
- package/dist/artifacts/module.js +0 -66
- package/dist/artifacts/operations.d.ts +0 -197
- package/dist/artifacts/operations.js +0 -1002
- package/dist/artifacts/refresh.d.ts +0 -55
- package/dist/artifacts/refresh.js +0 -236
- package/dist/artifacts/routes.js +0 -206
- package/dist/artifacts/types.d.ts +0 -214
- package/dist/artifacts/types.js +0 -22
- package/dist/artifacts/validate.d.ts +0 -62
- package/dist/artifacts/validate.js +0 -651
- package/dist/artifacts.d.ts +0 -6
- package/dist/artifacts.js +0 -9
- package/dist/auth/bearer.d.ts +0 -11
- package/dist/auth/bearer.js +0 -50
- package/dist/providers/linear.js +0 -217
- package/dist/providers/mixpanel.js +0 -247
- package/dist/providers/revenuecat.js +0 -317
- package/dist/providers/stripe.js +0 -253
- package/dist/providers/vercel.js +0 -1253
- package/dist/storage/file.d.ts +0 -15
- package/dist/storage/file.js +0 -455
- package/ethos.md +0 -93
- package/examples/worker/src/cloudflare-kv.ts +0 -43
- package/examples/worker/src/d1-activity-row.ts +0 -155
- package/examples/worker/src/d1-activity.ts +0 -147
- package/examples/worker/src/d1-storage.ts +0 -138
- package/examples/worker/src/r2-artifact-blobs.ts +0 -29
- package/templates/node/src/file-activity.ts +0 -182
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ writing code instead of loading a thousand tool definitions.
|
|
|
10
10
|
You ask your agent a question that touches a service — Linear, Stripe, an
|
|
11
11
|
internal API, anything you have connected. Here is what happens:
|
|
12
12
|
|
|
13
|
-
1. The agent talks to one endpoint, yours, and sees
|
|
13
|
+
1. The agent talks to one endpoint, yours, and sees six tools. Always six,
|
|
14
14
|
no matter how many services sit behind it.
|
|
15
15
|
2. It writes a short JavaScript program. Connecta runs it in a sandbox next to
|
|
16
16
|
your integrations. The program can search for tools, call them, chain the
|
|
@@ -18,10 +18,9 @@ internal API, anything you have connected. Here is what happens:
|
|
|
18
18
|
3. Only the answer comes back into the agent's context — not raw pages of
|
|
19
19
|
API output.
|
|
20
20
|
4. If the agent wants to change something — create, update, delete — it
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
your MCP client can put in front of you first.
|
|
21
|
+
can do that from a program in a `trusted` pool. A `read-only` pool
|
|
22
|
+
routes each write through `call_destructive_tool`. The host controls approval; trusted endpoints annotate `execute_code` as a
|
|
23
|
+
write. Pool trust defaults to `read-only`.
|
|
25
24
|
|
|
26
25
|
Credentials never leave the server. The program never sees them, and neither
|
|
27
26
|
does the agent.
|
|
@@ -30,8 +29,8 @@ does the agent.
|
|
|
30
29
|
flowchart TB
|
|
31
30
|
Client["Your MCP client<br/>Claude, Cursor, …"]
|
|
32
31
|
|
|
33
|
-
subgraph Connecta["Connecta — one endpoint,
|
|
34
|
-
Sandbox["execute_code<br/>the agent's program runs here<br/>
|
|
32
|
+
subgraph Connecta["Connecta — one endpoint, six tools, your credentials"]
|
|
33
|
+
Sandbox["execute_code<br/>the agent's program runs here<br/>reads, plus writes<br/>in trusted pools"]
|
|
35
34
|
Explicit["call_destructive_tool<br/>one visible call per write<br/>your client can ask you first"]
|
|
36
35
|
end
|
|
37
36
|
|
|
@@ -47,7 +46,7 @@ This is the kind of thing the agent writes, not you:
|
|
|
47
46
|
|
|
48
47
|
```js
|
|
49
48
|
async () => {
|
|
50
|
-
const { nodes } = await connecta.call("tracker.list_issues", { state: "started" });
|
|
49
|
+
const { data: { nodes } } = await connecta.call("tracker.list_issues", { state: "started" });
|
|
51
50
|
const byOwner = {};
|
|
52
51
|
for (const issue of nodes) {
|
|
53
52
|
(byOwner[issue.assignee?.name ?? "unassigned"] ??= []).push(issue.identifier);
|
|
@@ -64,11 +63,19 @@ Fifty issues in, one small object out. Your context window notices.
|
|
|
64
63
|
services in a config file; your client never changes.
|
|
65
64
|
- **Wrap any HTTP API by hand.** A few lines per tool. No OpenAPI conversion —
|
|
66
65
|
generated tool sprawl is the problem, not the fix.
|
|
67
|
-
- **Use maintained connections** for Cloudflare, Linear, Mixpanel, Notion,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
- **Use maintained connections** for <!-- providers:start -->Basecamp, Breeze ChMS, Church Community Builder, Cloudflare, GitHub, Gmail, Google Docs, Google Drive, Google Forms, Google Sheets, Google Slides, Linear, Mixpanel, Notion, Overflow, Planning Center, RevenueCat, Stripe, Tithe.ly, and Vercel<!-- providers:end -->: known endpoints, auth defaults, and
|
|
67
|
+
vetted read/write classifications, imported one at a time.
|
|
68
|
+
Cloudflare, Notion, and Vercel each let the deployment choose their
|
|
69
|
+
hand-written API interface or official hosted MCP. Planning Center, Overflow,
|
|
70
|
+
Tithe.ly, Breeze, and Church Community Builder are hand-written over their
|
|
71
|
+
whole APIs, with every money-moving write behind approval. Gmail reads mail
|
|
72
|
+
and writes drafts — never sends — and Google Drive reads, writes, and shares
|
|
73
|
+
files — never deletes one for good — as each signed-in Workspace user through
|
|
74
|
+
domain-wide delegation, with no per-user consent step; Google Docs reads and
|
|
75
|
+
edits documents, and Google Sheets reads and writes spreadsheets, the same
|
|
76
|
+
way. Google Slides reads, creates, edits, and comments on each user's decks the
|
|
77
|
+
same way.
|
|
78
|
+
Google Forms reads and edits forms and reads their responses the same way.
|
|
72
79
|
- **Let the agent work in code.** Search, chain, filter, join, and reduce
|
|
73
80
|
inside the sandbox instead of round-tripping every call through the model.
|
|
74
81
|
- **Teach undeclared result shapes by using them.** Successful read-only calls
|
|
@@ -80,45 +87,61 @@ Fifty issues in, one small object out. Your context window notices.
|
|
|
80
87
|
notice also points the agent to reduce or search the result inside a
|
|
81
88
|
program. Discovery can show compact schemas, exact JSON Schema, or a
|
|
82
89
|
TypeScript signature to read while writing JavaScript.
|
|
83
|
-
- **Keep writes deliberate.**
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
- **Keep writes deliberate.** The registry classifies tools as reads or writes.
|
|
91
|
+
Read-only pools route writes through `call_destructive_tool`; trusted pools
|
|
92
|
+
let programs write and annotate `execute_code` as a write. The host controls
|
|
93
|
+
approval. Exact deployment classification overrides beat provider review
|
|
94
|
+
and downstream annotations.
|
|
88
95
|
- **Run it on Node or Cloudflare Workers.** The core is shared; each deployment
|
|
89
|
-
supplies its platform's executor and
|
|
90
|
-
|
|
96
|
+
supplies its platform's executor and one store: a D1 database on Workers
|
|
97
|
+
(`@zackbart/connecta/d1`), a SQLite file on Node (`@zackbart/connecta/sqlite`).
|
|
98
|
+
The Node template also runs unchanged in Docker.
|
|
91
99
|
|
|
92
100
|
Deployments explicitly compose optional features: `operatorUi()` from
|
|
93
101
|
`@zackbart/connecta/ui`, `encryptedCredentialVault()` from `/credentials`,
|
|
94
|
-
`activityHistory()` from `/activity`,
|
|
102
|
+
`activityHistory()` from `/activity`, and
|
|
95
103
|
inbound authentication adapters from `/auth/*`. Omit a module and its implementation does no runtime work. Core
|
|
96
104
|
keeps connector discovery, execution, invocation, and enforcement together.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
Both deployment shapes write configuration as `defineConfig((env) => …)`, with
|
|
106
|
+
each optional module a type-checked expression the environment switches on.
|
|
107
|
+
`createConnecta` validates it against one schema — an unknown option or an
|
|
108
|
+
unusable value refuses to boot — and `connecta.describeConfig()` returns a
|
|
109
|
+
secret-free snapshot of what the deployment runs with. With `operatorUi()` enabled,
|
|
110
|
+
`GET /ui/api/config` returns that snapshot scoped to the caller, live connector
|
|
111
|
+
facts, and the caller's grants and permissions. `CONNECTA_TOKEN=<bearer> connecta
|
|
112
|
+
doctor --config --url https://connecta.example` prints only the scoped snapshot
|
|
113
|
+
as JSON, using the same serializer. It does not run the diagnostic program.
|
|
114
|
+
|
|
115
|
+
The optional UI has Overview, Connectors, Tools, Access, Activity and
|
|
116
|
+
Config pages. Connector detail separates configuration, schemas, authentication,
|
|
117
|
+
activity and diagnostics into tabs. Overview shows health, attention items and
|
|
118
|
+
endpoint setup in a drawer; Config marks each snapshot value as default or
|
|
119
|
+
configured. Catalog descriptions and schemas render as text. Activity filters
|
|
120
|
+
stay in the URL and calls group by recorded request id. Authentication, history,
|
|
121
|
+
and token controls use the signed-in person's existing permissions.
|
|
122
|
+
Connector selection and access rules remain in deployment code.
|
|
105
123
|
|
|
106
124
|
One deployment may serve several authenticated people inside the same tenant.
|
|
107
|
-
Cloudflare Access supplies Worker identity; Node
|
|
108
|
-
|
|
109
|
-
client access tokens. Shared-credential administration and personal connection
|
|
125
|
+
Cloudflare Access supplies Worker identity; Node uses Clerk for human auth. Machine clients use connecta-issued `cta_`
|
|
126
|
+
access tokens. Static bearer secrets are retired. Connecta owns no accounts or groups. Shared-credential administration and personal connection
|
|
110
127
|
setup require separate explicit permissions, both denied by default. See
|
|
111
128
|
[inbound auth](./documentation/auth.md#principals-visibility-and-operators).
|
|
129
|
+
Clerk deployments enable `aud_claim_enabled` and use resource-bound JWT or
|
|
130
|
+
opaque OAuth tokens by default. An explicit `allowedOAuthClientIds` list is a
|
|
131
|
+
fallback for unbound tokens from dedicated clients. Clerk session
|
|
132
|
+
tokens authenticate operator routes only; [Clerk auth](./documentation/auth.md#clerk-oauth-tokens-and-operator-sessions)
|
|
133
|
+
describes endpoint audience validation and the configuration migration.
|
|
112
134
|
|
|
113
135
|
Connecta is not a platform, a marketplace, a policy engine, or a multi-tenant
|
|
114
|
-
service.
|
|
115
|
-
and
|
|
136
|
+
service. The [principles](https://github.com/zackbart/connecta/blob/main/PRINCIPLES.md)
|
|
137
|
+
state the goals and invariants; [decision records](https://github.com/zackbart/connecta/tree/main/decisions)
|
|
138
|
+
explain past choices.
|
|
116
139
|
|
|
117
140
|
## Getting started
|
|
118
141
|
|
|
119
|
-
Setup is written for an agent. Point yours at [`AGENTS.md`](
|
|
142
|
+
Setup is written for an agent. Point yours at [`AGENTS.md`](https://github.com/zackbart/connecta/blob/main/AGENTS.md) and
|
|
120
143
|
ask it to set up a Connecta deployment; the
|
|
121
|
-
[documentation](./documentation/) covers the architecture, the
|
|
144
|
+
[documentation](./documentation/) covers the architecture, the six tools,
|
|
122
145
|
code mode, and inbound auth if you want to go deeper. When upgrading an
|
|
123
146
|
existing deployment, each [changelog](./CHANGELOG.md) release opens with what
|
|
124
147
|
breaks and what a deployment can ignore.
|
|
@@ -136,6 +159,6 @@ Upgrading from v0.23 does not require rotating managed `cta_…` tokens. Import
|
|
|
136
159
|
`accessTokens: true` with `accessTokens: accessTokens(storage)`, and keep the
|
|
137
160
|
same persistent storage namespace and identity/tool/pool grant rules. Enable
|
|
138
161
|
`identity.accessTokenManagement` only for the interactive operators who should
|
|
139
|
-
manage tokens.
|
|
140
|
-
|
|
141
|
-
`documentation/auth.md` for the migration
|
|
162
|
+
manage tokens. Records written by v0.23 and later move with the store: a 0.28
|
|
163
|
+
JSON state file migrates with `connecta migrate-state`, and a D1 table is
|
|
164
|
+
read as is. See the package's `documentation/auth.md` for the migration.
|
package/bin/connecta.mjs
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
lstat,
|
|
7
|
-
mkdtemp,
|
|
8
|
-
readFile,
|
|
9
|
-
rename,
|
|
10
|
-
rm,
|
|
11
|
-
symlink,
|
|
12
|
-
writeFile,
|
|
13
|
-
} from "node:fs/promises";
|
|
3
|
+
import { copyFile, cp, lstat, mkdtemp, readFile, rename, rm, symlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { CONNECTA_VERSION } from "./version.mjs";
|
|
5
|
+
import { META_TOOL_NAMES } from "./meta-tool-names.mjs";
|
|
14
6
|
import { basename, dirname, join, resolve } from "node:path";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
16
8
|
|
|
17
9
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
18
10
|
const [command, ...args] = process.argv.slice(2);
|
|
@@ -27,7 +19,8 @@ function shellCd(path) {
|
|
|
27
19
|
function usage() {
|
|
28
20
|
console.log(`Usage:
|
|
29
21
|
connecta init [directory]
|
|
30
|
-
|
|
22
|
+
connecta migrate-state <state.json> <connecta.sqlite>
|
|
23
|
+
CONNECTA_TOKEN=<bearer> connecta doctor [--config] [--url http://localhost:8787]
|
|
31
24
|
CF_ACCESS_CLIENT_ID=<id> CF_ACCESS_CLIENT_SECRET=<secret> connecta doctor --url https://worker.example`);
|
|
32
25
|
}
|
|
33
26
|
|
|
@@ -44,18 +37,13 @@ async function init() {
|
|
|
44
37
|
await lstat(target);
|
|
45
38
|
throw new Error(`Refusing to overwrite existing path: ${target}`);
|
|
46
39
|
} catch (error) {
|
|
47
|
-
const code =
|
|
48
|
-
error && typeof error === "object" && "code" in error
|
|
49
|
-
? String(error.code)
|
|
50
|
-
: "";
|
|
40
|
+
const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
|
|
51
41
|
if (code !== "ENOENT") throw error;
|
|
52
42
|
}
|
|
53
43
|
|
|
54
44
|
// Build beside the destination, then rename once complete. A failed copy or
|
|
55
45
|
// rewrite leaves no partial destination that blocks a clean retry.
|
|
56
|
-
let stage = await mkdtemp(
|
|
57
|
-
join(parent, `.${basename(target)}.connecta-init-`),
|
|
58
|
-
);
|
|
46
|
+
let stage = await mkdtemp(join(parent, `.${basename(target)}.connecta-init-`));
|
|
59
47
|
try {
|
|
60
48
|
await cp(join(packageRoot, "templates", "node"), stage, {
|
|
61
49
|
recursive: true,
|
|
@@ -63,10 +51,7 @@ async function init() {
|
|
|
63
51
|
|
|
64
52
|
// npm excludes .gitignore files and symlinks from packed dependencies.
|
|
65
53
|
// Restore both conventions explicitly in the generated project.
|
|
66
|
-
await writeFile(
|
|
67
|
-
join(stage, ".gitignore"),
|
|
68
|
-
".connecta-state.json\n.connecta-activity.jsonl\n.env\nnode_modules/\n",
|
|
69
|
-
);
|
|
54
|
+
await writeFile(join(stage, ".gitignore"), ".connecta.sqlite*\n.env\nnode_modules/\n");
|
|
70
55
|
await rm(join(stage, "CLAUDE.md"), { force: true });
|
|
71
56
|
try {
|
|
72
57
|
await symlink("AGENTS.md", join(stage, "CLAUDE.md"));
|
|
@@ -76,18 +61,11 @@ async function init() {
|
|
|
76
61
|
await copyFile(join(stage, "AGENTS.md"), join(stage, "CLAUDE.md"));
|
|
77
62
|
}
|
|
78
63
|
|
|
79
|
-
const rootPackage = JSON.parse(
|
|
80
|
-
await readFile(join(packageRoot, "package.json"), "utf8"),
|
|
81
|
-
);
|
|
64
|
+
const rootPackage = JSON.parse(await readFile(join(packageRoot, "package.json"), "utf8"));
|
|
82
65
|
const deploymentPath = join(stage, "package.json");
|
|
83
|
-
const deploymentPackage = JSON.parse(
|
|
84
|
-
await readFile(deploymentPath, "utf8"),
|
|
85
|
-
);
|
|
66
|
+
const deploymentPackage = JSON.parse(await readFile(deploymentPath, "utf8"));
|
|
86
67
|
deploymentPackage.dependencies["@zackbart/connecta"] = rootPackage.version;
|
|
87
|
-
await writeFile(
|
|
88
|
-
deploymentPath,
|
|
89
|
-
JSON.stringify(deploymentPackage, null, 2) + "\n",
|
|
90
|
-
);
|
|
68
|
+
await writeFile(deploymentPath, JSON.stringify(deploymentPackage, null, 2) + "\n");
|
|
91
69
|
|
|
92
70
|
await rename(stage, target);
|
|
93
71
|
stage = "";
|
|
@@ -99,7 +77,10 @@ async function init() {
|
|
|
99
77
|
console.log("Next:");
|
|
100
78
|
console.log(` ${shellCd(target)}`);
|
|
101
79
|
console.log(" npm install");
|
|
102
|
-
console.log(
|
|
80
|
+
console.log(' npm run --silent provision-token -- "local-machine"');
|
|
81
|
+
console.log(" # Save the returned cta_ token privately for your MCP client and doctor.");
|
|
82
|
+
console.log(" npm start");
|
|
83
|
+
console.log(" # README.md covers database paths and Docker provisioning.");
|
|
103
84
|
}
|
|
104
85
|
|
|
105
86
|
function option(name, fallback) {
|
|
@@ -135,7 +116,7 @@ async function doctorFetch(url, init = {}) {
|
|
|
135
116
|
const response = await fetch(url, {
|
|
136
117
|
...init,
|
|
137
118
|
redirect: "manual",
|
|
138
|
-
signal: AbortSignal.timeout(DOCTOR_TIMEOUT_MS),
|
|
119
|
+
signal: AbortSignal.any([AbortSignal.timeout(DOCTOR_TIMEOUT_MS), ...(init.signal ? [init.signal] : [])]),
|
|
139
120
|
});
|
|
140
121
|
if ([301, 302, 303, 307, 308].includes(response.status)) {
|
|
141
122
|
await response.body?.cancel().catch(() => {});
|
|
@@ -145,10 +126,7 @@ async function doctorFetch(url, init = {}) {
|
|
|
145
126
|
}
|
|
146
127
|
return response;
|
|
147
128
|
} catch (error) {
|
|
148
|
-
if (
|
|
149
|
-
error instanceof Error &&
|
|
150
|
-
(error.name === "TimeoutError" || error.name === "AbortError")
|
|
151
|
-
) {
|
|
129
|
+
if (error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError")) {
|
|
152
130
|
throw new Error(`Timed out after ${DOCTOR_TIMEOUT_MS}ms contacting ${url}`);
|
|
153
131
|
}
|
|
154
132
|
throw error;
|
|
@@ -156,9 +134,9 @@ async function doctorFetch(url, init = {}) {
|
|
|
156
134
|
}
|
|
157
135
|
|
|
158
136
|
async function doctor() {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (
|
|
137
|
+
for (let index = 0; index < args.length; index++) {
|
|
138
|
+
if (args[index] === "--config") continue;
|
|
139
|
+
if (args[index] !== "--url" || !args[++index] || args[index].startsWith("--")) {
|
|
162
140
|
usage();
|
|
163
141
|
process.exitCode = 1;
|
|
164
142
|
return;
|
|
@@ -173,22 +151,15 @@ async function doctor() {
|
|
|
173
151
|
throw new Error("Doctor URL must use http or https.");
|
|
174
152
|
}
|
|
175
153
|
const loopbackHosts = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
!loopbackHosts.has(parsedUrl.hostname)
|
|
179
|
-
) {
|
|
180
|
-
throw new Error(
|
|
181
|
-
"Refusing to send authentication credentials over remote plaintext HTTP. Use HTTPS.",
|
|
182
|
-
);
|
|
154
|
+
if (parsedUrl.protocol === "http:" && !loopbackHosts.has(parsedUrl.hostname)) {
|
|
155
|
+
throw new Error("Refusing to send authentication credentials over remote plaintext HTTP. Use HTTPS.");
|
|
183
156
|
}
|
|
184
157
|
const baseUrl = requestedUrl.replace(/\/+$/, "");
|
|
185
158
|
const token = process.env.CONNECTA_TOKEN;
|
|
186
159
|
const accessClientId = process.env.CF_ACCESS_CLIENT_ID;
|
|
187
160
|
const accessClientSecret = process.env.CF_ACCESS_CLIENT_SECRET;
|
|
188
161
|
if (Boolean(accessClientId) !== Boolean(accessClientSecret)) {
|
|
189
|
-
throw new Error(
|
|
190
|
-
"Set both CF_ACCESS_CLIENT_ID and CF_ACCESS_CLIENT_SECRET.",
|
|
191
|
-
);
|
|
162
|
+
throw new Error("Set both CF_ACCESS_CLIENT_ID and CF_ACCESS_CLIENT_SECRET.");
|
|
192
163
|
}
|
|
193
164
|
if (!token && !accessClientId) {
|
|
194
165
|
throw new Error(
|
|
@@ -205,9 +176,28 @@ async function doctor() {
|
|
|
205
176
|
: {}),
|
|
206
177
|
};
|
|
207
178
|
|
|
208
|
-
|
|
209
|
-
await doctorFetch(`${baseUrl}/
|
|
210
|
-
|
|
179
|
+
if (args.includes("--config")) {
|
|
180
|
+
const response = await doctorFetch(`${baseUrl}/ui/api/config`, { headers: authHeaders });
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
await response.body?.cancel().catch(() => {});
|
|
183
|
+
throw new Error(`HTTP ${response.status}: config snapshot unavailable.`);
|
|
184
|
+
}
|
|
185
|
+
let contract;
|
|
186
|
+
try {
|
|
187
|
+
contract = await response.json();
|
|
188
|
+
} catch {
|
|
189
|
+
throw new Error("Deployment returned an invalid config contract.");
|
|
190
|
+
}
|
|
191
|
+
if (contract?.schemaVersion !== 1 || contract.config?.schemaVersion !== 1) {
|
|
192
|
+
throw new Error("Deployment returned an unsupported config contract.");
|
|
193
|
+
}
|
|
194
|
+
// The server builds this with describeConfig's allowlist; do not print
|
|
195
|
+
// the overlay, identity, health deploymentInfo, or a raw error body.
|
|
196
|
+
console.log(JSON.stringify(contract.config, null, 2));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const health = await jsonResponse(await doctorFetch(`${baseUrl}/health`, { headers: authHeaders }));
|
|
211
201
|
if (health.status !== "ok") {
|
|
212
202
|
throw new Error(`Unexpected health status: ${String(health.status)}`);
|
|
213
203
|
}
|
|
@@ -225,107 +215,88 @@ async function doctor() {
|
|
|
225
215
|
.slice(0, 40)
|
|
226
216
|
.trim()
|
|
227
217
|
: "";
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
Accept: "application/json, text/event-stream",
|
|
237
|
-
},
|
|
238
|
-
body: JSON.stringify({
|
|
239
|
-
jsonrpc: "2.0",
|
|
240
|
-
id: ++requestId,
|
|
241
|
-
method,
|
|
242
|
-
params,
|
|
243
|
-
}),
|
|
244
|
-
}),
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
const listed = await mcp("tools/list", {});
|
|
248
|
-
if (listed.error) {
|
|
249
|
-
throw new Error(`tools/list failed: ${JSON.stringify(listed.error)}`);
|
|
250
|
-
}
|
|
251
|
-
const actual = listed.result?.tools
|
|
252
|
-
?.map((tool) => tool.name)
|
|
253
|
-
.sort();
|
|
254
|
-
const expected = [
|
|
255
|
-
"authorize_connector",
|
|
256
|
-
"call_destructive_tool",
|
|
257
|
-
"call_tool",
|
|
258
|
-
"execute_code",
|
|
259
|
-
"get_result",
|
|
260
|
-
"search_tools",
|
|
261
|
-
"skills",
|
|
262
|
-
];
|
|
263
|
-
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
264
|
-
throw new Error(
|
|
265
|
-
`Unexpected MCP surface. Expected ${expected.join(", ")}; received ` +
|
|
266
|
-
`${Array.isArray(actual) ? actual.join(", ") : "no tool list"}.`,
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const executed = await mcp("tools/call", {
|
|
271
|
-
name: "execute_code",
|
|
272
|
-
arguments: { code: "async () => 42" },
|
|
218
|
+
const { Client, StreamableHTTPClientTransport } = await import("@modelcontextprotocol/client");
|
|
219
|
+
const client = new Client(
|
|
220
|
+
{ name: "connecta-doctor", version: CONNECTA_VERSION },
|
|
221
|
+
{ versionNegotiation: { mode: "auto", probe: { timeoutMs: DOCTOR_TIMEOUT_MS } } },
|
|
222
|
+
);
|
|
223
|
+
const transport = new StreamableHTTPClientTransport(new URL(`${baseUrl}/mcp`), {
|
|
224
|
+
requestInit: { headers: authHeaders },
|
|
225
|
+
fetch: doctorFetch,
|
|
273
226
|
});
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
`
|
|
284
|
-
|
|
285
|
-
|
|
227
|
+
let negotiatedVersion;
|
|
228
|
+
try {
|
|
229
|
+
await client.connect(transport);
|
|
230
|
+
negotiatedVersion = client.getNegotiatedProtocolVersion();
|
|
231
|
+
const listed = await client.listTools({}, { timeout: DOCTOR_TIMEOUT_MS });
|
|
232
|
+
const actual = listed.tools.map((tool) => tool.name).sort();
|
|
233
|
+
const expected = [...META_TOOL_NAMES].sort();
|
|
234
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Unexpected MCP surface. Expected ${expected.join(", ")}; received ` +
|
|
237
|
+
`${Array.isArray(actual) ? actual.join(", ") : "no tool list"}.`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
286
240
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
report.unclassifiedTools ||
|
|
295
|
-
report.unservedTools ||
|
|
296
|
-
report.annotationConflicts ||
|
|
297
|
-
report.schemaChanges,
|
|
298
|
-
);
|
|
299
|
-
for (const [connectorId, report] of drifted) {
|
|
300
|
-
console.warn(
|
|
301
|
-
`[connecta] catalog drift on "${connectorId}" (observed ` +
|
|
302
|
-
`${report.observedAt}): ${report.unclassifiedTools} unclassified, ` +
|
|
303
|
-
`${report.unservedTools} no longer served, ` +
|
|
304
|
-
`${report.annotationConflicts} annotation conflict(s), ` +
|
|
305
|
-
`${report.schemaChanges} schema change(s).`,
|
|
241
|
+
const executed = await client.callTool(
|
|
242
|
+
{
|
|
243
|
+
name: "execute_code",
|
|
244
|
+
arguments: { code: "async () => 42" },
|
|
245
|
+
},
|
|
246
|
+
undefined,
|
|
247
|
+
{ timeout: DOCTOR_TIMEOUT_MS },
|
|
306
248
|
);
|
|
249
|
+
const executionResult = executed.structuredContent ?? JSON.parse(executed.content?.[0]?.text ?? "null");
|
|
250
|
+
if (executed.isError || executionResult?.result !== 42) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`${executorName ? `${executorName} execution` : "execute_code"} check ` + `failed: ${JSON.stringify(executed)}`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
} finally {
|
|
256
|
+
await client.close();
|
|
307
257
|
}
|
|
308
258
|
|
|
309
259
|
console.log(
|
|
310
260
|
`Connecta doctor passed: ${health.connectors} connector(s), ` +
|
|
311
261
|
`${executorName ? `${executorName} executed` : "code executed"}, ` +
|
|
312
|
-
|
|
313
|
-
(drifted.length > 0
|
|
314
|
-
? `, catalog drift on ${drifted.length} connector(s).`
|
|
315
|
-
: "."),
|
|
262
|
+
`prescribed ${META_TOOL_NAMES.length}-tool surface, MCP ${negotiatedVersion}, package ${CONNECTA_VERSION}.`,
|
|
316
263
|
);
|
|
317
264
|
}
|
|
318
265
|
|
|
266
|
+
/**
|
|
267
|
+
* One-shot copy of a 0.28 `fileStorage` JSON state file into the SQLite
|
|
268
|
+
* database 0.29 stores everything in. Stop the old deployment first; the file
|
|
269
|
+
* is read, never changed.
|
|
270
|
+
*/
|
|
271
|
+
async function migrateState() {
|
|
272
|
+
if (args.length !== 2 || args.some((arg) => arg.startsWith("--"))) {
|
|
273
|
+
usage();
|
|
274
|
+
process.exitCode = 1;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const [statePath, databasePath] = args.map((arg) => resolve(process.cwd(), arg));
|
|
278
|
+
const { importStateFile, openSqlite } = await import(pathToFileURL(join(packageRoot, "dist", "sqlite.js")).href);
|
|
279
|
+
const database = openSqlite(databasePath);
|
|
280
|
+
try {
|
|
281
|
+
const result = importStateFile(database, statePath);
|
|
282
|
+
console.log(
|
|
283
|
+
`Imported ${result.imported} entries from ${statePath} into ${databasePath}` +
|
|
284
|
+
` (${result.kept} already present, ${result.expired} expired).`,
|
|
285
|
+
);
|
|
286
|
+
} finally {
|
|
287
|
+
database.close();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
319
291
|
try {
|
|
320
292
|
if (command === "init") await init();
|
|
321
293
|
else if (command === "doctor") await doctor();
|
|
294
|
+
else if (command === "migrate-state") await migrateState();
|
|
322
295
|
else {
|
|
323
296
|
usage();
|
|
324
297
|
process.exitCode = command === "--help" || command === "-h" ? 0 : 1;
|
|
325
298
|
}
|
|
326
299
|
} catch (error) {
|
|
327
|
-
console.error(
|
|
328
|
-
`[connecta] ${error instanceof Error ? error.message : String(error)}`,
|
|
329
|
-
);
|
|
300
|
+
console.error(`[connecta] ${error instanceof Error ? error.message : String(error)}`);
|
|
330
301
|
process.exitCode = 1;
|
|
331
302
|
}
|
package/bin/version.mjs
ADDED