@code-partner/codepipe-hub 0.14.1-dev.423.ga49d1f61 → 0.14.1-dev.424.ge3c8cc77
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/hub/dist/embedded.js +1 -1
- package/hub/dist/index.js +332 -104
- package/package.json +1 -1
package/hub/dist/embedded.js
CHANGED
|
@@ -457,7 +457,7 @@ var PLATFORM_VERSION;
|
|
|
457
457
|
var init_version_generated = __esm({
|
|
458
458
|
"../packages/shared/dist/version.generated.js"() {
|
|
459
459
|
"use strict";
|
|
460
|
-
PLATFORM_VERSION = "0.14.1-dev.
|
|
460
|
+
PLATFORM_VERSION = "0.14.1-dev.424.ge3c8cc77";
|
|
461
461
|
}
|
|
462
462
|
});
|
|
463
463
|
|
package/hub/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ var PLATFORM_VERSION;
|
|
|
13
13
|
var init_version_generated = __esm({
|
|
14
14
|
"../packages/shared/dist/version.generated.js"() {
|
|
15
15
|
"use strict";
|
|
16
|
-
PLATFORM_VERSION = "0.14.1-dev.
|
|
16
|
+
PLATFORM_VERSION = "0.14.1-dev.424.ge3c8cc77";
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
|
|
@@ -8675,7 +8675,10 @@ var init_route_definition = __esm({
|
|
|
8675
8675
|
node: ["/node/v1"],
|
|
8676
8676
|
local: ["/local"],
|
|
8677
8677
|
mcp: ["/mcp"],
|
|
8678
|
-
bootstrap:
|
|
8678
|
+
// `/cli` is the CLI's own bootstrap: the connection request it opens, the
|
|
8679
|
+
// page and the email link a person confirms it on (DEV-927 settled it here
|
|
8680
|
+
// rather than moving a path the CLI and a sent email already hold).
|
|
8681
|
+
bootstrap: ["/auth", "/bootstrap", "/cli"],
|
|
8679
8682
|
operational: ["/healthz", "/readyz"],
|
|
8680
8683
|
// Static serves the browser bundle from the root and the install script from
|
|
8681
8684
|
// its documented path (ADR-0060 §5), so it has no single prefix to check.
|
|
@@ -8697,7 +8700,8 @@ var init_route_definition = __esm({
|
|
|
8697
8700
|
"/api/tasks",
|
|
8698
8701
|
"/api/projects",
|
|
8699
8702
|
"/api/admin",
|
|
8700
|
-
"/api/notifications"
|
|
8703
|
+
"/api/notifications",
|
|
8704
|
+
"/bug-report"
|
|
8701
8705
|
];
|
|
8702
8706
|
PUBLIC_MUTATION_ALLOWLIST = [
|
|
8703
8707
|
"auth.login",
|
|
@@ -8705,11 +8709,8 @@ var init_route_definition = __esm({
|
|
|
8705
8709
|
"auth.loginPassword",
|
|
8706
8710
|
"auth.logout",
|
|
8707
8711
|
"auth.register",
|
|
8708
|
-
"auth.setPassword",
|
|
8709
8712
|
"bootstrap.request",
|
|
8710
|
-
"bootstrap.confirm",
|
|
8711
8713
|
"bootstrap.authorizeEmail",
|
|
8712
|
-
"bootstrap.authorizeConfirm",
|
|
8713
8714
|
// DEV-927: a node registers with a registration token in the body — the
|
|
8714
8715
|
// token IS the credential, and there is no node identity yet to bear it.
|
|
8715
8716
|
"node.sandbox.register",
|
|
@@ -8720,8 +8721,8 @@ var init_route_definition = __esm({
|
|
|
8720
8721
|
});
|
|
8721
8722
|
|
|
8722
8723
|
// src/http/route-registry.ts
|
|
8723
|
-
function installRouteGuard(app, mode, log) {
|
|
8724
|
-
const registry = new RouteRegistry(mode, log);
|
|
8724
|
+
function installRouteGuard(app, mode, log, legacy = []) {
|
|
8725
|
+
const registry = new RouteRegistry(mode, log, legacy);
|
|
8725
8726
|
app.addHook("onRoute", (route) => registry.inspect(route));
|
|
8726
8727
|
return registry;
|
|
8727
8728
|
}
|
|
@@ -8742,15 +8743,18 @@ var init_route_registry = __esm({
|
|
|
8742
8743
|
init_route_definition();
|
|
8743
8744
|
META_KEY = "codepipeRoute";
|
|
8744
8745
|
RouteRegistry = class {
|
|
8745
|
-
constructor(mode, log = { warn: (message) => console.warn(message) }) {
|
|
8746
|
+
constructor(mode, log = { warn: (message) => console.warn(message) }, legacy = []) {
|
|
8746
8747
|
this.mode = mode;
|
|
8747
8748
|
this.log = log;
|
|
8749
|
+
this.legacyAllowed = new Set(legacy);
|
|
8748
8750
|
}
|
|
8749
8751
|
routes = /* @__PURE__ */ new Map();
|
|
8750
8752
|
paths = /* @__PURE__ */ new Set();
|
|
8751
8753
|
operationIds = /* @__PURE__ */ new Set();
|
|
8752
8754
|
unclassified = 0;
|
|
8753
8755
|
invalid = 0;
|
|
8756
|
+
legacyAllowed;
|
|
8757
|
+
legacySeen = /* @__PURE__ */ new Set();
|
|
8754
8758
|
/** Every route registered with metadata, in registration order. */
|
|
8755
8759
|
list() {
|
|
8756
8760
|
return [...this.routes.values()];
|
|
@@ -8768,6 +8772,12 @@ var init_route_registry = __esm({
|
|
|
8768
8772
|
unclassifiedCount() {
|
|
8769
8773
|
return this.unclassified;
|
|
8770
8774
|
}
|
|
8775
|
+
/** The same registrations by name (`METHOD /path`), so a test can hold the
|
|
8776
|
+
* legacy list to exactly what the process still serves: an entry nobody
|
|
8777
|
+
* registers any more is a stale allowance, and it fails there. */
|
|
8778
|
+
unclassifiedRoutes() {
|
|
8779
|
+
return [...this.legacySeen];
|
|
8780
|
+
}
|
|
8771
8781
|
/** Registrations that had metadata and broke a rule. Counted separately from
|
|
8772
8782
|
* the unclassified ones: "not migrated yet" and "migrated wrongly" are
|
|
8773
8783
|
* different problems, and a violation that lands in neither count is a
|
|
@@ -8787,12 +8797,14 @@ var init_route_registry = __esm({
|
|
|
8787
8797
|
if (methods.length === 0) return;
|
|
8788
8798
|
this.paths.add(route.url);
|
|
8789
8799
|
if (!meta) {
|
|
8790
|
-
this.
|
|
8791
|
-
if (this.mode === "enforce") {
|
|
8800
|
+
const tolerated = methods.every((method) => this.legacyAllowed.has(`${method} ${route.url}`));
|
|
8801
|
+
if (this.mode === "enforce" && !tolerated) {
|
|
8792
8802
|
throw new Error(
|
|
8793
|
-
`route registry: ${methods.join("/")} ${route.url} registered without metadata \u2014 every route declares its surface, operationId and policy (ADR-0060 \xA73)`
|
|
8803
|
+
`route registry: ${methods.join("/")} ${route.url} registered without metadata \u2014 every route declares its surface, operationId and policy (ADR-0060 \xA73); the old roots are closed since the cutover (DEV-927)`
|
|
8794
8804
|
);
|
|
8795
8805
|
}
|
|
8806
|
+
this.unclassified += 1;
|
|
8807
|
+
for (const method of methods) this.legacySeen.add(`${method} ${route.url}`);
|
|
8796
8808
|
return;
|
|
8797
8809
|
}
|
|
8798
8810
|
const violations = methods.flatMap(
|
|
@@ -28906,9 +28918,7 @@ var DASHBOARD_ROUTES = [
|
|
|
28906
28918
|
var CONSOLE_ROUTES = [/^\/$/];
|
|
28907
28919
|
var DECLARED_RESERVED_ROOTS = [
|
|
28908
28920
|
...Object.values(SURFACE_PREFIXES).flat(),
|
|
28909
|
-
...RETIRED_ROOTS
|
|
28910
|
-
"/cli",
|
|
28911
|
-
"/ws"
|
|
28921
|
+
...RETIRED_ROOTS
|
|
28912
28922
|
];
|
|
28913
28923
|
function isBrowserRoute(app, path) {
|
|
28914
28924
|
const clean = path.split("?")[0].split("#")[0];
|
|
@@ -29072,6 +29082,121 @@ async function registerSpaHosts(app, opts) {
|
|
|
29072
29082
|
// src/server.ts
|
|
29073
29083
|
init_route_registry();
|
|
29074
29084
|
|
|
29085
|
+
// src/http/legacy-routes.ts
|
|
29086
|
+
var STATIC_PLUGIN_ROUTES = ["GET /*"];
|
|
29087
|
+
var LEGACY_ROUTES = [
|
|
29088
|
+
"DELETE /api/admin/farms/:farmId",
|
|
29089
|
+
"DELETE /api/admin/runners/:runnerId",
|
|
29090
|
+
"DELETE /api/admin/sandboxes/:sandboxId",
|
|
29091
|
+
"DELETE /projects/:projectId/secrets/:name",
|
|
29092
|
+
"DELETE /runner/:runnerId",
|
|
29093
|
+
"DELETE /runner/reg-token",
|
|
29094
|
+
"DELETE /sandbox/:sandboxId",
|
|
29095
|
+
"DELETE /sandbox/reg-token",
|
|
29096
|
+
"GET /admin/pause",
|
|
29097
|
+
"GET /api/admin/farms",
|
|
29098
|
+
"GET /api/admin/logs",
|
|
29099
|
+
"GET /api/admin/presence",
|
|
29100
|
+
"GET /api/admin/projects",
|
|
29101
|
+
"GET /api/admin/registrations",
|
|
29102
|
+
"GET /api/admin/runners",
|
|
29103
|
+
"GET /api/admin/server",
|
|
29104
|
+
"GET /api/admin/settings",
|
|
29105
|
+
"GET /api/admin/stats",
|
|
29106
|
+
"GET /api/admin/users",
|
|
29107
|
+
"GET /api/notifications/settings",
|
|
29108
|
+
"GET /api/projects/:projectId/tasks/:taskKey/plan-review",
|
|
29109
|
+
"GET /bug-report",
|
|
29110
|
+
"GET /projects/:projectId/actions",
|
|
29111
|
+
"GET /projects/:projectId/adr-backfill",
|
|
29112
|
+
"GET /projects/:projectId/clusters/:anchorKey/journal",
|
|
29113
|
+
"GET /projects/:projectId/config-proposals/:proposalId/agent-activity/stream",
|
|
29114
|
+
"GET /projects/:projectId/context-file",
|
|
29115
|
+
"GET /projects/:projectId/drafts/:draftId/agent-activity/stream",
|
|
29116
|
+
"GET /projects/:projectId/model/drift",
|
|
29117
|
+
"GET /projects/:projectId/model/search",
|
|
29118
|
+
"GET /projects/:projectId/pause",
|
|
29119
|
+
"GET /projects/:projectId/prompts",
|
|
29120
|
+
"GET /projects/:projectId/prompts/:role",
|
|
29121
|
+
"GET /projects/:projectId/review-metrics",
|
|
29122
|
+
"GET /projects/:projectId/runs",
|
|
29123
|
+
"GET /projects/:projectId/runs/:runId/stream",
|
|
29124
|
+
"GET /projects/:projectId/tasks/:taskKey/actions",
|
|
29125
|
+
"GET /projects/:projectId/tasks/:taskKey/agent-activity/stream",
|
|
29126
|
+
"GET /projects/:projectId/tasks/:taskKey/external-review/draft",
|
|
29127
|
+
"GET /projects/:projectId/tasks/:taskKey/review-rounds",
|
|
29128
|
+
"GET /projects/:projectId/tracker-status",
|
|
29129
|
+
"GET /projects/:projectId/tracker/fields",
|
|
29130
|
+
"GET /runner/reg-token",
|
|
29131
|
+
"GET /runners",
|
|
29132
|
+
"GET /sandbox/reg-token",
|
|
29133
|
+
"POST /admin/pause",
|
|
29134
|
+
"POST /api/admin/farms/:farmId/assign",
|
|
29135
|
+
"POST /api/admin/farms/:farmId/unassign",
|
|
29136
|
+
"POST /api/admin/registrations/:id/approve",
|
|
29137
|
+
"POST /api/admin/registrations/:id/reject",
|
|
29138
|
+
"POST /api/admin/runners/:runnerId/owner",
|
|
29139
|
+
"POST /api/admin/users",
|
|
29140
|
+
"POST /api/admin/users/:email/activate",
|
|
29141
|
+
"POST /api/admin/users/:email/block",
|
|
29142
|
+
"POST /api/projects/:projectId/tasks/:taskKey/develop",
|
|
29143
|
+
"POST /api/projects/:projectId/tasks/:taskKey/observer-answer",
|
|
29144
|
+
"POST /api/projects/:projectId/tasks/:taskKey/plan-review",
|
|
29145
|
+
"POST /api/projects/:projectId/tasks/:taskKey/stage",
|
|
29146
|
+
"POST /api/projects/:projectId/tasks/:taskKey/workflow-route",
|
|
29147
|
+
"POST /bug-report",
|
|
29148
|
+
"POST /projects/:projectId/actions/:actionId/run",
|
|
29149
|
+
"POST /projects/:projectId/adr-backfill/discard",
|
|
29150
|
+
"POST /projects/:projectId/adr-backfill/promote",
|
|
29151
|
+
"POST /projects/:projectId/check-readiness",
|
|
29152
|
+
"POST /projects/:projectId/clusters/:anchorKey/reject",
|
|
29153
|
+
"POST /projects/:projectId/creds",
|
|
29154
|
+
"POST /projects/:projectId/detach-from-farm",
|
|
29155
|
+
"POST /projects/:projectId/gh-runner/connect",
|
|
29156
|
+
"POST /projects/:projectId/gh-runner/disconnect",
|
|
29157
|
+
"POST /projects/:projectId/model-bootstrap",
|
|
29158
|
+
"POST /projects/:projectId/move-to-farm",
|
|
29159
|
+
"POST /projects/:projectId/pause",
|
|
29160
|
+
"POST /projects/:projectId/refresh-context-status",
|
|
29161
|
+
"POST /projects/:projectId/retry-init",
|
|
29162
|
+
"POST /projects/:projectId/secrets",
|
|
29163
|
+
"POST /projects/:projectId/sync-config",
|
|
29164
|
+
"POST /projects/:projectId/sync-context",
|
|
29165
|
+
"POST /projects/:projectId/tasks/:taskKey/actions/:actionId/run",
|
|
29166
|
+
"POST /projects/:projectId/tasks/:taskKey/approve",
|
|
29167
|
+
"POST /projects/:projectId/tasks/:taskKey/build-flag",
|
|
29168
|
+
"POST /projects/:projectId/tasks/:taskKey/cancel",
|
|
29169
|
+
"POST /projects/:projectId/tasks/:taskKey/comment",
|
|
29170
|
+
"POST /projects/:projectId/tasks/:taskKey/external-review/close",
|
|
29171
|
+
"POST /projects/:projectId/tasks/:taskKey/external-review/dismiss",
|
|
29172
|
+
"POST /projects/:projectId/tasks/:taskKey/external-review/publish",
|
|
29173
|
+
"POST /projects/:projectId/tasks/:taskKey/external-review/rerun",
|
|
29174
|
+
"POST /projects/:projectId/tasks/:taskKey/external-review/start",
|
|
29175
|
+
"POST /projects/:projectId/tasks/:taskKey/force-done",
|
|
29176
|
+
"POST /projects/:projectId/tasks/:taskKey/open-pr",
|
|
29177
|
+
"POST /projects/:projectId/tasks/:taskKey/pause",
|
|
29178
|
+
"POST /projects/:projectId/tasks/:taskKey/publish-branch",
|
|
29179
|
+
"POST /projects/:projectId/tasks/:taskKey/reject",
|
|
29180
|
+
"POST /projects/:projectId/tasks/:taskKey/restart",
|
|
29181
|
+
"POST /projects/:projectId/tasks/:taskKey/retry",
|
|
29182
|
+
"POST /projects/:projectId/tasks/:taskKey/to-backlog",
|
|
29183
|
+
"POST /projects/:projectId/tasks/:taskKey/tracker-status",
|
|
29184
|
+
"POST /projects/:projectId/tasks/:taskKey/unarchive",
|
|
29185
|
+
"POST /projects/:projectId/tracker-sync",
|
|
29186
|
+
"POST /runner/reg-token",
|
|
29187
|
+
"POST /sandbox/reg-token",
|
|
29188
|
+
"PUT /api/admin/farms/:farmId/quota",
|
|
29189
|
+
"PUT /api/admin/settings",
|
|
29190
|
+
"PUT /api/notifications/settings",
|
|
29191
|
+
"PUT /projects/:projectId/adr/:file",
|
|
29192
|
+
"PUT /projects/:projectId/prompts/:role",
|
|
29193
|
+
"PUT /projects/:projectId/tasks/:taskKey/branch-name",
|
|
29194
|
+
"PUT /projects/:projectId/tasks/:taskKey/cluster-base",
|
|
29195
|
+
"PUT /projects/:projectId/tasks/:taskKey/commit-summary",
|
|
29196
|
+
"PUT /projects/:projectId/tasks/:taskKey/description",
|
|
29197
|
+
"PUT /projects/:projectId/tasks/:taskKey/solution"
|
|
29198
|
+
];
|
|
29199
|
+
|
|
29075
29200
|
// src/http/browser-security.ts
|
|
29076
29201
|
init_auth();
|
|
29077
29202
|
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
@@ -38000,6 +38125,7 @@ function clearPasswordAttempts(key) {
|
|
|
38000
38125
|
|
|
38001
38126
|
// src/routes/auth.ts
|
|
38002
38127
|
init_users();
|
|
38128
|
+
init_route_registry();
|
|
38003
38129
|
async function authRoutes(app, deps) {
|
|
38004
38130
|
const { db, config, mailer } = deps;
|
|
38005
38131
|
const loginByIp = createAbuseThrottle({ windowMs: 15 * 6e4, max: 30 });
|
|
@@ -38040,18 +38166,21 @@ async function authRoutes(app, deps) {
|
|
|
38040
38166
|
function registerAccountRoutes() {
|
|
38041
38167
|
app.post(
|
|
38042
38168
|
"/auth/login",
|
|
38043
|
-
{
|
|
38044
|
-
|
|
38045
|
-
|
|
38046
|
-
|
|
38047
|
-
|
|
38048
|
-
|
|
38049
|
-
|
|
38050
|
-
|
|
38051
|
-
|
|
38169
|
+
routeMeta({
|
|
38170
|
+
surface: "bootstrap",
|
|
38171
|
+
operationId: "auth.login",
|
|
38172
|
+
authPolicy: "public",
|
|
38173
|
+
stability: "preview",
|
|
38174
|
+
summary: "Sign in: send a magic link",
|
|
38175
|
+
requestSchema: {
|
|
38176
|
+
type: "object",
|
|
38177
|
+
required: ["email"],
|
|
38178
|
+
properties: {
|
|
38179
|
+
email: { type: "string", minLength: 3, maxLength: 254 },
|
|
38180
|
+
pwa_nonce: { type: "string", minLength: 1, maxLength: 64 }
|
|
38052
38181
|
}
|
|
38053
38182
|
}
|
|
38054
|
-
},
|
|
38183
|
+
}),
|
|
38055
38184
|
async (req, reply) => {
|
|
38056
38185
|
const email = (req.body.email ?? "").trim().toLowerCase();
|
|
38057
38186
|
if (loginThrottled(email, req.ip)) {
|
|
@@ -38069,18 +38198,21 @@ async function authRoutes(app, deps) {
|
|
|
38069
38198
|
);
|
|
38070
38199
|
app.post(
|
|
38071
38200
|
"/auth/login-start",
|
|
38072
|
-
{
|
|
38073
|
-
|
|
38074
|
-
|
|
38075
|
-
|
|
38076
|
-
|
|
38077
|
-
|
|
38078
|
-
|
|
38079
|
-
|
|
38080
|
-
|
|
38201
|
+
routeMeta({
|
|
38202
|
+
surface: "bootstrap",
|
|
38203
|
+
operationId: "auth.loginStart",
|
|
38204
|
+
authPolicy: "public",
|
|
38205
|
+
stability: "preview",
|
|
38206
|
+
summary: "Start a sign-in, without saying whether the account exists",
|
|
38207
|
+
requestSchema: {
|
|
38208
|
+
type: "object",
|
|
38209
|
+
required: ["email"],
|
|
38210
|
+
properties: {
|
|
38211
|
+
email: { type: "string", minLength: 3, maxLength: 254 },
|
|
38212
|
+
pwa_nonce: { type: "string", minLength: 1, maxLength: 64 }
|
|
38081
38213
|
}
|
|
38082
38214
|
}
|
|
38083
|
-
},
|
|
38215
|
+
}),
|
|
38084
38216
|
async (req, reply) => {
|
|
38085
38217
|
const email = (req.body.email ?? "").trim().toLowerCase();
|
|
38086
38218
|
if (loginThrottled(email, req.ip)) {
|
|
@@ -38102,18 +38234,21 @@ async function authRoutes(app, deps) {
|
|
|
38102
38234
|
);
|
|
38103
38235
|
app.post(
|
|
38104
38236
|
"/auth/login-password",
|
|
38105
|
-
{
|
|
38106
|
-
|
|
38107
|
-
|
|
38108
|
-
|
|
38109
|
-
|
|
38110
|
-
|
|
38111
|
-
|
|
38112
|
-
|
|
38113
|
-
|
|
38237
|
+
routeMeta({
|
|
38238
|
+
surface: "bootstrap",
|
|
38239
|
+
operationId: "auth.loginPassword",
|
|
38240
|
+
authPolicy: "public",
|
|
38241
|
+
stability: "preview",
|
|
38242
|
+
summary: "Sign in with a password",
|
|
38243
|
+
requestSchema: {
|
|
38244
|
+
type: "object",
|
|
38245
|
+
required: ["email", "password"],
|
|
38246
|
+
properties: {
|
|
38247
|
+
email: { type: "string", minLength: 3, maxLength: 254 },
|
|
38248
|
+
password: { type: "string", minLength: 1, maxLength: MAX_PASSWORD_LENGTH }
|
|
38114
38249
|
}
|
|
38115
38250
|
}
|
|
38116
|
-
},
|
|
38251
|
+
}),
|
|
38117
38252
|
async (req, reply) => {
|
|
38118
38253
|
const email = (req.body.email ?? "").trim().toLowerCase();
|
|
38119
38254
|
const password = req.body.password ?? "";
|
|
@@ -38137,15 +38272,18 @@ async function authRoutes(app, deps) {
|
|
|
38137
38272
|
);
|
|
38138
38273
|
app.post(
|
|
38139
38274
|
"/auth/set-password",
|
|
38140
|
-
{
|
|
38141
|
-
|
|
38142
|
-
|
|
38143
|
-
|
|
38144
|
-
|
|
38145
|
-
|
|
38146
|
-
|
|
38275
|
+
routeMeta({
|
|
38276
|
+
surface: "bootstrap",
|
|
38277
|
+
operationId: "auth.setPassword",
|
|
38278
|
+
authPolicy: "session",
|
|
38279
|
+
stability: "preview",
|
|
38280
|
+
summary: "Set the signed-in account's password",
|
|
38281
|
+
requestSchema: {
|
|
38282
|
+
type: "object",
|
|
38283
|
+
required: ["password"],
|
|
38284
|
+
properties: { password: { type: "string", minLength: 1, maxLength: 512 } }
|
|
38147
38285
|
}
|
|
38148
|
-
},
|
|
38286
|
+
}),
|
|
38149
38287
|
async (req, reply) => {
|
|
38150
38288
|
const session = await requireSession(db, req, reply);
|
|
38151
38289
|
if (!session) return reply;
|
|
@@ -38164,7 +38302,14 @@ async function authRoutes(app, deps) {
|
|
|
38164
38302
|
return reply.send({ status: "ok" });
|
|
38165
38303
|
}
|
|
38166
38304
|
);
|
|
38167
|
-
app.get("/auth/verify",
|
|
38305
|
+
app.get("/auth/verify", routeMeta({
|
|
38306
|
+
surface: "bootstrap",
|
|
38307
|
+
operationId: "auth.verify",
|
|
38308
|
+
authPolicy: "public",
|
|
38309
|
+
// A redirect from an email link, not a JSON operation to generate a client for.
|
|
38310
|
+
stability: "internal",
|
|
38311
|
+
summary: "Redeem a magic link"
|
|
38312
|
+
}), async (req, reply) => {
|
|
38168
38313
|
const wantsHtml = (req.headers.accept ?? "").toLowerCase().includes("text/html");
|
|
38169
38314
|
const token = (req.query.token ?? "").trim();
|
|
38170
38315
|
if (!token) {
|
|
@@ -38186,7 +38331,14 @@ async function authRoutes(app, deps) {
|
|
|
38186
38331
|
});
|
|
38187
38332
|
}
|
|
38188
38333
|
function registerSessionRoutes() {
|
|
38189
|
-
app.get("/auth/me",
|
|
38334
|
+
app.get("/auth/me", routeMeta({
|
|
38335
|
+
surface: "bootstrap",
|
|
38336
|
+
operationId: "auth.me",
|
|
38337
|
+
// A cookie, or the single-owner's credential on the local profile.
|
|
38338
|
+
authPolicy: "application_actor",
|
|
38339
|
+
stability: "preview",
|
|
38340
|
+
summary: "Who is signed in"
|
|
38341
|
+
}), async (req, reply) => {
|
|
38190
38342
|
const session = await readSession(db, req);
|
|
38191
38343
|
if (!session) return reply.status(401).send({ error: "no_session" });
|
|
38192
38344
|
const user = await findUser(db, session.email);
|
|
@@ -38198,7 +38350,13 @@ async function authRoutes(app, deps) {
|
|
|
38198
38350
|
singleOwner
|
|
38199
38351
|
};
|
|
38200
38352
|
});
|
|
38201
|
-
app.post("/auth/logout",
|
|
38353
|
+
app.post("/auth/logout", routeMeta({
|
|
38354
|
+
surface: "bootstrap",
|
|
38355
|
+
operationId: "auth.logout",
|
|
38356
|
+
authPolicy: "public",
|
|
38357
|
+
stability: "preview",
|
|
38358
|
+
summary: "Sign out"
|
|
38359
|
+
}), async (req, reply) => {
|
|
38202
38360
|
const cookie2 = req.cookies[SESSION_COOKIE_NAME];
|
|
38203
38361
|
if (cookie2) {
|
|
38204
38362
|
const unsigned = req.unsignCookie(cookie2);
|
|
@@ -38209,7 +38367,13 @@ async function authRoutes(app, deps) {
|
|
|
38209
38367
|
reply.clearCookie(SESSION_COOKIE_NAME, { path: "/" });
|
|
38210
38368
|
return { status: "ok" };
|
|
38211
38369
|
});
|
|
38212
|
-
app.get("/auth/pwa-ready",
|
|
38370
|
+
app.get("/auth/pwa-ready", routeMeta({
|
|
38371
|
+
surface: "bootstrap",
|
|
38372
|
+
operationId: "auth.pwaReady",
|
|
38373
|
+
authPolicy: "public",
|
|
38374
|
+
stability: "preview",
|
|
38375
|
+
summary: "Whether an installed app's sign-in has completed"
|
|
38376
|
+
}), async (req, reply) => {
|
|
38213
38377
|
const nonce = (req.query.nonce ?? "").trim();
|
|
38214
38378
|
if (!nonce) return reply.status(400).send({ error: "missing_nonce" });
|
|
38215
38379
|
const result = await consumePwaNonce(db, nonce);
|
|
@@ -38234,6 +38398,7 @@ import { nanoid as nanoid41 } from "nanoid";
|
|
|
38234
38398
|
init_settings();
|
|
38235
38399
|
init_users();
|
|
38236
38400
|
init_cli_tokens();
|
|
38401
|
+
init_route_registry();
|
|
38237
38402
|
var REQUEST_TTL_MS = 15 * 6e4;
|
|
38238
38403
|
var APPROVAL_TTL_MS = 24 * 60 * 6e4;
|
|
38239
38404
|
async function cliBootstrapRoutes(app, deps) {
|
|
@@ -38267,15 +38432,18 @@ async function cliBootstrapRoutes(app, deps) {
|
|
|
38267
38432
|
}
|
|
38268
38433
|
app.post(
|
|
38269
38434
|
"/cli/bootstrap-request",
|
|
38270
|
-
{
|
|
38271
|
-
|
|
38272
|
-
|
|
38273
|
-
|
|
38274
|
-
|
|
38275
|
-
|
|
38276
|
-
|
|
38435
|
+
routeMeta({
|
|
38436
|
+
surface: "bootstrap",
|
|
38437
|
+
operationId: "bootstrap.request",
|
|
38438
|
+
authPolicy: "public",
|
|
38439
|
+
stability: "preview",
|
|
38440
|
+
summary: "A CLI asks to be connected to an account",
|
|
38441
|
+
requestSchema: {
|
|
38442
|
+
type: "object",
|
|
38443
|
+
required: ["email"],
|
|
38444
|
+
properties: { email: { type: "string", minLength: 3, maxLength: 254 } }
|
|
38277
38445
|
}
|
|
38278
|
-
},
|
|
38446
|
+
}),
|
|
38279
38447
|
async (req, reply) => {
|
|
38280
38448
|
const email = (req.body.email ?? "").trim().toLowerCase();
|
|
38281
38449
|
if (!isValidEmail(email)) {
|
|
@@ -38323,6 +38491,14 @@ async function cliBootstrapRoutes(app, deps) {
|
|
|
38323
38491
|
);
|
|
38324
38492
|
app.get(
|
|
38325
38493
|
"/cli/authorize",
|
|
38494
|
+
routeMeta({
|
|
38495
|
+
surface: "bootstrap",
|
|
38496
|
+
operationId: "bootstrap.authorizePage",
|
|
38497
|
+
authPolicy: "public",
|
|
38498
|
+
// An HTML page, not a JSON operation to generate a client for.
|
|
38499
|
+
stability: "internal",
|
|
38500
|
+
summary: "The page where a person confirms a CLI connection"
|
|
38501
|
+
}),
|
|
38326
38502
|
async (req, reply) => {
|
|
38327
38503
|
const requestId = (req.query.request ?? "").trim();
|
|
38328
38504
|
const row = requestId ? await loadRequest(requestId) : void 0;
|
|
@@ -38371,15 +38547,19 @@ async function cliBootstrapRoutes(app, deps) {
|
|
|
38371
38547
|
);
|
|
38372
38548
|
app.post(
|
|
38373
38549
|
"/cli/authorize/confirm",
|
|
38374
|
-
{
|
|
38375
|
-
|
|
38376
|
-
|
|
38377
|
-
|
|
38378
|
-
|
|
38379
|
-
|
|
38380
|
-
|
|
38550
|
+
routeMeta({
|
|
38551
|
+
surface: "bootstrap",
|
|
38552
|
+
operationId: "bootstrap.authorizeConfirm",
|
|
38553
|
+
// A cookie, or the single-owner's credential on the local profile.
|
|
38554
|
+
authPolicy: "application_actor",
|
|
38555
|
+
stability: "preview",
|
|
38556
|
+
summary: "Confirm a CLI connection as the signed-in person",
|
|
38557
|
+
requestSchema: {
|
|
38558
|
+
type: "object",
|
|
38559
|
+
required: ["request"],
|
|
38560
|
+
properties: { request: { type: "string", minLength: 1, maxLength: 64 } }
|
|
38381
38561
|
}
|
|
38382
|
-
},
|
|
38562
|
+
}),
|
|
38383
38563
|
async (req, reply) => {
|
|
38384
38564
|
const session = await readSession(db, req);
|
|
38385
38565
|
if (!session || !await isAllowedLogin(db, config, session.email)) {
|
|
@@ -38397,15 +38577,18 @@ async function cliBootstrapRoutes(app, deps) {
|
|
|
38397
38577
|
);
|
|
38398
38578
|
app.post(
|
|
38399
38579
|
"/cli/authorize/email",
|
|
38400
|
-
{
|
|
38401
|
-
|
|
38402
|
-
|
|
38403
|
-
|
|
38404
|
-
|
|
38405
|
-
|
|
38406
|
-
|
|
38580
|
+
routeMeta({
|
|
38581
|
+
surface: "bootstrap",
|
|
38582
|
+
operationId: "bootstrap.authorizeEmail",
|
|
38583
|
+
authPolicy: "public",
|
|
38584
|
+
stability: "preview",
|
|
38585
|
+
summary: "Confirm a CLI connection by email instead",
|
|
38586
|
+
requestSchema: {
|
|
38587
|
+
type: "object",
|
|
38588
|
+
required: ["request"],
|
|
38589
|
+
properties: { request: { type: "string", minLength: 1, maxLength: 64 } }
|
|
38407
38590
|
}
|
|
38408
|
-
},
|
|
38591
|
+
}),
|
|
38409
38592
|
async (req, reply) => {
|
|
38410
38593
|
const requestId = (req.body.request ?? "").trim();
|
|
38411
38594
|
const row = await loadRequest(requestId);
|
|
@@ -38423,6 +38606,13 @@ async function cliBootstrapRoutes(app, deps) {
|
|
|
38423
38606
|
);
|
|
38424
38607
|
app.get(
|
|
38425
38608
|
"/cli/bootstrap-confirm",
|
|
38609
|
+
routeMeta({
|
|
38610
|
+
surface: "bootstrap",
|
|
38611
|
+
operationId: "bootstrap.confirm",
|
|
38612
|
+
authPolicy: "public",
|
|
38613
|
+
stability: "internal",
|
|
38614
|
+
summary: "Redeem the CLI connection link from the email"
|
|
38615
|
+
}),
|
|
38426
38616
|
async (req, reply) => {
|
|
38427
38617
|
const token = (req.query.token ?? "").trim();
|
|
38428
38618
|
reply.type("text/html");
|
|
@@ -38473,6 +38663,13 @@ async function cliBootstrapRoutes(app, deps) {
|
|
|
38473
38663
|
);
|
|
38474
38664
|
app.get(
|
|
38475
38665
|
"/cli/bootstrap-poll",
|
|
38666
|
+
routeMeta({
|
|
38667
|
+
surface: "bootstrap",
|
|
38668
|
+
operationId: "bootstrap.poll",
|
|
38669
|
+
authPolicy: "public",
|
|
38670
|
+
stability: "preview",
|
|
38671
|
+
summary: "A CLI polls whether its connection was confirmed"
|
|
38672
|
+
}),
|
|
38476
38673
|
async (req, reply) => {
|
|
38477
38674
|
const requestId = (req.query.request ?? "").trim();
|
|
38478
38675
|
if (!requestId) return reply.status(400).send({ error: "missing_request" });
|
|
@@ -38512,13 +38709,20 @@ import { nanoid as nanoid42 } from "nanoid";
|
|
|
38512
38709
|
init_auth();
|
|
38513
38710
|
init_settings();
|
|
38514
38711
|
init_users();
|
|
38712
|
+
init_route_registry();
|
|
38515
38713
|
var CHALLENGE_TTL_MS = 10 * 6e4;
|
|
38516
38714
|
var MIN_FILL_MS = 2e3;
|
|
38517
38715
|
async function registrationRoutes(app, deps) {
|
|
38518
38716
|
const { db, config, mailer } = deps;
|
|
38519
38717
|
const submitByIp = createAbuseThrottle({ windowMs: 60 * 6e4, max: 10 });
|
|
38520
38718
|
const challengeByIp = createAbuseThrottle({ windowMs: 60 * 6e4, max: 60 });
|
|
38521
|
-
app.get("/auth/register-challenge",
|
|
38719
|
+
app.get("/auth/register-challenge", routeMeta({
|
|
38720
|
+
surface: "bootstrap",
|
|
38721
|
+
operationId: "auth.registerChallenge",
|
|
38722
|
+
authPolicy: "public",
|
|
38723
|
+
stability: "preview",
|
|
38724
|
+
summary: "The challenge a registration form answers"
|
|
38725
|
+
}), async (req, reply) => {
|
|
38522
38726
|
if (!challengeByIp.take(`ip:${req.ip}`)) {
|
|
38523
38727
|
return reply.status(429).send({ error: "too_many_requests" });
|
|
38524
38728
|
}
|
|
@@ -38532,21 +38736,24 @@ async function registrationRoutes(app, deps) {
|
|
|
38532
38736
|
});
|
|
38533
38737
|
app.post(
|
|
38534
38738
|
"/auth/register",
|
|
38535
|
-
{
|
|
38536
|
-
|
|
38537
|
-
|
|
38538
|
-
|
|
38539
|
-
|
|
38540
|
-
|
|
38541
|
-
|
|
38542
|
-
|
|
38543
|
-
|
|
38544
|
-
|
|
38545
|
-
|
|
38546
|
-
}
|
|
38739
|
+
routeMeta({
|
|
38740
|
+
surface: "bootstrap",
|
|
38741
|
+
operationId: "auth.register",
|
|
38742
|
+
authPolicy: "public",
|
|
38743
|
+
stability: "preview",
|
|
38744
|
+
summary: "Ask for an account",
|
|
38745
|
+
requestSchema: {
|
|
38746
|
+
type: "object",
|
|
38747
|
+
required: ["email", "challengeId"],
|
|
38748
|
+
properties: {
|
|
38749
|
+
email: { type: "string", minLength: 3, maxLength: 254 },
|
|
38750
|
+
name: { type: "string", maxLength: 200 },
|
|
38751
|
+
note: { type: "string", maxLength: 1e3 },
|
|
38752
|
+
challengeId: { type: "string", minLength: 1, maxLength: 64 },
|
|
38753
|
+
website: { type: "string", maxLength: 200 }
|
|
38547
38754
|
}
|
|
38548
38755
|
}
|
|
38549
|
-
},
|
|
38756
|
+
}),
|
|
38550
38757
|
async (req, reply) => {
|
|
38551
38758
|
const email = (req.body.email ?? "").trim().toLowerCase();
|
|
38552
38759
|
const name = (req.body.name ?? "").trim() || null;
|
|
@@ -38594,7 +38801,13 @@ async function registrationRoutes(app, deps) {
|
|
|
38594
38801
|
return reply.status(202).send({ status: "pending" });
|
|
38595
38802
|
}
|
|
38596
38803
|
);
|
|
38597
|
-
app.get("/auth/register-approve",
|
|
38804
|
+
app.get("/auth/register-approve", routeMeta({
|
|
38805
|
+
surface: "bootstrap",
|
|
38806
|
+
operationId: "auth.registerApprove",
|
|
38807
|
+
authPolicy: "public",
|
|
38808
|
+
stability: "internal",
|
|
38809
|
+
summary: "Approve a registration request from its link"
|
|
38810
|
+
}), async (req, reply) => {
|
|
38598
38811
|
const token = (req.query.token ?? "").trim();
|
|
38599
38812
|
const row = token ? await db.get(`SELECT id, email, name, note, status, approve_token as approveToken,
|
|
38600
38813
|
reject_token as rejectToken, created_at as createdAt,
|
|
@@ -38614,7 +38827,13 @@ async function registrationRoutes(app, deps) {
|
|
|
38614
38827
|
)
|
|
38615
38828
|
);
|
|
38616
38829
|
});
|
|
38617
|
-
app.get("/auth/register-reject",
|
|
38830
|
+
app.get("/auth/register-reject", routeMeta({
|
|
38831
|
+
surface: "bootstrap",
|
|
38832
|
+
operationId: "auth.registerReject",
|
|
38833
|
+
authPolicy: "public",
|
|
38834
|
+
stability: "internal",
|
|
38835
|
+
summary: "Reject a registration request from its link"
|
|
38836
|
+
}), async (req, reply) => {
|
|
38618
38837
|
const token = (req.query.token ?? "").trim();
|
|
38619
38838
|
const row = token ? await db.get(`SELECT id, email, name, status FROM registration_requests WHERE reject_token = ?`, token) : void 0;
|
|
38620
38839
|
if (!row) {
|
|
@@ -38816,6 +39035,7 @@ function safeNext(next) {
|
|
|
38816
39035
|
}
|
|
38817
39036
|
|
|
38818
39037
|
// src/routes/install.ts
|
|
39038
|
+
init_route_registry();
|
|
38819
39039
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
38820
39040
|
import { dirname as dirname3, resolve as resolve2 } from "node:path";
|
|
38821
39041
|
import { existsSync as existsSync6 } from "node:fs";
|
|
@@ -38826,7 +39046,14 @@ var SCRIPT_PATH = [
|
|
|
38826
39046
|
resolve2(MODULE_DIR2, "../../../deploy/install-sandbox.sh")
|
|
38827
39047
|
].find((p) => existsSync6(p)) ?? resolve2(MODULE_DIR2, "../../../deploy/install-sandbox.sh");
|
|
38828
39048
|
async function installRoutes(app) {
|
|
38829
|
-
app.get("/install-sandbox.sh",
|
|
39049
|
+
app.get("/install-sandbox.sh", routeMeta({
|
|
39050
|
+
surface: "static",
|
|
39051
|
+
operationId: "ops.installSandboxScript",
|
|
39052
|
+
authPolicy: "public",
|
|
39053
|
+
// A shell script, not a JSON operation.
|
|
39054
|
+
stability: "internal",
|
|
39055
|
+
summary: "The SANDBOX install script"
|
|
39056
|
+
}), async (_req, reply) => {
|
|
38830
39057
|
try {
|
|
38831
39058
|
const body = await readFile(SCRIPT_PATH, "utf8");
|
|
38832
39059
|
return reply.type("text/x-shellscript; charset=utf-8").send(body);
|
|
@@ -41561,9 +41788,11 @@ async function buildServer(config, db) {
|
|
|
41561
41788
|
}
|
|
41562
41789
|
});
|
|
41563
41790
|
}
|
|
41564
|
-
const routeRegistry = installRouteGuard(app, "
|
|
41565
|
-
|
|
41566
|
-
|
|
41791
|
+
const routeRegistry = installRouteGuard(app, "enforce", { warn: (message) => app.log.warn(message) }, [
|
|
41792
|
+
...LEGACY_ROUTES,
|
|
41793
|
+
...STATIC_PLUGIN_ROUTES
|
|
41794
|
+
]);
|
|
41795
|
+
app.decorate("routeRegistry", routeRegistry);
|
|
41567
41796
|
const mailer = buildMailer(loadMailerConfig(), app.log);
|
|
41568
41797
|
app.log.info({ mailer: mailer.mode }, "mailer mode");
|
|
41569
41798
|
const notifyDeps = {
|
|
@@ -41785,10 +42014,9 @@ async function buildServer(config, db) {
|
|
|
41785
42014
|
app.log.info(
|
|
41786
42015
|
{
|
|
41787
42016
|
classified: routeRegistry.list().length,
|
|
41788
|
-
|
|
41789
|
-
invalid: routeRegistry.invalidCount()
|
|
42017
|
+
legacy: routeRegistry.unclassifiedRoutes().filter((r) => !STATIC_PLUGIN_ROUTES.includes(r)).length
|
|
41790
42018
|
},
|
|
41791
|
-
"route registry: routes still
|
|
42019
|
+
"route registry: legacy routes still served for the browser"
|
|
41792
42020
|
);
|
|
41793
42021
|
});
|
|
41794
42022
|
await registerSpaHosts(app, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-partner/codepipe-hub",
|
|
3
|
-
"version": "0.14.1-dev.
|
|
3
|
+
"version": "0.14.1-dev.424.ge3c8cc77",
|
|
4
4
|
"description": "The CodePipe hub, packaged for one machine: the CLI installs it on demand for the local profile.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./hub/dist/index.js",
|