@nanobpm/nano-workforce 0.37.0 → 0.39.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 +14 -0
- package/README.md +5 -6
- package/SPEC.md +15 -13
- package/app/abandon.test.ts +4 -4
- package/app/abandon.ts +2 -2
- package/app/blackboard.test.ts +3 -3
- package/app/blackboard.ts +2 -2
- package/app/persist-escalation.test.ts +1 -1
- package/app/persist-round.test.ts +1 -1
- package/app/service.ts +1 -1
- package/biome.json +0 -2
- package/main.ts +3 -3
- package/nano.app.json +12 -25
- package/openapi.yaml +653 -0
- package/operations/answerFeatureEscalation.test.ts +100 -0
- package/operations/answerFeatureEscalation.ts +40 -0
- package/operations/appendBlackboard.ts +58 -0
- package/{actions → operations}/blackboard.test.ts +16 -10
- package/{actions/abandon.test.ts → operations/checkAbandon.test.ts} +5 -11
- package/{actions/abandon.ts → operations/checkAbandon.ts} +6 -7
- package/operations/getVersion.ts +4 -6
- package/operations/listActivePrs.ts +5 -9
- package/operations/postMessage.ts +4 -12
- package/operations/readBlackboard.ts +26 -0
- package/operations/startAndMessage.test.ts +5 -3
- package/operations/startConvergenceLoop.ts +16 -19
- package/operations/startPlanFanout.ts +14 -19
- package/package.json +5 -4
- package/pages/epic.page.json +1 -1
- package/pages/home.page.json +1 -1
- package/tsconfig.json +1 -0
- package/workers/persist-task-escalation/worker.ts +1 -1
- package/actions/blackboard.ts +0 -77
- package/actions/feature-answer-hook.ts +0 -45
- package/actions/plan-hook.ts +0 -20
- package/actions/webhook-submit.ts +0 -22
- package/openapi.json +0 -248
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [0.39.0](https://github.com/nanobpm/nano-workforce/compare/v0.38.0...v0.39.0) (2026-08-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* adopt urban 0.38.0 typed delegate registry for the OpenAPI surface ([#110](https://github.com/nanobpm/nano-workforce/issues/110)) ([fe62908](https://github.com/nanobpm/nano-workforce/commit/fe62908968c0bd707f8e2da58fa550cca9fbab5b))
|
|
7
|
+
|
|
8
|
+
# [0.38.0](https://github.com/nanobpm/nano-workforce/compare/v0.37.0...v0.38.0) (2026-08-10)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* migrate hooks to OpenAPI operations, one HTTP surface (ADR 0059) ([#109](https://github.com/nanobpm/nano-workforce/issues/109)) ([493d78c](https://github.com/nanobpm/nano-workforce/commit/493d78c5b58ba18098c7d458bd9538660eb2d0f3))
|
|
14
|
+
|
|
1
15
|
# [0.37.0](https://github.com/nanobpm/nano-workforce/compare/v0.36.0...v0.37.0) (2026-08-10)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -213,7 +213,7 @@ the UI and the process re-arms and retries.
|
|
|
213
213
|
### Fleet mode: hand it an issue (plan → implement → converge)
|
|
214
214
|
|
|
215
215
|
```
|
|
216
|
-
issue (UI / POST /
|
|
216
|
+
issue (UI / POST /app/api/actions/start/plan-fanout) ─► plan-fanout (BPMN)
|
|
217
217
|
plan (senior:plan) ─► record-plan ─► implement × N (parallel, senior:feature) ─► record-results
|
|
218
218
|
│
|
|
219
219
|
each opened PR ──► convergence-loop (above)
|
|
@@ -222,12 +222,11 @@ the UI and the process re-arms and retries.
|
|
|
222
222
|
A planning agent decomposes the issue into tasks; a parallel multi-instance activity
|
|
223
223
|
fans them out over implementation agents (one PR per task); `record-results` enrols
|
|
224
224
|
every opened PR into the convergence loop. Submit from the **"Hand an issue to the
|
|
225
|
-
fleet"** form, or:
|
|
225
|
+
fleet"** form, or POST the same operation the form does:
|
|
226
226
|
|
|
227
227
|
```bash
|
|
228
|
-
curl -sS -X POST http://localhost:3000/
|
|
228
|
+
curl -sS -X POST http://localhost:3000/app/api/actions/start/plan-fanout \
|
|
229
229
|
-H 'content-type: application/json' \
|
|
230
|
-
-H "x-hook-secret: $NANO_PR_WEBHOOK_SECRET" \
|
|
231
230
|
-d '{ "issue": "owner/repo#123" }'
|
|
232
231
|
```
|
|
233
232
|
|
|
@@ -243,8 +242,8 @@ curl -sS -X POST http://localhost:3000/hooks/plan \
|
|
|
243
242
|
| `GITHUB_TOKEN` | — | token for the review poller / merge (or use the host `gh` CLI) |
|
|
244
243
|
| `NANO_PR_GITHUB_TRANSPORT` | `auto` | how the poller reads GitHub: `gh` (host CLI), `token` (`GITHUB_TOKEN` over HTTP), or `auto` |
|
|
245
244
|
| `NANO_PR_POLL_MS` | `60000` | review-ready poll interval |
|
|
246
|
-
| `NANO_PR_MAX_ROUNDS` | `20` | default cap: escalate after N rounds (per-submit override via the form /
|
|
247
|
-
| `NANO_PR_WEBHOOK_SECRET` | — | shared secret for `POST /hooks/
|
|
245
|
+
| `NANO_PR_MAX_ROUNDS` | `20` | default cap: escalate after N rounds (per-submit override via the form / the `maxRounds` field on `start/convergence-loop`; clamped 1–100) |
|
|
246
|
+
| `NANO_PR_WEBHOOK_SECRET` | — | optional shared secret for the `POST /app/api/hooks/feature-answer` webhook operation (`X-Hook-Secret`); unset = open |
|
|
248
247
|
| `NANO_PR_AUTO_MERGE` | `1` | after convergence, run the merge stage; `0` = stop at `converged` (review-only) |
|
|
249
248
|
| `NANO_PR_MERGE_METHOD` | `squash` | merge method: `squash`, `merge`, or `rebase` |
|
|
250
249
|
| `NANO_PR_MERGE_ADMIN` | `0` | pass `--admin` to override failing non-required checks (use with care) |
|
package/SPEC.md
CHANGED
|
@@ -56,7 +56,7 @@ surface are proposed and open for adjustment.
|
|
|
56
56
|
```
|
|
57
57
|
nano-workforce/
|
|
58
58
|
nano.app.json # manifest (ADR 0027): sqlite data, domain types, submit webhook trigger
|
|
59
|
-
main.ts #
|
|
59
|
+
main.ts # Node entrypoint: deploy + start workers + start the runtime (page runtime + OpenAPI operations + poller)
|
|
60
60
|
deno.json
|
|
61
61
|
pages/
|
|
62
62
|
home.page.json # the screen, authored declaratively (ADR 0042 Page Composer)
|
|
@@ -263,20 +263,22 @@ row by migration `003`).
|
|
|
263
263
|
|
|
264
264
|
The app-specific business-logic endpoints are **OpenAPI operations** mounted
|
|
265
265
|
under `api.base` (`/app/api`), each implemented by a delegate module in
|
|
266
|
-
`operations
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
266
|
+
`operations/`. The webhook endpoints are ordinary operations too (ADR 0059 — the
|
|
267
|
+
`actions[]` array is retired), mounted under `/app/api/hooks/*`. The runtime
|
|
268
|
+
serves them all; `main.ts` only starts the runtime and the review-ready poller.
|
|
269
|
+
The full, authoritative contract is `openapi.yaml` (Swagger UI at
|
|
270
|
+
`/app/api-docs`); the OpenAPI rows below are the complete set of operations:
|
|
271
271
|
|
|
272
272
|
| method | route | purpose |
|
|
273
273
|
|---|---|---|
|
|
274
274
|
| `GET` | `/app/api/status` | list tracked PRs + count |
|
|
275
275
|
| `GET` | `/app/api/version` | app + engine version |
|
|
276
|
-
| `POST` | `/app/api/actions/start/convergence-loop` | parse the PR ref → create the aggregate + start the process |
|
|
277
|
-
| `POST` | `/app/api/actions/start/plan-fanout` | start a plan fan-out run |
|
|
276
|
+
| `POST` | `/app/api/actions/start/convergence-loop` | parse the PR ref → create the aggregate + start the process (the ONE submit door — page + external callers) |
|
|
277
|
+
| `POST` | `/app/api/actions/start/plan-fanout` | parse the issue ref → start a plan fan-out run (the ONE plan door) |
|
|
278
278
|
| `POST` | `/app/api/actions/message` (`escalation-answered`) | answer an open escalation → publish `escalation-answered` |
|
|
279
|
-
| `POST` | `/hooks/
|
|
279
|
+
| `POST` | `/app/api/hooks/feature-answer` | answer an implementation-phase task escalation out of band (optional shared-secret) |
|
|
280
|
+
| `GET`/`POST` | `/app/api/hooks/blackboard` | per-plan coordination blackboard (capability-token side-channel) |
|
|
281
|
+
| `GET` | `/app/api/hooks/abandon` | cooperative abandon check (per-PR capability token) |
|
|
280
282
|
|
|
281
283
|
Everything else (`GET /`, `GET /app/pages/*`, `GET /app/data/*`, the renderer) is
|
|
282
284
|
served by the runtime — including `POST /app/actions/cancel`, which is Urban's
|
|
@@ -393,7 +395,7 @@ queries skip (`merging`), so a slow pass can't double-signal.
|
|
|
393
395
|
| `GITHUB_TOKEN` | — | GitHub API (poller + agent) |
|
|
394
396
|
| `NANO_PR_POLL_MS` | 60000 | poll interval |
|
|
395
397
|
| `NANO_PR_MAX_ROUNDS` | 20 | default round cap (per-submit `maxRounds` override, clamped 1–100) |
|
|
396
|
-
| `NANO_PR_WEBHOOK_SECRET` | — |
|
|
398
|
+
| `NANO_PR_WEBHOOK_SECRET` | — | optional shared secret for the `/app/api/hooks/feature-answer` webhook operation (`X-Hook-Secret`) |
|
|
397
399
|
| `NANO_PR_AUTO_MERGE` | 1 | run the merge stage after convergence (`0` = review-only) |
|
|
398
400
|
| `NANO_PR_MERGE_METHOD` | squash | `squash` \| `merge` \| `rebase` |
|
|
399
401
|
| `NANO_PR_MERGE_ADMIN` | 0 | pass `--admin` on merge |
|
|
@@ -443,9 +445,9 @@ scalar-only and cannot express the `tasks`/`results` lists, so the workers self-
|
|
|
443
445
|
**Domain model** (`db/migrations/004_planning.sql`): `plans` (one row per issue) +
|
|
444
446
|
`plan_tasks` (one row per slice, tracking its `status`/`pr_key`/`summary`).
|
|
445
447
|
|
|
446
|
-
**Entry points**: the page's "Hand an issue to the fleet" form
|
|
447
|
-
|
|
448
|
-
|
|
448
|
+
**Entry points**: the epic page's "Hand an issue to the fleet" form or
|
|
449
|
+
`POST /app/api/actions/start/plan-fanout` (`{ issue | url }`) — the same flat
|
|
450
|
+
operation the form posts.
|
|
449
451
|
|
|
450
452
|
**Visibility**: the home page adds a **Plans** grid (Active: planning/dispatched;
|
|
451
453
|
History: done/failed/abandoned) with a `plan_tasks` child grid showing each task's
|
package/app/abandon.test.ts
CHANGED
|
@@ -53,7 +53,7 @@ test("mintAbandonToken is url-safe, unpadded, and unique", () => {
|
|
|
53
53
|
test("abandonUrl carries the token on the query string (url-encoded)", () => {
|
|
54
54
|
assertEquals(
|
|
55
55
|
abandonUrl("tok+/=", "https://host"),
|
|
56
|
-
"https://host/hooks/abandon?token=tok%2B%2F%3D",
|
|
56
|
+
"https://host/app/api/hooks/abandon?token=tok%2B%2F%3D",
|
|
57
57
|
);
|
|
58
58
|
});
|
|
59
59
|
|
|
@@ -68,13 +68,13 @@ test("abandonTokenFromUrl returns undefined for absent/tokenless/garbage input",
|
|
|
68
68
|
assertEquals(abandonTokenFromUrl(undefined), undefined);
|
|
69
69
|
assertEquals(abandonTokenFromUrl(null), undefined);
|
|
70
70
|
assertEquals(abandonTokenFromUrl(""), undefined);
|
|
71
|
-
assertEquals(abandonTokenFromUrl("https://host/hooks/abandon"), undefined, "no token param");
|
|
71
|
+
assertEquals(abandonTokenFromUrl("https://host/app/api/hooks/abandon"), undefined, "no token param");
|
|
72
72
|
assertEquals(abandonTokenFromUrl("not a url"), undefined, "unparseable input never throws");
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
test("renderAbandonBrief embeds the concrete URL and the stop contract", () => {
|
|
76
|
-
const brief = renderAbandonBrief("https://host/hooks/abandon?token=tok");
|
|
77
|
-
assertEquals(brief.includes("https://host/hooks/abandon?token=tok"), true);
|
|
76
|
+
const brief = renderAbandonBrief("https://host/app/api/hooks/abandon?token=tok");
|
|
77
|
+
assertEquals(brief.includes("https://host/app/api/hooks/abandon?token=tok"), true);
|
|
78
78
|
assertEquals(brief.includes("abandoned"), true);
|
|
79
79
|
assertEquals(brief.includes("Abort"), true);
|
|
80
80
|
// Must use `curl -f` so a 404 (torn-down run) fails the command instead of exiting 0 with an
|
package/app/abandon.ts
CHANGED
|
@@ -42,13 +42,13 @@ export function mintAbandonToken(): string {
|
|
|
42
42
|
/** The capability URL for a PR's abandon check: the token rides the query string so the agent can
|
|
43
43
|
* GET the exact string it was handed with no header assembly. */
|
|
44
44
|
export function abandonUrl(token: string, base: string = publicBaseUrl()): string {
|
|
45
|
-
return `${base}/hooks/abandon?token=${encodeURIComponent(token)}`;
|
|
45
|
+
return `${base}/app/api/hooks/abandon?token=${encodeURIComponent(token)}`;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/** Recover the capability token from an abandon URL — the inverse of `abandonUrl`. Returns
|
|
49
49
|
* undefined when the input is absent or carries no `token` query param. A desync-heal uses this to
|
|
50
50
|
* reconstruct a missing `pull_requests` row with the SAME token the running agent was already
|
|
51
|
-
* handed (via the `abandonUrl` process variable), so its `curl -f "…/hooks/abandon?token=…"` abort
|
|
51
|
+
* handed (via the `abandonUrl` process variable), so its `curl -f "…/app/api/hooks/abandon?token=…"` abort
|
|
52
52
|
* check keeps resolving instead of 404-ing on a freshly-minted token and aborting a live run. */
|
|
53
53
|
export function abandonTokenFromUrl(url: string | null | undefined): string | undefined {
|
|
54
54
|
if (!url) return undefined;
|
package/app/blackboard.test.ts
CHANGED
|
@@ -64,7 +64,7 @@ test("publicBaseUrl: a blank/whitespace override falls back instead of yielding
|
|
|
64
64
|
try {
|
|
65
65
|
assertEquals(publicBaseUrl(""), "http://localhost:3000");
|
|
66
66
|
assertEquals(publicBaseUrl(" "), "http://localhost:3000");
|
|
67
|
-
assertEquals(blackboardUrl("t", publicBaseUrl("")), "http://localhost:3000/hooks/blackboard?token=t");
|
|
67
|
+
assertEquals(blackboardUrl("t", publicBaseUrl("")), "http://localhost:3000/app/api/hooks/blackboard?token=t");
|
|
68
68
|
} finally {
|
|
69
69
|
if (prev === undefined) delete process.env.NANO_PR_BASE_URL;
|
|
70
70
|
else process.env.NANO_PR_BASE_URL = prev;
|
|
@@ -74,7 +74,7 @@ test("publicBaseUrl: a blank/whitespace override falls back instead of yielding
|
|
|
74
74
|
test("blackboardUrl: capability token rides the query string", () => {
|
|
75
75
|
assertEquals(
|
|
76
76
|
blackboardUrl("tok+en/x", "https://h"),
|
|
77
|
-
"https://h/hooks/blackboard?token=tok%2Ben%2Fx",
|
|
77
|
+
"https://h/app/api/hooks/blackboard?token=tok%2Ben%2Fx",
|
|
78
78
|
);
|
|
79
79
|
});
|
|
80
80
|
|
|
@@ -87,7 +87,7 @@ test("normalizeKind: valid passes through, anything else becomes note", () => {
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
test("renderCoordinationBrief: leads with a separator and teaches the protocol + URL", () => {
|
|
90
|
-
const url = "https://h/hooks/blackboard?token=abc";
|
|
90
|
+
const url = "https://h/app/api/hooks/blackboard?token=abc";
|
|
91
91
|
const brief = renderCoordinationBrief(url);
|
|
92
92
|
assert(brief.startsWith("\n\n---"), "must own a leading separator (appendPrompt adds none)");
|
|
93
93
|
assertStringIncludes(brief, url);
|
package/app/blackboard.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// stable `dedupe_key` is a no-op (backed by a unique index; we also short-circuit here).
|
|
14
14
|
// - CAPABILITY URL. The per-plan token IS the credential; the agent curls the exact URL it was
|
|
15
15
|
// handed (delivered in `appendPrompt`). Delivery is in-band (rides the prompt the harness
|
|
16
|
-
// already forwards); use is out-of-band (a direct side-channel to `/hooks/blackboard`).
|
|
16
|
+
// already forwards); use is out-of-band (a direct side-channel to `/app/api/hooks/blackboard`).
|
|
17
17
|
//
|
|
18
18
|
// Data access goes through the record gateway (`data.table`), never hand-written SQL — matching
|
|
19
19
|
// app/service.ts and app/plan.ts.
|
|
@@ -87,7 +87,7 @@ export function publicBaseUrl(env: string | undefined = process.env.NANO_PR_PUBL
|
|
|
87
87
|
/** The capability URL for a plan's blackboard: the token rides the query string, so the agent can
|
|
88
88
|
* GET/POST the exact string it was handed with no header assembly. */
|
|
89
89
|
export function blackboardUrl(token: string, base: string = publicBaseUrl()): string {
|
|
90
|
-
return `${base}/hooks/blackboard?token=${encodeURIComponent(token)}`;
|
|
90
|
+
return `${base}/app/api/hooks/blackboard?token=${encodeURIComponent(token)}`;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/** The coordination-protocol block appended (verbatim, via `appendPrompt`) to each implementer
|
|
@@ -149,7 +149,7 @@ test("persist-escalation heals from the prKey when repo/prNumber are absent", as
|
|
|
149
149
|
round: 2,
|
|
150
150
|
status: "needs_input",
|
|
151
151
|
question: "decide",
|
|
152
|
-
abandonUrl: "https://host/hooks/abandon?token=TOK-en_123",
|
|
152
|
+
abandonUrl: "https://host/app/api/hooks/abandon?token=TOK-en_123",
|
|
153
153
|
},
|
|
154
154
|
};
|
|
155
155
|
await handler(job as any, app as any);
|
|
@@ -103,7 +103,7 @@ test("persist-round heals from the prKey when repo/prNumber are absent", async (
|
|
|
103
103
|
prKey: "o/r#12",
|
|
104
104
|
round: 2,
|
|
105
105
|
status: "addressed",
|
|
106
|
-
abandonUrl: "https://host/hooks/abandon?token=TOK-en_123",
|
|
106
|
+
abandonUrl: "https://host/app/api/hooks/abandon?token=TOK-en_123",
|
|
107
107
|
},
|
|
108
108
|
};
|
|
109
109
|
await handler(job as any, app as any);
|
package/app/service.ts
CHANGED
|
@@ -129,7 +129,7 @@ interface PullRequest {
|
|
|
129
129
|
// frugal-CI fresh-head-run remedy. A rebase changes the head and therefore permits a new nudge.
|
|
130
130
|
fresh_head_run_head: string | null;
|
|
131
131
|
// Cooperative abandon check (015_pr_abandon_token.sql, issue #76): the per-PR capability token a
|
|
132
|
-
// running agent curls (GET /hooks/abandon?token=…) to learn whether this run was cancelled before
|
|
132
|
+
// running agent curls (GET /app/api/hooks/abandon?token=…) to learn whether this run was cancelled before
|
|
133
133
|
// it performs a side effect. Minted at submit, reused across the convergence + merge instances.
|
|
134
134
|
abandon_token: string | null;
|
|
135
135
|
// Technical-incident surfacing (017_pr_incident.sql, issue #94), written by the poller's
|
package/biome.json
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
"includes": [
|
|
5
5
|
"app/**/*.ts",
|
|
6
6
|
"operations/**/*.ts",
|
|
7
|
-
"actions/**/*.ts",
|
|
8
7
|
"workers/**/*.ts",
|
|
9
8
|
"pages/**/*.ts",
|
|
10
9
|
"components/**/*.ts",
|
|
@@ -20,7 +19,6 @@
|
|
|
20
19
|
"includes": [
|
|
21
20
|
"app/**/*.ts",
|
|
22
21
|
"operations/**/*.ts",
|
|
23
|
-
"actions/**/*.ts",
|
|
24
22
|
"workers/**/*.ts",
|
|
25
23
|
"pages/**/*.ts",
|
|
26
24
|
"components/**/*.ts",
|
package/main.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// nano-workforce — Urban App entrypoint (ADR 0055).
|
|
2
2
|
//
|
|
3
3
|
// The whole app is declared in `nano.app.json` (models, sqlite datasource, app-hosted record
|
|
4
|
-
// workers, the schema-driven pages surface, and the
|
|
4
|
+
// workers, the schema-driven pages surface, and the OpenAPI control surface) and materialized by the
|
|
5
5
|
// `@nanobpm/urban` runtime via `runFromEnv`:
|
|
6
6
|
// • deploys the BPMN + hosts the `pr.*` record workers (workers/*/worker.ts),
|
|
7
7
|
// • serves the schema-driven page runtime (ADR 0042) from `pages/home.page.json`,
|
|
8
|
-
// • mounts the
|
|
9
|
-
//
|
|
8
|
+
// • mounts the OpenAPI operations (openapi.yaml → operations/*.ts) — the start/cancel/message
|
|
9
|
+
// control endpoints plus the webhook operations under `/app/api/hooks/*` (ADR 0059).
|
|
10
10
|
//
|
|
11
11
|
// The only thing that isn't declarative is the review-ready poller: it does arbitrary GitHub
|
|
12
12
|
// polling and then correlates a `review-ready` message. A cron trigger can only fire an engine
|
package/nano.app.json
CHANGED
|
@@ -124,6 +124,16 @@
|
|
|
124
124
|
"handler": "workers/retro-record/worker.ts"
|
|
125
125
|
}
|
|
126
126
|
],
|
|
127
|
+
"externalTaskTypes": [
|
|
128
|
+
"senior:pr-review",
|
|
129
|
+
"senior:fix-ci",
|
|
130
|
+
"senior:rebase",
|
|
131
|
+
"senior:plan",
|
|
132
|
+
"senior:plan-review",
|
|
133
|
+
"senior:feature",
|
|
134
|
+
"senior:trial-merge",
|
|
135
|
+
"senior:retro"
|
|
136
|
+
],
|
|
127
137
|
"surfaces": {
|
|
128
138
|
"pages": {
|
|
129
139
|
"enabled": true,
|
|
@@ -140,31 +150,8 @@
|
|
|
140
150
|
"icon": "assets/icon.svg"
|
|
141
151
|
},
|
|
142
152
|
"api": {
|
|
143
|
-
"spec": "openapi.
|
|
153
|
+
"spec": "openapi.yaml",
|
|
144
154
|
"dir": "operations",
|
|
145
155
|
"validateResponses": "dev"
|
|
146
|
-
}
|
|
147
|
-
"actions": [
|
|
148
|
-
{
|
|
149
|
-
"path": "/hooks/submit",
|
|
150
|
-
"module": "actions/webhook-submit.ts"
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
"path": "/hooks/plan",
|
|
154
|
-
"module": "actions/plan-hook.ts"
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
"path": "/hooks/feature-answer",
|
|
158
|
-
"module": "actions/feature-answer-hook.ts"
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
"path": "/hooks/blackboard",
|
|
162
|
-
"module": "actions/blackboard.ts"
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
"path": "/hooks/abandon",
|
|
166
|
-
"module": "actions/abandon.ts",
|
|
167
|
-
"method": "GET"
|
|
168
|
-
}
|
|
169
|
-
]
|
|
156
|
+
}
|
|
170
157
|
}
|