@cargo-ai/worker-sdk 1.0.1
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/README.md +90 -0
- package/build/src/createWorker.d.ts +51 -0
- package/build/src/createWorker.d.ts.map +1 -0
- package/build/src/createWorker.js +44 -0
- package/build/src/customIntegration.d.ts +97 -0
- package/build/src/customIntegration.d.ts.map +1 -0
- package/build/src/customIntegration.js +212 -0
- package/build/src/index.d.ts +9 -0
- package/build/src/index.d.ts.map +1 -0
- package/build/src/index.js +4 -0
- package/build/src/types.d.ts +15 -0
- package/build/src/types.d.ts.map +1 -0
- package/build/src/types.js +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/package.json +47 -0
- package/templates/blank/README.md +112 -0
- package/templates/blank/manifest.json +3 -0
- package/templates/blank/package.json +20 -0
- package/templates/blank/scripts/copy-runtime-files.mjs +25 -0
- package/templates/blank/src/index.ts +46 -0
- package/templates/blank/tsconfig.json +24 -0
- package/templates/custom-integration/.env.example +5 -0
- package/templates/custom-integration/README.md +136 -0
- package/templates/custom-integration/manifest.json +3 -0
- package/templates/custom-integration/package.json +21 -0
- package/templates/custom-integration/scripts/copy-runtime-files.mjs +25 -0
- package/templates/custom-integration/src/actions/createRecord.ts +34 -0
- package/templates/custom-integration/src/actions/index.ts +20 -0
- package/templates/custom-integration/src/authenticate.ts +27 -0
- package/templates/custom-integration/src/autocompletes/index.ts +13 -0
- package/templates/custom-integration/src/completeOauth.ts +10 -0
- package/templates/custom-integration/src/connectorConfig.ts +16 -0
- package/templates/custom-integration/src/dynamicSchemas/index.ts +13 -0
- package/templates/custom-integration/src/extractors/index.ts +18 -0
- package/templates/custom-integration/src/extractors/listRecords.ts +51 -0
- package/templates/custom-integration/src/index.ts +44 -0
- package/templates/custom-integration/src/listUsers.ts +11 -0
- package/templates/custom-integration/tsconfig.json +24 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# **APP_NAME**
|
|
2
|
+
|
|
3
|
+
A Cargo Hosting worker (edge HTTP handler), scaffolded by `cargo-ai hosting worker init`.
|
|
4
|
+
|
|
5
|
+
Built on [`@cargo-ai/worker-sdk`](https://www.npmjs.com/package/@cargo-ai/worker-sdk), which wires [Hono](https://hono.dev) + [Chanfana](https://chanfana.com) so every route you declare automatically shows up in an OpenAPI 3.1 spec.
|
|
6
|
+
|
|
7
|
+
## Layout
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
__APP_NAME__/
|
|
11
|
+
├── manifest.json # outboundAllowlist (hosts the worker may call)
|
|
12
|
+
├── package.json
|
|
13
|
+
├── tsconfig.json
|
|
14
|
+
├── scripts/
|
|
15
|
+
│ └── copy-runtime-files.mjs
|
|
16
|
+
└── src/
|
|
17
|
+
└── index.ts # declares routes on the app returned by createWorker(...)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`npm run build` runs `tsc` (compiles `src/` into `dist/`) and copies `manifest.json`, `package.json`, and `package-lock.json` into `dist/`. The contents of `dist/` are what get uploaded as the deployment source.
|
|
21
|
+
|
|
22
|
+
## Get started
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Deploy
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
# Provision a worker slot in your Cargo workspace:
|
|
32
|
+
cargo-ai hosting worker create --name "__APP_NAME__" --slug your-slug
|
|
33
|
+
|
|
34
|
+
# Build, then deploy:
|
|
35
|
+
npm run build
|
|
36
|
+
cargo-ai hosting deployment create --worker-uuid <workerUuid> --source ./dist
|
|
37
|
+
|
|
38
|
+
# Promote the resulting deployment to live:
|
|
39
|
+
cargo-ai hosting deployment promote --uuid <deploymentUuid>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The worker is then served at `https://<your-slug>.worker.getcargo.run` (or whatever the hosting domain is for your environment).
|
|
43
|
+
|
|
44
|
+
## OpenAPI + docs
|
|
45
|
+
|
|
46
|
+
Out of the box:
|
|
47
|
+
|
|
48
|
+
- `GET /openapi.json` — OpenAPI 3.1 spec derived from your route definitions.
|
|
49
|
+
- `GET /docs` — Swagger UI.
|
|
50
|
+
|
|
51
|
+
Adding a new documented route is a class declaration:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { type Context, OpenAPIRoute } from "@cargo-ai/worker-sdk";
|
|
55
|
+
import { z } from "zod";
|
|
56
|
+
|
|
57
|
+
class GetThing extends OpenAPIRoute {
|
|
58
|
+
override schema = {
|
|
59
|
+
request: { params: z.object({ id: z.string().uuid() }) },
|
|
60
|
+
responses: {
|
|
61
|
+
"200": {
|
|
62
|
+
description: "The thing.",
|
|
63
|
+
content: {
|
|
64
|
+
"application/json": {
|
|
65
|
+
schema: z.object({ id: z.string(), name: z.string() }),
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
override async handle(c: Context): Promise<Response> {
|
|
73
|
+
const { params } = await this.getValidatedData<typeof this.schema>();
|
|
74
|
+
return c.json({ id: params.id, name: "example" });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
openapi.get("/things/:id", GetThing);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Params, query, body, and responses are validated at runtime and described in the generated spec.
|
|
82
|
+
|
|
83
|
+
## Talk to the Cargo API
|
|
84
|
+
|
|
85
|
+
`env.CARGO_API_TOKEN` is injected by Cargo Hosting at runtime. Never hardcode credentials.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
openapi.get(
|
|
89
|
+
"/segments",
|
|
90
|
+
class extends OpenAPIRoute {
|
|
91
|
+
override schema = {
|
|
92
|
+
responses: {
|
|
93
|
+
"200": {
|
|
94
|
+
description: "OK",
|
|
95
|
+
content: {
|
|
96
|
+
"application/json": { schema: z.object({}).passthrough() },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
override async handle(c: Context): Promise<Response> {
|
|
102
|
+
const res = await fetch(
|
|
103
|
+
"https://api.getcargo.io/v1/segmentation/segments/list",
|
|
104
|
+
{ headers: { Authorization: `Bearer ${c.env.CARGO_API_TOKEN}` } },
|
|
105
|
+
);
|
|
106
|
+
return c.json(await res.json());
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Add `api.getcargo.io` to `manifest.json#outboundAllowlist` before the worker can call it.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__APP_NAME__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "A Cargo Hosting worker (edge HTTP handler) with automatic OpenAPI generation via @cargo-ai/worker-sdk (Hono + Chanfana under the hood).",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc && node ./scripts/copy-runtime-files.mjs",
|
|
9
|
+
"type:check": "tsc --noEmit",
|
|
10
|
+
"deploy": "npm run build && cargo-ai hosting deployment create --worker-uuid $CARGO_WORKER_UUID --source ./dist"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@cargo-ai/worker-sdk": "^0.1.0",
|
|
14
|
+
"zod": "^4.3.6"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^20.10.8",
|
|
18
|
+
"typescript": "5.3.2"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Copies the non-TypeScript files the deploy pipeline needs (manifest.json,
|
|
2
|
+
// package.json, package-lock.json) into ./dist alongside the compiled JS.
|
|
3
|
+
//
|
|
4
|
+
// Cargo Hosting builds the worker by running `npm ci` + `esbuild index.js`
|
|
5
|
+
// against the contents of the directory you pass to `--source`, so everything
|
|
6
|
+
// has to live in one place.
|
|
7
|
+
|
|
8
|
+
import { copyFile } from "node:fs/promises";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
|
|
11
|
+
const files = ["manifest.json", "package.json", "package-lock.json"];
|
|
12
|
+
const distDir = "dist";
|
|
13
|
+
|
|
14
|
+
for (const file of files) {
|
|
15
|
+
try {
|
|
16
|
+
await copyFile(file, path.join(distDir, file));
|
|
17
|
+
console.log(`copied ${file} -> ${distDir}/${file}`);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (error.code === "ENOENT") {
|
|
20
|
+
console.warn(`skipping missing ${file}`);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Context, createWorker, OpenAPIRoute } from "@cargo-ai/worker-sdk";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
const { app, openapi } = createWorker({
|
|
5
|
+
title: "__APP_NAME__",
|
|
6
|
+
description: "A Cargo Hosting worker.",
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
class GetHello extends OpenAPIRoute {
|
|
10
|
+
override schema = {
|
|
11
|
+
tags: ["hello"],
|
|
12
|
+
summary: "Greet the caller.",
|
|
13
|
+
request: {
|
|
14
|
+
query: z.object({
|
|
15
|
+
name: z.string().default("world").describe("Name to greet."),
|
|
16
|
+
}),
|
|
17
|
+
},
|
|
18
|
+
responses: {
|
|
19
|
+
"200": {
|
|
20
|
+
description: "Greeting.",
|
|
21
|
+
content: {
|
|
22
|
+
"application/json": {
|
|
23
|
+
schema: z.object({
|
|
24
|
+
ok: z.literal(true),
|
|
25
|
+
message: z.string(),
|
|
26
|
+
worker: z.string(),
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
override async handle(c: Context): Promise<Response> {
|
|
35
|
+
const { query } = await this.getValidatedData<typeof this.schema>();
|
|
36
|
+
return c.json({
|
|
37
|
+
ok: true as const,
|
|
38
|
+
message: `Hello, ${query.name}!`,
|
|
39
|
+
worker: "__APP_NAME__",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
openapi.get("/", GetHello);
|
|
45
|
+
|
|
46
|
+
export default app;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "nodenext",
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"noImplicitAny": true,
|
|
11
|
+
"noUnusedLocals": true,
|
|
12
|
+
"noUnusedParameters": true,
|
|
13
|
+
"noFallthroughCasesInSwitch": true,
|
|
14
|
+
"noImplicitReturns": true,
|
|
15
|
+
"esModuleInterop": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"forceConsistentCasingInFileNames": true,
|
|
18
|
+
"resolveJsonModule": true,
|
|
19
|
+
"declaration": false,
|
|
20
|
+
"sourceMap": false,
|
|
21
|
+
"noEmitOnError": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["src/**/*.ts"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# **APP_NAME**
|
|
2
|
+
|
|
3
|
+
A Cargo Hosting **worker** that implements the [Cargo Custom Integration](https://docs.getcargo.io/integration/custom-integration) HTTP contract, built on [`@cargo-ai/worker-sdk`](https://www.npmjs.com/package/@cargo-ai/worker-sdk) (Hono + Chanfana under the hood).
|
|
4
|
+
|
|
5
|
+
After it's deployed you register it as a connector with `cargo-ai connection custom-integration create --kind worker --worker-uuid <workerUuid>`, and it shows up in your Cargo workspace's connector catalog.
|
|
6
|
+
|
|
7
|
+
## Layout
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
__APP_NAME__/
|
|
11
|
+
├── manifest.json # outboundAllowlist (hosts the worker may call)
|
|
12
|
+
├── package.json
|
|
13
|
+
├── tsconfig.json
|
|
14
|
+
├── scripts/
|
|
15
|
+
│ └── copy-runtime-files.mjs
|
|
16
|
+
└── src/
|
|
17
|
+
├── index.ts # createCustomIntegration({ ... }) — that's the whole router
|
|
18
|
+
├── connectorConfig.ts # zod schema + type for the connector config
|
|
19
|
+
├── authenticate.ts # POST /authenticate
|
|
20
|
+
├── listUsers.ts # POST /listUsers
|
|
21
|
+
├── completeOauth.ts # POST /completeOauth
|
|
22
|
+
├── actions/
|
|
23
|
+
│ ├── index.ts # registers each action handler under a slug
|
|
24
|
+
│ └── createRecord.ts
|
|
25
|
+
├── extractors/
|
|
26
|
+
│ ├── index.ts # registers each extractor handler under a slug
|
|
27
|
+
│ └── listRecords.ts
|
|
28
|
+
├── autocompletes/
|
|
29
|
+
│ └── index.ts
|
|
30
|
+
└── dynamicSchemas/
|
|
31
|
+
└── index.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`npm run build` runs `tsc` and copies `manifest.json`, `package.json`, and `package-lock.json` into `dist/`. The contents of `dist/` are what get uploaded as the deployment source — Cargo Hosting then runs `npm ci` + `esbuild` against them to produce the final edge bundle.
|
|
35
|
+
|
|
36
|
+
## What the SDK gives you for free
|
|
37
|
+
|
|
38
|
+
`createCustomIntegration({ ... })` returns a ready-to-export Hono app that serves:
|
|
39
|
+
|
|
40
|
+
| Endpoint | Purpose |
|
|
41
|
+
| --------------------------------------------- | ------------------------------------------------------------------- |
|
|
42
|
+
| `GET /manifest` | Cargo-native connector manifest consumed by the Cargo backend. |
|
|
43
|
+
| `GET /openapi.json` | OpenAPI 3.1 spec derived from the same Zod schemas. |
|
|
44
|
+
| `GET /docs` | Swagger UI. |
|
|
45
|
+
| `POST /authenticate` | Calls `spec.authenticate`. |
|
|
46
|
+
| `POST /listUsers` | Calls `spec.listUsers` (if provided). |
|
|
47
|
+
| `POST /completeOauth` | Calls `spec.completeOauth` (if provided). |
|
|
48
|
+
| `POST /actions/<slug>/execute` | Dispatches to `spec.actions[slug]`. Body `config` is Zod-validated. |
|
|
49
|
+
| `POST /extractors/<slug>/fetch` + `.../count` | Dispatches to `spec.extractors[slug]`. |
|
|
50
|
+
| `POST /autocompletes/<slug>` | Dispatches to `spec.autocompletes[slug]`. |
|
|
51
|
+
| `POST /dynamicSchemas/<slug>` | Dispatches to `spec.dynamicSchemas[slug]`. |
|
|
52
|
+
|
|
53
|
+
Zod schemas are the single source of truth — the same schema drives the runtime validation, the `GET /manifest` JSON Schema the Cargo UI renders, and the `GET /openapi.json` description.
|
|
54
|
+
|
|
55
|
+
## End-to-end flow
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
# 1. Install deps:
|
|
59
|
+
npm install
|
|
60
|
+
|
|
61
|
+
# 2. Provision a worker slot in your Cargo workspace:
|
|
62
|
+
cargo-ai hosting worker create --name "__APP_NAME__" --slug your-slug
|
|
63
|
+
|
|
64
|
+
# 3. Build, then deploy the bundle:
|
|
65
|
+
npm run build
|
|
66
|
+
cargo-ai hosting deployment create --worker-uuid <workerUuid> --source ./dist
|
|
67
|
+
|
|
68
|
+
# 4. Promote the deployment to live:
|
|
69
|
+
cargo-ai hosting deployment promote --uuid <deploymentUuid>
|
|
70
|
+
|
|
71
|
+
# 5. Register the worker as a custom integration so it appears as a connector:
|
|
72
|
+
cargo-ai connection custom-integration create \
|
|
73
|
+
--kind worker \
|
|
74
|
+
--worker-uuid <workerUuid>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Iterate
|
|
78
|
+
|
|
79
|
+
Edit any `src/**/*.ts` file, then re-run:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
npm run build
|
|
83
|
+
cargo-ai hosting deployment create --worker-uuid <workerUuid> --source ./dist
|
|
84
|
+
cargo-ai hosting deployment promote --uuid <deploymentUuid>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The next call to `GET https://<your-slug>.worker.getcargo.run/manifest` reflects the new manifest, and `GET .../openapi.json` / `.../docs` reflect the new schemas.
|
|
88
|
+
|
|
89
|
+
## Calling third-party APIs
|
|
90
|
+
|
|
91
|
+
Add the host to `manifest.json#outboundAllowlist` (the Cargo Hosting sandbox blocks unrelated outbound traffic):
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"outboundAllowlist": ["api.example.com"]
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Then `fetch` it from any handler. The connector config (whatever you defined in `src/connectorConfig.ts`) reaches your handler as `payload.connector.config`:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import type { CustomIntegrationAction } from "@cargo-ai/worker-sdk";
|
|
103
|
+
import { z } from "zod";
|
|
104
|
+
|
|
105
|
+
import type { ConnectorConfig } from "../connectorConfig.js";
|
|
106
|
+
|
|
107
|
+
const schema = z.object({
|
|
108
|
+
name: z.string().meta({ title: "Name" }),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
export const createRecord: CustomIntegrationAction<
|
|
112
|
+
ConnectorConfig,
|
|
113
|
+
typeof schema
|
|
114
|
+
> = {
|
|
115
|
+
name: "Create Record",
|
|
116
|
+
description: "Creates a new record.",
|
|
117
|
+
config: { schema },
|
|
118
|
+
execute: async (payload) => {
|
|
119
|
+
const { apiKey } = payload.connector.config;
|
|
120
|
+
const res = await fetch("https://api.example.com/records", {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: {
|
|
123
|
+
"content-type": "application/json",
|
|
124
|
+
Authorization: `Bearer ${apiKey}`,
|
|
125
|
+
},
|
|
126
|
+
body: JSON.stringify({ name: payload.config.name }),
|
|
127
|
+
});
|
|
128
|
+
const created = (await res.json()) as { id: string; name: string };
|
|
129
|
+
return {
|
|
130
|
+
outcome: "executed",
|
|
131
|
+
title: `Created ${created.name}`,
|
|
132
|
+
data: created,
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__APP_NAME__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "A Cargo Hosting worker that implements the Cargo Custom Integration HTTP contract, built on @cargo-ai/worker-sdk with automatic OpenAPI generation.",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc && node ./scripts/copy-runtime-files.mjs",
|
|
9
|
+
"type:check": "tsc --noEmit",
|
|
10
|
+
"deploy": "npm run build && cargo-ai hosting deployment create --worker-uuid $CARGO_WORKER_UUID --source ./dist"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@cargo-ai/types": "^1.0.22",
|
|
14
|
+
"@cargo-ai/worker-sdk": "^0.1.0",
|
|
15
|
+
"zod": "^4.3.6"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^20.10.8",
|
|
19
|
+
"typescript": "5.3.2"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Copies the non-TypeScript files the deploy pipeline needs (manifest.json,
|
|
2
|
+
// package.json, package-lock.json) into ./dist alongside the compiled JS.
|
|
3
|
+
//
|
|
4
|
+
// Cargo Hosting builds the worker by running `npm ci` + `esbuild index.js`
|
|
5
|
+
// against the contents of the directory you pass to `--source`, so everything
|
|
6
|
+
// has to live in one place.
|
|
7
|
+
|
|
8
|
+
import { copyFile } from "node:fs/promises";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
|
|
11
|
+
const files = ["manifest.json", "package.json", "package-lock.json"];
|
|
12
|
+
const distDir = "dist";
|
|
13
|
+
|
|
14
|
+
for (const file of files) {
|
|
15
|
+
try {
|
|
16
|
+
await copyFile(file, path.join(distDir, file));
|
|
17
|
+
console.log(`copied ${file} -> ${distDir}/${file}`);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (error.code === "ENOENT") {
|
|
20
|
+
console.warn(`skipping missing ${file}`);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CustomIntegrationAction } from "@cargo-ai/worker-sdk";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import type { ConnectorConfig } from "../connectorConfig.js";
|
|
5
|
+
|
|
6
|
+
const schema = z.object({
|
|
7
|
+
name: z.string().meta({ title: "Name" }),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const createRecord: CustomIntegrationAction<
|
|
11
|
+
ConnectorConfig,
|
|
12
|
+
typeof schema
|
|
13
|
+
> = {
|
|
14
|
+
name: "Create Record",
|
|
15
|
+
description: "Creates a new record in the connected service.",
|
|
16
|
+
config: {
|
|
17
|
+
schema,
|
|
18
|
+
},
|
|
19
|
+
execute: async (payload) => {
|
|
20
|
+
const { name } = payload.config;
|
|
21
|
+
|
|
22
|
+
// TODO: call the third-party API using `payload.connector.config.apiKey`.
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
outcome: "executed",
|
|
26
|
+
title: `Created ${name}`,
|
|
27
|
+
data: {
|
|
28
|
+
id: crypto.randomUUID(),
|
|
29
|
+
name,
|
|
30
|
+
createdAt: new Date().toISOString(),
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CustomIntegrationAction } from "@cargo-ai/worker-sdk";
|
|
2
|
+
|
|
3
|
+
import type { ConnectorConfig } from "../connectorConfig.js";
|
|
4
|
+
import { createRecord } from "./createRecord.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register action handlers here, keyed by the slug that Cargo should call.
|
|
8
|
+
* Cargo routes `POST /actions/<slug>/execute` at this worker to the matching
|
|
9
|
+
* handler. The zod config schema on each handler drives:
|
|
10
|
+
* - runtime validation of `payload.config`
|
|
11
|
+
* - the JSON Schema exposed at `GET /manifest#actions[slug].config.jsonSchema`
|
|
12
|
+
* - the OpenAPI schema for `POST /actions/<slug>/execute`
|
|
13
|
+
*/
|
|
14
|
+
export const actions: Record<
|
|
15
|
+
string,
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
CustomIntegrationAction<ConnectorConfig, any>
|
|
18
|
+
> = {
|
|
19
|
+
createRecord,
|
|
20
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ConnectionTypes } from "@cargo-ai/types";
|
|
2
|
+
|
|
3
|
+
import type { ConnectorConfig } from "./connectorConfig.js";
|
|
4
|
+
|
|
5
|
+
export const authenticate = async (
|
|
6
|
+
payload: ConnectionTypes.IntegrationAuthenticatePayload<ConnectorConfig>,
|
|
7
|
+
): Promise<ConnectionTypes.IntegrationAuthenticateResult> => {
|
|
8
|
+
const { apiKey } = payload.connector.config;
|
|
9
|
+
|
|
10
|
+
if (apiKey.length === 0) {
|
|
11
|
+
return {
|
|
12
|
+
outcome: "error",
|
|
13
|
+
reason: "unauthenticated",
|
|
14
|
+
errorMessage: "Missing API key",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// TODO: call the third-party API to validate `apiKey`.
|
|
19
|
+
// const res = await fetch("https://api.example.com/me", {
|
|
20
|
+
// headers: { Authorization: `Bearer ${apiKey}` },
|
|
21
|
+
// });
|
|
22
|
+
// if (res.ok === false) {
|
|
23
|
+
// return { outcome: "error", reason: "unauthenticated", errorMessage: "Invalid API key" };
|
|
24
|
+
// }
|
|
25
|
+
|
|
26
|
+
return { outcome: "success" };
|
|
27
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CustomIntegrationAutocomplete } from "@cargo-ai/worker-sdk";
|
|
2
|
+
|
|
3
|
+
import type { ConnectorConfig } from "../connectorConfig.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Register autocomplete handlers here, keyed by the slug that Cargo should
|
|
7
|
+
* call. Cargo routes `POST /autocompletes/<slug>` at this worker; the handler
|
|
8
|
+
* receives `{ connector, slug, params, value? }` and returns `{ results }`.
|
|
9
|
+
*/
|
|
10
|
+
export const autocompletes: Record<
|
|
11
|
+
string,
|
|
12
|
+
CustomIntegrationAutocomplete<ConnectorConfig>
|
|
13
|
+
> = {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ConnectionTypes } from "@cargo-ai/types";
|
|
2
|
+
|
|
3
|
+
export const completeOauth = async (
|
|
4
|
+
_payload: ConnectionTypes.IntegrationCompleteOauthPayload,
|
|
5
|
+
): Promise<ConnectionTypes.IntegrationCompleteOauthResult> => {
|
|
6
|
+
// TODO: exchange the OAuth params (e.g. `code`, `redirectUri`) supplied in
|
|
7
|
+
// `payload.params` for a token via the third-party OAuth endpoint, then
|
|
8
|
+
// return the token (which Cargo will store in the connector config).
|
|
9
|
+
return { value: "" };
|
|
10
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shape of the connector config — what Cargo collects from the workspace user
|
|
5
|
+
* when they create a connector against this integration.
|
|
6
|
+
*
|
|
7
|
+
* Cargo passes this object as `payload.connector.config` to every action,
|
|
8
|
+
* extractor, autocomplete, dynamic schema, and `authenticate` call. Add fields
|
|
9
|
+
* here as you need them; the matching JSON Schema is generated automatically
|
|
10
|
+
* for the `GET /manifest` wire format and for `GET /openapi.json`.
|
|
11
|
+
*/
|
|
12
|
+
export const zodConnectorConfig = z.object({
|
|
13
|
+
apiKey: z.string().meta({ title: "API Key" }),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type ConnectorConfig = z.infer<typeof zodConnectorConfig>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CustomIntegrationDynamicSchema } from "@cargo-ai/worker-sdk";
|
|
2
|
+
|
|
3
|
+
import type { ConnectorConfig } from "../connectorConfig.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Register dynamic-schema handlers here, keyed by the slug that Cargo should
|
|
7
|
+
* call. Cargo routes `POST /dynamicSchemas/<slug>` at this worker with
|
|
8
|
+
* `{ connector, params }` and expects `{ jsonSchema, uiSchema? }` back.
|
|
9
|
+
*/
|
|
10
|
+
export const dynamicSchemas: Record<
|
|
11
|
+
string,
|
|
12
|
+
CustomIntegrationDynamicSchema<ConnectorConfig>
|
|
13
|
+
> = {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CustomIntegrationExtractor } from "@cargo-ai/worker-sdk";
|
|
2
|
+
|
|
3
|
+
import type { ConnectorConfig } from "../connectorConfig.js";
|
|
4
|
+
import { listRecords } from "./listRecords.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register extractor handlers here, keyed by the slug that Cargo should call.
|
|
8
|
+
* Cargo routes `POST /extractors/<slug>/fetch` and `POST /extractors/<slug>/count`
|
|
9
|
+
* at this worker to the matching handler. The zod config/meta schemas drive
|
|
10
|
+
* the manifest, the OpenAPI spec, and runtime validation.
|
|
11
|
+
*/
|
|
12
|
+
export const extractors: Record<
|
|
13
|
+
string,
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
CustomIntegrationExtractor<ConnectorConfig, any, any>
|
|
16
|
+
> = {
|
|
17
|
+
listRecords,
|
|
18
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ConnectionTypes } from "@cargo-ai/types";
|
|
2
|
+
import type { CustomIntegrationExtractor } from "@cargo-ai/worker-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import type { ConnectorConfig } from "../connectorConfig.js";
|
|
6
|
+
|
|
7
|
+
const configSchema = z.object({});
|
|
8
|
+
const metaSchema = z.object({});
|
|
9
|
+
|
|
10
|
+
const COLUMNS: ConnectionTypes.IntegrationColumn[] = [
|
|
11
|
+
{ slug: "id", type: "string", label: "ID" },
|
|
12
|
+
{ slug: "name", type: "string", label: "Name" },
|
|
13
|
+
{ slug: "createdAt", type: "date", label: "Created At" },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export const listRecords: CustomIntegrationExtractor<
|
|
17
|
+
ConnectorConfig,
|
|
18
|
+
typeof configSchema,
|
|
19
|
+
typeof metaSchema
|
|
20
|
+
> = {
|
|
21
|
+
name: "List Records",
|
|
22
|
+
description: "Pulls records from the connected service into a Cargo dataset.",
|
|
23
|
+
config: {
|
|
24
|
+
schema: configSchema,
|
|
25
|
+
},
|
|
26
|
+
meta: {
|
|
27
|
+
schema: metaSchema,
|
|
28
|
+
},
|
|
29
|
+
mode: {
|
|
30
|
+
kind: "fetch",
|
|
31
|
+
isIncremental: false,
|
|
32
|
+
},
|
|
33
|
+
preview: "records",
|
|
34
|
+
fetch: async (_payload) => {
|
|
35
|
+
// TODO: call the third-party API and map results into the columns above.
|
|
36
|
+
return {
|
|
37
|
+
outcome: "fetched",
|
|
38
|
+
columns: COLUMNS,
|
|
39
|
+
idColumnSlug: "id",
|
|
40
|
+
titleColumnSlug: "name",
|
|
41
|
+
data: {
|
|
42
|
+
kind: "records",
|
|
43
|
+
records: [],
|
|
44
|
+
hasMore: false,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
count: async (_payload) => {
|
|
49
|
+
return { count: 0 };
|
|
50
|
+
},
|
|
51
|
+
};
|