capability-worker 0.1.0 → 0.1.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 +24 -21
- package/dist/runtime/http/index.d.ts +2 -1
- package/dist/runtime/http/index.js +1 -1
- package/package.json +2 -1
- package/templates/new-capability-worker/README.md +15 -0
- package/templates/new-capability-worker/cli/definition.json +48 -0
- package/templates/new-capability-worker/cli/mapping.json +8 -0
- package/templates/new-capability-worker/package.json +13 -0
- package/templates/new-capability-worker/pnpm-workspace.yaml +6 -0
- package/templates/new-capability-worker/skills/starter/SKILL.md +9 -0
- package/templates/new-capability-worker/worker/capability/info.template.json +36 -0
- package/templates/new-capability-worker/worker/package.json +20 -0
- package/templates/new-capability-worker/worker/src/worker.ts +51 -0
- package/templates/new-capability-worker/worker/tsconfig.json +12 -0
- package/templates/new-capability-worker/worker/wrangler.jsonc +17 -0
package/README.md
CHANGED
|
@@ -229,7 +229,7 @@ Use `requireCapabilitySignature` when route handlers prefer exception flow:
|
|
|
229
229
|
|
|
230
230
|
```ts
|
|
231
231
|
import { requireCapabilitySignature } from "capability-worker/auth";
|
|
232
|
-
import { errorResponse,
|
|
232
|
+
import { errorResponse, okJson } from "capability-worker/http";
|
|
233
233
|
|
|
234
234
|
export default {
|
|
235
235
|
async fetch(request: Request, env: Env): Promise<Response> {
|
|
@@ -242,7 +242,7 @@ export default {
|
|
|
242
242
|
.bind(accountId)
|
|
243
243
|
.all();
|
|
244
244
|
|
|
245
|
-
return
|
|
245
|
+
return okJson(rows.results);
|
|
246
246
|
} catch (error) {
|
|
247
247
|
return errorResponse(error);
|
|
248
248
|
}
|
|
@@ -254,7 +254,7 @@ Use `verifyCapabilitySignature` when route handlers prefer explicit result handl
|
|
|
254
254
|
|
|
255
255
|
```ts
|
|
256
256
|
import { verifyCapabilitySignature } from "capability-worker/auth";
|
|
257
|
-
import { errorJson,
|
|
257
|
+
import { errorJson, okJson } from "capability-worker/http";
|
|
258
258
|
|
|
259
259
|
const auth = await verifyCapabilitySignature(request, env);
|
|
260
260
|
|
|
@@ -262,10 +262,7 @@ if (!auth.ok) {
|
|
|
262
262
|
return errorJson(auth.code, auth.message, auth.status);
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
return
|
|
266
|
-
ok: true,
|
|
267
|
-
accountId: auth.accountId
|
|
268
|
-
});
|
|
265
|
+
return okJson({ accountId: auth.accountId });
|
|
269
266
|
```
|
|
270
267
|
|
|
271
268
|
Rules for signed routes:
|
|
@@ -382,6 +379,7 @@ import {
|
|
|
382
379
|
errorJson,
|
|
383
380
|
errorResponse,
|
|
384
381
|
jsonResponse,
|
|
382
|
+
okJson,
|
|
385
383
|
textResponse
|
|
386
384
|
} from "capability-worker/http";
|
|
387
385
|
|
|
@@ -392,7 +390,16 @@ export async function readMeeting(request: Request, env: Env) {
|
|
|
392
390
|
throw new HttpError(404, "meeting_not_found", "Meeting not found.");
|
|
393
391
|
}
|
|
394
392
|
|
|
395
|
-
return
|
|
393
|
+
return okJson(meeting);
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Success shape:
|
|
398
|
+
|
|
399
|
+
```json
|
|
400
|
+
{
|
|
401
|
+
"ok": true,
|
|
402
|
+
"data": {}
|
|
396
403
|
}
|
|
397
404
|
```
|
|
398
405
|
|
|
@@ -412,6 +419,7 @@ Useful helpers:
|
|
|
412
419
|
|
|
413
420
|
```ts
|
|
414
421
|
jsonResponse(value, init);
|
|
422
|
+
okJson(data, init);
|
|
415
423
|
textResponse(value, init);
|
|
416
424
|
htmlResponse(value, init);
|
|
417
425
|
redirectResponse(location, status);
|
|
@@ -915,6 +923,10 @@ CAPABILITY_PUBLIC_KEYS_URL = "https://host.example.com/capability/public-keys"
|
|
|
915
923
|
|
|
916
924
|
## New Capability Worker Recipe
|
|
917
925
|
|
|
926
|
+
The npm package includes a small starter at `templates/new-capability-worker`.
|
|
927
|
+
It uses `capability-build-assets`, `requireCapabilitySignature`, `okJson`,
|
|
928
|
+
`errorJson`, and `textResponse` from this package.
|
|
929
|
+
|
|
918
930
|
1. Create `cli/definition.json`.
|
|
919
931
|
2. Create `cli/mapping.json`.
|
|
920
932
|
3. Create `skills/<capability-name>/SKILL.md`.
|
|
@@ -932,7 +944,7 @@ Minimal route skeleton:
|
|
|
932
944
|
|
|
933
945
|
```ts
|
|
934
946
|
import { requireCapabilitySignature } from "capability-worker/auth";
|
|
935
|
-
import { errorResponse,
|
|
947
|
+
import { errorJson, errorResponse, okJson, textResponse } from "capability-worker/http";
|
|
936
948
|
|
|
937
949
|
export default {
|
|
938
950
|
async fetch(request: Request, env: Env): Promise<Response> {
|
|
@@ -940,7 +952,7 @@ export default {
|
|
|
940
952
|
const url = new URL(request.url);
|
|
941
953
|
|
|
942
954
|
if (url.pathname === "/api/health") {
|
|
943
|
-
return
|
|
955
|
+
return okJson({ status: "ok" });
|
|
944
956
|
}
|
|
945
957
|
|
|
946
958
|
if (url.pathname === "/api/meetings" && request.method === "GET") {
|
|
@@ -954,22 +966,13 @@ export default {
|
|
|
954
966
|
.all();
|
|
955
967
|
|
|
956
968
|
if (wantsJson) {
|
|
957
|
-
return
|
|
969
|
+
return okJson(rows.results);
|
|
958
970
|
}
|
|
959
971
|
|
|
960
972
|
return textResponse(formatMeetingsTable(rows.results));
|
|
961
973
|
}
|
|
962
974
|
|
|
963
|
-
return
|
|
964
|
-
{
|
|
965
|
-
ok: false,
|
|
966
|
-
error: {
|
|
967
|
-
code: "not_found",
|
|
968
|
-
message: "Not found."
|
|
969
|
-
}
|
|
970
|
-
},
|
|
971
|
-
404
|
|
972
|
-
);
|
|
975
|
+
return errorJson("not_found", "Not found.", 404);
|
|
973
976
|
} catch (error) {
|
|
974
977
|
return errorResponse(error);
|
|
975
978
|
}
|
|
@@ -7,6 +7,7 @@ declare class HttpError extends Error {
|
|
|
7
7
|
});
|
|
8
8
|
}
|
|
9
9
|
declare function jsonResponse(value: unknown, init?: ResponseInit | number): Response;
|
|
10
|
+
declare function okJson(data: unknown, init?: ResponseInit | number): Response;
|
|
10
11
|
declare function textResponse(value: string, init?: ResponseInit | number): Response;
|
|
11
12
|
declare function htmlResponse(value: string, init?: ResponseInit | number): Response;
|
|
12
13
|
declare function redirectResponse(location: string, status?: number): Response;
|
|
@@ -21,4 +22,4 @@ declare function booleanParam(value: unknown): boolean;
|
|
|
21
22
|
declare function limitParam(value: unknown, defaultValue?: number, max?: number): number;
|
|
22
23
|
declare function getRepeated(url: URL, key: string): string[];
|
|
23
24
|
|
|
24
|
-
export { HttpError, booleanParam, errorJson, errorResponse, getRepeated, htmlResponse, isJsonRequested, jsonResponse, limitParam, parseJsonBody, readJsonObject, redirectResponse, stringArrayParam, stringParam, textResponse };
|
|
25
|
+
export { HttpError, booleanParam, errorJson, errorResponse, getRepeated, htmlResponse, isJsonRequested, jsonResponse, limitParam, okJson, parseJsonBody, readJsonObject, redirectResponse, stringArrayParam, stringParam, textResponse };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
class t extends Error{status;code;expose;constructor(t,e,n,r){super(n),this.name="HttpError",this.status=t,this.code=e,this.expose=r?.expose??t<500}}function e(t,e=200){const n=
|
|
1
|
+
class t extends Error{status;code;expose;constructor(t,e,n,r){super(n),this.name="HttpError",this.status=t,this.code=e,this.expose=r?.expose??t<500}}function e(t,e=200){const n=m(e);return new Response(JSON.stringify(t,null,2),{...n,headers:x(n.headers,"content-type","application/json; charset=utf-8")})}function n(t,n=200){return e({ok:!0,data:t},n)}function r(t,e=200){return new Response(t.endsWith("\n")?t:`${t}\n`,{...m(e),headers:x(m(e).headers,"content-type","text/plain; charset=utf-8")})}function o(t,e=200){const n=m(e);return new Response(t,{...n,headers:x(n.headers,"content-type","text/html; charset=utf-8")})}function s(t,e=302){return new Response(null,{status:e,headers:{location:t,"cache-control":"no-store"}})}function a(t,n,r=400,o={}){return e({ok:!1,error:{code:t,message:n},...o},r)}function c(e){return e instanceof t?a(e.code,e.expose?e.message:"Internal error.",e.status):a("internal_error","Internal error.",500)}async function u(t){if(!t.body)return{};try{const e=await t.json();return!e||"object"!=typeof e||Array.isArray(e)?{}:e}catch{return{}}}async function i(e){const n=await e.json().catch(()=>null);if(!n||"object"!=typeof n||Array.isArray(n))throw new t(400,"invalid_json","Expected a JSON object.");return n}function f(t,e){const n=t.searchParams.get("json");return""===n||"1"===n||"true"===n||!0===e?.json}function h(t){if("string"!=typeof t)return;const e=t.trim();return e.length>0?e:void 0}function p(t){if(Array.isArray(t))return t.filter(t=>"string"==typeof t).map(t=>t.trim()).filter(Boolean);const e=h(t);return e?[e]:[]}function l(t){return!0===t||"true"===t||"1"===t||""===t}function y(t,e=20,n=100){const r="number"==typeof t?t:Number(t);return Number.isFinite(r)?Math.max(1,Math.min(n,Math.floor(r))):e}function d(t,e){return t.searchParams.getAll(e).map(t=>t.trim()).filter(Boolean)}function m(t){return"number"==typeof t?{status:t}:t}function x(t,e,n){const r=new Headers(t);return r.has(e)||r.set(e,n),r}export{t as HttpError,l as booleanParam,a as errorJson,c as errorResponse,d as getRepeated,o as htmlResponse,f as isJsonRequested,e as jsonResponse,y as limitParam,n as okJson,u as parseJsonBody,i as readJsonObject,s as redirectResponse,p as stringArrayParam,h as stringParam,r as textResponse};
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "capability-worker",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Shared toolkit for Cloudflare Workers that expose CLI-like capabilities over HTTP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
|
+
"templates",
|
|
9
10
|
"README.md",
|
|
10
11
|
"LICENSE"
|
|
11
12
|
],
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# New Capability Worker
|
|
2
|
+
|
|
3
|
+
Small starter for a Cloudflare Worker capability that uses the shared `capability-worker` package.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm install
|
|
9
|
+
CAPABILITY_BASE_URL=https://starter.example.com/api pnpm -C worker run build:capability-assets
|
|
10
|
+
pnpm -C worker run typecheck
|
|
11
|
+
pnpm -C worker run dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Replace `starter` names, command definitions, mapping routes, and domain behavior before deploying.
|
|
15
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "cli.definition.v1",
|
|
3
|
+
"cli": {
|
|
4
|
+
"name": "starter",
|
|
5
|
+
"summary": "Run starter capability commands.",
|
|
6
|
+
"description": "A minimal capability definition to replace with product-specific commands."
|
|
7
|
+
},
|
|
8
|
+
"commandGroups": [
|
|
9
|
+
{
|
|
10
|
+
"id": "starter",
|
|
11
|
+
"title": "Starter",
|
|
12
|
+
"description": "Starter commands."
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"commands": [
|
|
16
|
+
{
|
|
17
|
+
"id": "hello",
|
|
18
|
+
"group": "starter",
|
|
19
|
+
"command": "starter hello",
|
|
20
|
+
"summary": "Return a signed hello response.",
|
|
21
|
+
"description": "Verifies the capability signature and returns a small account-scoped response.",
|
|
22
|
+
"usage": "starter hello [--json]",
|
|
23
|
+
"risk": "read",
|
|
24
|
+
"requiresConfirmation": false,
|
|
25
|
+
"arguments": [],
|
|
26
|
+
"options": [
|
|
27
|
+
{
|
|
28
|
+
"name": "--json",
|
|
29
|
+
"key": "json",
|
|
30
|
+
"type": "boolean",
|
|
31
|
+
"description": "Return structured JSON instead of text."
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"examples": [
|
|
35
|
+
{
|
|
36
|
+
"description": "Return a text response.",
|
|
37
|
+
"command": "starter hello"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"description": "Return a JSON response.",
|
|
41
|
+
"command": "starter hello --json"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"output": "Without --json, prints a short text response. With --json, returns a standard { ok, data } JSON response."
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "new-capability-worker",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"packageManager": "pnpm@11.9.0",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build:capability-assets": "pnpm -C worker run build:capability-assets",
|
|
8
|
+
"typecheck": "pnpm -C worker run typecheck",
|
|
9
|
+
"dev": "pnpm -C worker run dev",
|
|
10
|
+
"deploy": "pnpm -C worker run deploy"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "capability.info.v1",
|
|
3
|
+
"id": "starter",
|
|
4
|
+
"name": "starter",
|
|
5
|
+
"displayName": "Starter",
|
|
6
|
+
"summary": "Run starter capability commands.",
|
|
7
|
+
"description": "A minimal capability definition to replace with product-specific commands.",
|
|
8
|
+
"homepage": "${CAPABILITY_BASE_URL}",
|
|
9
|
+
"apiBaseUrl": "${CAPABILITY_BASE_URL}",
|
|
10
|
+
"endpoints": {
|
|
11
|
+
"definition": "definition",
|
|
12
|
+
"mapping": "mapping"
|
|
13
|
+
},
|
|
14
|
+
"skill": {
|
|
15
|
+
"name": "starter",
|
|
16
|
+
"entrypoint": {
|
|
17
|
+
"path": "SKILL.md",
|
|
18
|
+
"url": "skill/SKILL.md",
|
|
19
|
+
"contentType": "text/markdown"
|
|
20
|
+
},
|
|
21
|
+
"resources": []
|
|
22
|
+
},
|
|
23
|
+
"auth": {
|
|
24
|
+
"scheme": "capability-signature-v1",
|
|
25
|
+
"accountHeader": "x-capability-account-id",
|
|
26
|
+
"timestampHeader": "x-capability-timestamp",
|
|
27
|
+
"keyIdHeader": "x-capability-key-id",
|
|
28
|
+
"signatureHeader": "x-capability-signature"
|
|
29
|
+
},
|
|
30
|
+
"version": "${APP_ASSET_VERSION}",
|
|
31
|
+
"updatedAt": "2026-07-06T00:00:00Z",
|
|
32
|
+
"cache": {
|
|
33
|
+
"maxAgeSeconds": 86400
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "starter-capability-worker",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build:capability-assets": "capability-build-assets --worker-root . --repo-root .. --validate",
|
|
7
|
+
"typecheck": "tsc --noEmit",
|
|
8
|
+
"dev": "wrangler dev",
|
|
9
|
+
"deploy": "pnpm run build:capability-assets && wrangler deploy"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"capability-worker": "^0.1.1"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@cloudflare/workers-types": "^4.20260627.0",
|
|
16
|
+
"typescript": "^5.9.0",
|
|
17
|
+
"wrangler": "^4.51.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { isCapabilityAssetPath } from "capability-worker";
|
|
2
|
+
import { requireCapabilitySignature } from "capability-worker/auth";
|
|
3
|
+
import {
|
|
4
|
+
errorJson,
|
|
5
|
+
errorResponse,
|
|
6
|
+
isJsonRequested,
|
|
7
|
+
okJson,
|
|
8
|
+
textResponse
|
|
9
|
+
} from "capability-worker/http";
|
|
10
|
+
|
|
11
|
+
export interface Env {
|
|
12
|
+
ASSETS: Fetcher;
|
|
13
|
+
CAPABILITY_PUBLIC_KEYS_JSON?: string;
|
|
14
|
+
CAPABILITY_PUBLIC_KEYS_URL?: string;
|
|
15
|
+
CAPABILITY_PUBLIC_KEYS_SERVICE?: Fetcher;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
async fetch(request: Request, env: Env): Promise<Response> {
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(request.url);
|
|
22
|
+
|
|
23
|
+
if (isCapabilityAssetPath(url.pathname, { basePath: "/api" })) {
|
|
24
|
+
return env.ASSETS.fetch(request);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (url.pathname === "/api/health" && request.method === "GET") {
|
|
28
|
+
return okJson({ status: "ok" });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (url.pathname === "/api/hello" && request.method === "GET") {
|
|
32
|
+
const { accountId } = await requireCapabilitySignature(request, env);
|
|
33
|
+
const data = {
|
|
34
|
+
accountId,
|
|
35
|
+
message: "Hello from the starter capability."
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (isJsonRequested(url)) {
|
|
39
|
+
return okJson(data);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return textResponse(`Hello ${accountId}.`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return errorJson("not_found", "Not found.", 404);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return errorResponse(error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "starter-capability-worker",
|
|
3
|
+
"main": "src/worker.ts",
|
|
4
|
+
"compatibility_date": "2026-07-06",
|
|
5
|
+
"assets": {
|
|
6
|
+
"directory": "./public",
|
|
7
|
+
"binding": "ASSETS",
|
|
8
|
+
"html_handling": "none",
|
|
9
|
+
"not_found_handling": "none"
|
|
10
|
+
},
|
|
11
|
+
"vars": {
|
|
12
|
+
"CAPABILITY_BASE_URL": "https://starter.example.com/api",
|
|
13
|
+
"APP_ASSET_VERSION": "0.1.1",
|
|
14
|
+
"CAPABILITY_PUBLIC_KEYS_URL": "https://example.com/capability/public-keys"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|