@kelpie/server 0.12.0 → 0.13.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/dist/app.d.ts +13 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +8 -1
- package/dist/app.js.map +1 -1
- package/dist/boot.d.ts.map +1 -1
- package/dist/boot.js +1 -0
- package/dist/boot.js.map +1 -1
- package/dist/lib/apiKeyScopes.d.ts.map +1 -1
- package/dist/lib/apiKeyScopes.js +6 -0
- package/dist/lib/apiKeyScopes.js.map +1 -1
- package/dist/lib/columns.d.ts +6 -0
- package/dist/lib/columns.d.ts.map +1 -1
- package/dist/lib/columns.js +5 -3
- package/dist/lib/columns.js.map +1 -1
- package/dist/lib/config.d.ts +17 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +3 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/egress.d.ts.map +1 -1
- package/dist/lib/egress.js +8 -4
- package/dist/lib/egress.js.map +1 -1
- package/dist/lib/errors.d.ts +3 -3
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +13 -5
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/ids.d.ts +11 -0
- package/dist/lib/ids.d.ts.map +1 -1
- package/dist/lib/ids.js +11 -0
- package/dist/lib/ids.js.map +1 -1
- package/dist/lib/kelpieConfigFile.d.ts +15 -1
- package/dist/lib/kelpieConfigFile.d.ts.map +1 -1
- package/dist/lib/kelpieConfigFile.js +33 -1
- package/dist/lib/kelpieConfigFile.js.map +1 -1
- package/dist/modules/agent-tasks/catalog.d.ts.map +1 -1
- package/dist/modules/agent-tasks/catalog.js +32 -1
- package/dist/modules/agent-tasks/catalog.js.map +1 -1
- package/dist/modules/agent-tasks/index.d.ts.map +1 -1
- package/dist/modules/agent-tasks/index.js +1 -0
- package/dist/modules/agent-tasks/index.js.map +1 -1
- package/dist/modules/agent-tasks/resolve.d.ts.map +1 -1
- package/dist/modules/agent-tasks/resolve.js +5 -0
- package/dist/modules/agent-tasks/resolve.js.map +1 -1
- package/dist/modules/agent-tasks/routes.d.ts +2 -0
- package/dist/modules/agent-tasks/routes.d.ts.map +1 -1
- package/dist/modules/attachedRecords.d.ts +22 -8
- package/dist/modules/attachedRecords.d.ts.map +1 -1
- package/dist/modules/attachedRecords.js +42 -5
- package/dist/modules/attachedRecords.js.map +1 -1
- package/dist/modules/auth/index.d.ts.map +1 -1
- package/dist/modules/auth/index.js +1 -0
- package/dist/modules/auth/index.js.map +1 -1
- package/dist/modules/auth/repository.d.ts +14 -0
- package/dist/modules/auth/repository.d.ts.map +1 -1
- package/dist/modules/auth/repository.js +20 -2
- package/dist/modules/auth/repository.js.map +1 -1
- package/dist/modules/auth/routes.d.ts.map +1 -1
- package/dist/modules/auth/routes.js +30 -0
- package/dist/modules/auth/routes.js.map +1 -1
- package/dist/modules/auth/service.d.ts +28 -0
- package/dist/modules/auth/service.d.ts.map +1 -1
- package/dist/modules/auth/service.js +72 -4
- package/dist/modules/auth/service.js.map +1 -1
- package/dist/modules/companies/repository.d.ts.map +1 -1
- package/dist/modules/companies/repository.js +1 -1
- package/dist/modules/companies/repository.js.map +1 -1
- package/dist/modules/companies/routes.d.ts +2 -4
- package/dist/modules/companies/routes.d.ts.map +1 -1
- package/dist/modules/companies/routes.js +6 -11
- package/dist/modules/companies/routes.js.map +1 -1
- package/dist/modules/companies/schema.d.ts +13 -28
- package/dist/modules/companies/schema.d.ts.map +1 -1
- package/dist/modules/companies/schema.js +2 -2
- package/dist/modules/companies/schema.js.map +1 -1
- package/dist/modules/companies/service.d.ts +2 -3
- package/dist/modules/companies/service.d.ts.map +1 -1
- package/dist/modules/companies/service.js +4 -7
- package/dist/modules/companies/service.js.map +1 -1
- package/dist/modules/core.d.ts.map +1 -1
- package/dist/modules/core.js +2 -0
- package/dist/modules/core.js.map +1 -1
- package/dist/modules/custom-fields/repository.d.ts.map +1 -1
- package/dist/modules/custom-fields/repository.js +2 -0
- package/dist/modules/custom-fields/repository.js.map +1 -1
- package/dist/modules/custom-fields/routes.d.ts +1 -0
- package/dist/modules/custom-fields/routes.d.ts.map +1 -1
- package/dist/modules/dashboard/index.d.ts.map +1 -1
- package/dist/modules/dashboard/index.js +1 -0
- package/dist/modules/dashboard/index.js.map +1 -1
- package/dist/modules/dashboard/repository.d.ts +13 -0
- package/dist/modules/dashboard/repository.d.ts.map +1 -1
- package/dist/modules/dashboard/repository.js +36 -0
- package/dist/modules/dashboard/repository.js.map +1 -1
- package/dist/modules/dashboard/routes.d.ts.map +1 -1
- package/dist/modules/dashboard/routes.js +13 -0
- package/dist/modules/dashboard/routes.js.map +1 -1
- package/dist/modules/dashboard/service.d.ts +11 -0
- package/dist/modules/dashboard/service.d.ts.map +1 -1
- package/dist/modules/dashboard/service.js +24 -1
- package/dist/modules/dashboard/service.js.map +1 -1
- package/dist/modules/decisions/repository.d.ts.map +1 -1
- package/dist/modules/decisions/repository.js +2 -0
- package/dist/modules/decisions/repository.js.map +1 -1
- package/dist/modules/decisions/routes.d.ts +2 -0
- package/dist/modules/decisions/routes.d.ts.map +1 -1
- package/dist/modules/events/attendances.d.ts +35 -0
- package/dist/modules/events/attendances.d.ts.map +1 -0
- package/dist/modules/events/attendances.js +157 -0
- package/dist/modules/events/attendances.js.map +1 -0
- package/dist/modules/events/catalog.d.ts +35 -0
- package/dist/modules/events/catalog.d.ts.map +1 -0
- package/dist/modules/events/catalog.js +11 -0
- package/dist/modules/events/catalog.js.map +1 -0
- package/dist/modules/events/index.d.ts +10 -0
- package/dist/modules/events/index.d.ts.map +1 -0
- package/dist/modules/events/index.js +47 -0
- package/dist/modules/events/index.js.map +1 -0
- package/dist/modules/events/repository.d.ts +67 -0
- package/dist/modules/events/repository.d.ts.map +1 -0
- package/dist/modules/events/repository.js +212 -0
- package/dist/modules/events/repository.js.map +1 -0
- package/dist/modules/events/routes.d.ts +136 -0
- package/dist/modules/events/routes.d.ts.map +1 -0
- package/dist/modules/events/routes.js +280 -0
- package/dist/modules/events/routes.js.map +1 -0
- package/dist/modules/events/schema.d.ts +626 -0
- package/dist/modules/events/schema.d.ts.map +1 -0
- package/dist/modules/events/schema.js +93 -0
- package/dist/modules/events/schema.js.map +1 -0
- package/dist/modules/events/service.d.ts +68 -0
- package/dist/modules/events/service.d.ts.map +1 -0
- package/dist/modules/events/service.js +262 -0
- package/dist/modules/events/service.js.map +1 -0
- package/dist/modules/events/tools.d.ts +8 -0
- package/dist/modules/events/tools.d.ts.map +1 -0
- package/dist/modules/events/tools.js +80 -0
- package/dist/modules/events/tools.js.map +1 -0
- package/dist/modules/forms/applySubmissionFields.d.ts.map +1 -1
- package/dist/modules/forms/applySubmissionFields.js +7 -5
- package/dist/modules/forms/applySubmissionFields.js.map +1 -1
- package/dist/modules/forms/fillAddresses.d.ts +4 -0
- package/dist/modules/forms/fillAddresses.d.ts.map +1 -0
- package/dist/modules/forms/fillAddresses.js +93 -0
- package/dist/modules/forms/fillAddresses.js.map +1 -0
- package/dist/modules/forms/repository.d.ts +2 -2
- package/dist/modules/forms/repository.d.ts.map +1 -1
- package/dist/modules/forms/repository.js.map +1 -1
- package/dist/modules/forms/routes.d.ts +2 -0
- package/dist/modules/forms/routes.d.ts.map +1 -1
- package/dist/modules/forms/routes.js +2 -2
- package/dist/modules/forms/routes.js.map +1 -1
- package/dist/modules/forms/schema.js +2 -2
- package/dist/modules/forms/schema.js.map +1 -1
- package/dist/modules/forms/service.js +2 -2
- package/dist/modules/forms/service.js.map +1 -1
- package/dist/modules/forms/submission.d.ts +1 -0
- package/dist/modules/forms/submission.d.ts.map +1 -1
- package/dist/modules/forms/submission.js +27 -4
- package/dist/modules/forms/submission.js.map +1 -1
- package/dist/modules/hiring/roles.d.ts.map +1 -1
- package/dist/modules/hiring/roles.js +2 -1
- package/dist/modules/hiring/roles.js.map +1 -1
- package/dist/modules/import-export/addresses.d.ts +22 -0
- package/dist/modules/import-export/addresses.d.ts.map +1 -0
- package/dist/modules/import-export/addresses.js +83 -0
- package/dist/modules/import-export/addresses.js.map +1 -0
- package/dist/modules/import-export/drafts.d.ts +3 -4
- package/dist/modules/import-export/drafts.d.ts.map +1 -1
- package/dist/modules/import-export/drafts.js +4 -5
- package/dist/modules/import-export/drafts.js.map +1 -1
- package/dist/modules/import-export/exportRows.d.ts.map +1 -1
- package/dist/modules/import-export/exportRows.js +3 -3
- package/dist/modules/import-export/exportRows.js.map +1 -1
- package/dist/modules/import-export/plan.d.ts.map +1 -1
- package/dist/modules/import-export/plan.js +17 -2
- package/dist/modules/import-export/plan.js.map +1 -1
- package/dist/modules/import-export/presets.d.ts.map +1 -1
- package/dist/modules/import-export/presets.js +52 -7
- package/dist/modules/import-export/presets.js.map +1 -1
- package/dist/modules/import-export/repository.d.ts +3 -4
- package/dist/modules/import-export/repository.d.ts.map +1 -1
- package/dist/modules/import-export/repository.js +2 -3
- package/dist/modules/import-export/repository.js.map +1 -1
- package/dist/modules/import-export/writes.d.ts.map +1 -1
- package/dist/modules/import-export/writes.js +8 -4
- package/dist/modules/import-export/writes.js.map +1 -1
- package/dist/modules/lists/routes.d.ts +4 -0
- package/dist/modules/lists/routes.d.ts.map +1 -1
- package/dist/modules/notes/routes.d.ts +2 -0
- package/dist/modules/notes/routes.d.ts.map +1 -1
- package/dist/modules/people/repository.d.ts.map +1 -1
- package/dist/modules/people/repository.js +1 -1
- package/dist/modules/people/repository.js.map +1 -1
- package/dist/modules/people/routes.d.ts +2 -2
- package/dist/modules/people/routes.js +6 -6
- package/dist/modules/people/routes.js.map +1 -1
- package/dist/modules/people/schema.d.ts +17 -14
- package/dist/modules/people/schema.d.ts.map +1 -1
- package/dist/modules/people/schema.js +6 -4
- package/dist/modules/people/schema.js.map +1 -1
- package/dist/modules/people/service.d.ts +2 -2
- package/dist/modules/people/service.d.ts.map +1 -1
- package/dist/modules/people/service.js +5 -4
- package/dist/modules/people/service.js.map +1 -1
- package/dist/modules/plans/repository.d.ts +2 -2
- package/dist/modules/plans/repository.d.ts.map +1 -1
- package/dist/modules/plans/routes.d.ts +1 -0
- package/dist/modules/plans/routes.d.ts.map +1 -1
- package/dist/modules/plans/routes.js +4 -4
- package/dist/modules/plans/routes.js.map +1 -1
- package/dist/modules/plans/schema.d.ts +2 -2
- package/dist/modules/plans/schema.d.ts.map +1 -1
- package/dist/modules/plans/schema.js +3 -3
- package/dist/modules/plans/schema.js.map +1 -1
- package/dist/modules/plans/service.d.ts +2 -2
- package/dist/modules/plans/service.d.ts.map +1 -1
- package/dist/modules/plans/service.js.map +1 -1
- package/dist/modules/plans/tools.js +2 -2
- package/dist/modules/plans/tools.js.map +1 -1
- package/dist/modules/recordTargets.d.ts.map +1 -1
- package/dist/modules/recordTargets.js +11 -0
- package/dist/modules/recordTargets.js.map +1 -1
- package/dist/modules/references.d.ts.map +1 -1
- package/dist/modules/references.js +2 -0
- package/dist/modules/references.js.map +1 -1
- package/dist/modules/sample-data/fixture.d.ts +26 -4
- package/dist/modules/sample-data/fixture.d.ts.map +1 -1
- package/dist/modules/sample-data/fixture.js +157 -23
- package/dist/modules/sample-data/fixture.js.map +1 -1
- package/dist/modules/sample-data/index.d.ts +5 -2
- package/dist/modules/sample-data/index.d.ts.map +1 -1
- package/dist/modules/sample-data/index.js +7 -2
- package/dist/modules/sample-data/index.js.map +1 -1
- package/dist/modules/sample-data/routes.d.ts.map +1 -1
- package/dist/modules/sample-data/routes.js +2 -0
- package/dist/modules/sample-data/routes.js.map +1 -1
- package/dist/modules/sample-data/service.d.ts +11 -3
- package/dist/modules/sample-data/service.d.ts.map +1 -1
- package/dist/modules/sample-data/service.js +452 -363
- package/dist/modules/sample-data/service.js.map +1 -1
- package/dist/modules/sample-data/tools.d.ts.map +1 -1
- package/dist/modules/sample-data/tools.js +4 -3
- package/dist/modules/sample-data/tools.js.map +1 -1
- package/dist/modules/search/index.d.ts +1 -1
- package/dist/modules/search/index.d.ts.map +1 -1
- package/dist/modules/search/index.js +2 -1
- package/dist/modules/search/index.js.map +1 -1
- package/dist/modules/search/repository.d.ts +1 -0
- package/dist/modules/search/repository.d.ts.map +1 -1
- package/dist/modules/search/repository.js +22 -0
- package/dist/modules/search/repository.js.map +1 -1
- package/dist/modules/search/service.d.ts +2 -2
- package/dist/modules/search/service.d.ts.map +1 -1
- package/dist/modules/search/service.js +4 -3
- package/dist/modules/search/service.js.map +1 -1
- package/dist/modules/smtp-email/index.d.ts +12 -4
- package/dist/modules/smtp-email/index.d.ts.map +1 -1
- package/dist/modules/smtp-email/index.js +25 -5
- package/dist/modules/smtp-email/index.js.map +1 -1
- package/dist/modules/workspace/repository.d.ts +2 -0
- package/dist/modules/workspace/repository.d.ts.map +1 -1
- package/dist/modules/workspace/repository.js +12 -0
- package/dist/modules/workspace/repository.js.map +1 -1
- package/dist/modules/workspace/routes.d.ts.map +1 -1
- package/dist/modules/workspace/routes.js +13 -0
- package/dist/modules/workspace/routes.js.map +1 -1
- package/dist/modules/workspace/service.d.ts +10 -0
- package/dist/modules/workspace/service.d.ts.map +1 -1
- package/dist/modules/workspace/service.js +34 -10
- package/dist/modules/workspace/service.js.map +1 -1
- package/dist/modules/workspace/starters.d.ts +17 -2
- package/dist/modules/workspace/starters.d.ts.map +1 -1
- package/dist/modules/workspace/starters.js +141 -14
- package/dist/modules/workspace/starters.js.map +1 -1
- package/dist/modules/workspace/workspaceAccessMiddleware.d.ts.map +1 -1
- package/dist/modules/workspace/workspaceAccessMiddleware.js +3 -4
- package/dist/modules/workspace/workspaceAccessMiddleware.js.map +1 -1
- package/dist/runtime/events.d.ts +1 -1
- package/dist/runtime/events.d.ts.map +1 -1
- package/dist/runtime/events.js +2 -0
- package/dist/runtime/events.js.map +1 -1
- package/dist/runtime/module.d.ts +8 -0
- package/dist/runtime/module.d.ts.map +1 -1
- package/dist/runtime/registry.d.ts.map +1 -1
- package/dist/runtime/registry.js +102 -7
- package/dist/runtime/registry.js.map +1 -1
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/testing/app.d.ts +8 -0
- package/dist/testing/app.d.ts.map +1 -1
- package/dist/testing/app.js +6 -1
- package/dist/testing/app.js.map +1 -1
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +9 -1
- package/dist/testing/client.js.map +1 -1
- package/migrations/0044_events.sql +79 -0
- package/migrations/0045_addresses.sql +66 -0
- package/migrations/0046_drop_company_website.sql +66 -0
- package/migrations/meta/_journal.json +21 -0
- package/package.json +3 -3
- package/src/app.ts +25 -1
- package/src/boot.ts +1 -0
- package/src/lib/apiKeyScopes.ts +6 -0
- package/src/lib/columns.ts +12 -3
- package/src/lib/config.ts +21 -0
- package/src/lib/egress.ts +8 -4
- package/src/lib/errors.ts +16 -9
- package/src/lib/ids.ts +11 -0
- package/src/lib/kelpieConfigFile.ts +69 -1
- package/src/modules/agent-tasks/catalog.ts +33 -1
- package/src/modules/agent-tasks/index.ts +1 -0
- package/src/modules/agent-tasks/resolve.ts +11 -0
- package/src/modules/attachedRecords.ts +85 -13
- package/src/modules/auth/index.ts +1 -0
- package/src/modules/auth/repository.ts +34 -3
- package/src/modules/auth/routes.ts +44 -1
- package/src/modules/auth/service.ts +118 -4
- package/src/modules/companies/repository.ts +1 -0
- package/src/modules/companies/routes.ts +6 -11
- package/src/modules/companies/schema.ts +3 -3
- package/src/modules/companies/service.ts +6 -10
- package/src/modules/core.ts +2 -0
- package/src/modules/custom-fields/repository.ts +2 -0
- package/src/modules/dashboard/index.ts +1 -0
- package/src/modules/dashboard/repository.ts +64 -0
- package/src/modules/dashboard/routes.ts +15 -0
- package/src/modules/dashboard/service.ts +48 -0
- package/src/modules/decisions/repository.ts +2 -0
- package/src/modules/events/attendances.ts +245 -0
- package/src/modules/events/catalog.ts +36 -0
- package/src/modules/events/index.ts +54 -0
- package/src/modules/events/repository.ts +396 -0
- package/src/modules/events/routes.ts +405 -0
- package/src/modules/events/schema.ts +148 -0
- package/src/modules/events/service.ts +443 -0
- package/src/modules/events/tools.ts +118 -0
- package/src/modules/forms/applySubmissionFields.ts +7 -5
- package/src/modules/forms/fillAddresses.ts +185 -0
- package/src/modules/forms/repository.ts +3 -3
- package/src/modules/forms/routes.ts +2 -2
- package/src/modules/forms/schema.ts +2 -2
- package/src/modules/forms/service.ts +2 -2
- package/src/modules/forms/submission.ts +34 -4
- package/src/modules/hiring/roles.ts +2 -1
- package/src/modules/import-export/addresses.ts +150 -0
- package/src/modules/import-export/drafts.ts +7 -9
- package/src/modules/import-export/exportRows.ts +3 -3
- package/src/modules/import-export/plan.ts +30 -2
- package/src/modules/import-export/presets.ts +55 -7
- package/src/modules/import-export/repository.ts +5 -7
- package/src/modules/import-export/writes.ts +8 -4
- package/src/modules/people/repository.ts +1 -0
- package/src/modules/people/routes.ts +6 -6
- package/src/modules/people/schema.ts +7 -5
- package/src/modules/people/service.ts +7 -5
- package/src/modules/plans/repository.ts +2 -2
- package/src/modules/plans/routes.ts +6 -6
- package/src/modules/plans/schema.ts +4 -4
- package/src/modules/plans/service.ts +3 -3
- package/src/modules/plans/tools.ts +2 -2
- package/src/modules/recordTargets.ts +14 -0
- package/src/modules/references.ts +2 -0
- package/src/modules/sample-data/fixture.ts +208 -27
- package/src/modules/sample-data/index.ts +7 -2
- package/src/modules/sample-data/routes.ts +2 -0
- package/src/modules/sample-data/service.ts +166 -49
- package/src/modules/sample-data/tools.ts +4 -3
- package/src/modules/search/index.ts +2 -1
- package/src/modules/search/repository.ts +32 -1
- package/src/modules/search/service.ts +6 -4
- package/src/modules/smtp-email/index.ts +32 -10
- package/src/modules/workspace/repository.ts +18 -0
- package/src/modules/workspace/routes.ts +16 -0
- package/src/modules/workspace/service.ts +59 -14
- package/src/modules/workspace/starters.ts +275 -14
- package/src/modules/workspace/workspaceAccessMiddleware.ts +3 -4
- package/src/runtime/events.ts +2 -0
- package/src/runtime/module.ts +8 -0
- package/src/runtime/registry.ts +122 -8
- package/src/schema/index.ts +1 -0
- package/src/testing/app.ts +14 -1
- package/src/testing/client.ts +11 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
-- Domain is the company homepage. `website` was a second URL that almost
|
|
2
|
+
-- always repeated `https://` plus the same host. Fill a blank domain from
|
|
3
|
+
-- that URL, then drop the column. Visit on the heading opens https://{domain}.
|
|
4
|
+
UPDATE "companies" AS c
|
|
5
|
+
SET "domain" = v.host
|
|
6
|
+
FROM (
|
|
7
|
+
SELECT DISTINCT ON (workspace_id, host)
|
|
8
|
+
id,
|
|
9
|
+
workspace_id,
|
|
10
|
+
host
|
|
11
|
+
FROM (
|
|
12
|
+
SELECT
|
|
13
|
+
id,
|
|
14
|
+
workspace_id,
|
|
15
|
+
NULLIF(
|
|
16
|
+
regexp_replace(
|
|
17
|
+
regexp_replace(
|
|
18
|
+
regexp_replace(lower(trim(website)), '^[a-z][a-z0-9+.-]*://', ''),
|
|
19
|
+
'/.*$',
|
|
20
|
+
''
|
|
21
|
+
),
|
|
22
|
+
'\.$',
|
|
23
|
+
''
|
|
24
|
+
),
|
|
25
|
+
''
|
|
26
|
+
) AS host
|
|
27
|
+
FROM "companies"
|
|
28
|
+
WHERE "domain" IS NULL AND "website" IS NOT NULL
|
|
29
|
+
) AS sourced
|
|
30
|
+
WHERE host IS NOT NULL
|
|
31
|
+
ORDER BY workspace_id, host, id
|
|
32
|
+
) AS v
|
|
33
|
+
WHERE c.id = v.id
|
|
34
|
+
AND NOT EXISTS (
|
|
35
|
+
SELECT 1
|
|
36
|
+
FROM "companies" AS other
|
|
37
|
+
WHERE other.workspace_id = c.workspace_id
|
|
38
|
+
AND other.domain = v.host
|
|
39
|
+
AND other.id <> c.id
|
|
40
|
+
);
|
|
41
|
+
--> statement-breakpoint
|
|
42
|
+
ALTER TABLE "companies" DROP COLUMN "website";
|
|
43
|
+
--> statement-breakpoint
|
|
44
|
+
UPDATE "form_fields"
|
|
45
|
+
SET "map_to" = 'company.domain'
|
|
46
|
+
WHERE "map_to" = 'company.website';
|
|
47
|
+
--> statement-breakpoint
|
|
48
|
+
UPDATE "import_jobs"
|
|
49
|
+
SET "column_map" = ("column_map" - 'website') || jsonb_build_object('domain', "column_map"->>'website')
|
|
50
|
+
WHERE jsonb_typeof("column_map") = 'object'
|
|
51
|
+
AND "column_map" ? 'website'
|
|
52
|
+
AND "column_map"->>'website' IS NOT NULL
|
|
53
|
+
AND NOT (
|
|
54
|
+
"column_map" ? 'domain'
|
|
55
|
+
AND "column_map"->>'domain' IS NOT NULL
|
|
56
|
+
);
|
|
57
|
+
--> statement-breakpoint
|
|
58
|
+
UPDATE "import_jobs"
|
|
59
|
+
SET "column_map" = "column_map" - 'website'
|
|
60
|
+
WHERE jsonb_typeof("column_map") = 'object'
|
|
61
|
+
AND "column_map" ? 'website';
|
|
62
|
+
--> statement-breakpoint
|
|
63
|
+
UPDATE "import_jobs"
|
|
64
|
+
SET "match_key" = 'domain'
|
|
65
|
+
WHERE "object" = 'companies'
|
|
66
|
+
AND "match_key" = 'website';
|
|
@@ -309,6 +309,27 @@
|
|
|
309
309
|
"when": 1788700000000,
|
|
310
310
|
"tag": "0043_external_sign_in",
|
|
311
311
|
"breakpoints": true
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"idx": 44,
|
|
315
|
+
"version": "7",
|
|
316
|
+
"when": 1788800000000,
|
|
317
|
+
"tag": "0044_events",
|
|
318
|
+
"breakpoints": true
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"idx": 45,
|
|
322
|
+
"version": "7",
|
|
323
|
+
"when": 1788900000000,
|
|
324
|
+
"tag": "0045_addresses",
|
|
325
|
+
"breakpoints": true
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"idx": 46,
|
|
329
|
+
"version": "7",
|
|
330
|
+
"when": 1789000000000,
|
|
331
|
+
"tag": "0046_drop_company_website",
|
|
332
|
+
"breakpoints": true
|
|
312
333
|
}
|
|
313
334
|
]
|
|
314
335
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kelpie/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "The Kelpie service as a library: module runtime, core CRM modules, REST API, MCP surface, and the shared migration pipeline.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kelpie",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@hono/node-server": "^2.0.12",
|
|
55
|
-
"@kelpie/schemas": "^0.
|
|
55
|
+
"@kelpie/schemas": "^0.13.0",
|
|
56
56
|
"@node-rs/argon2": "^2.0.2",
|
|
57
57
|
"drizzle-orm": "^0.45.2",
|
|
58
58
|
"emittery": "^2.0.0",
|
|
59
|
-
"hono": "^4.
|
|
59
|
+
"hono": "^4.13.7",
|
|
60
60
|
"mailgen": "^2.0.36",
|
|
61
61
|
"nodemailer": "^9.0.5",
|
|
62
62
|
"postgres": "^3.4.9",
|
package/src/app.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Hono } from 'hono'
|
|
|
2
2
|
import type { Context } from 'hono'
|
|
3
3
|
import { cors } from 'hono/cors'
|
|
4
4
|
|
|
5
|
+
import type { PublicRegion } from '@kelpie/schemas'
|
|
6
|
+
|
|
5
7
|
import type { RuntimeMode } from './lib/config.ts'
|
|
6
8
|
import type { DatabaseProbe } from './lib/database.ts'
|
|
7
9
|
import { AppError, internalErrorBody, toErrorBody } from './lib/errors.ts'
|
|
@@ -65,6 +67,18 @@ export interface AppDependencies {
|
|
|
65
67
|
* name itself".
|
|
66
68
|
*/
|
|
67
69
|
readonly siteName?: string | undefined
|
|
70
|
+
/**
|
|
71
|
+
* Reported through `GET /v1/public/config` so the sign-up page can show a
|
|
72
|
+
* "signups are closed" message instead of the form. Defaults to `true`
|
|
73
|
+
* (open), matching the config layer's own default.
|
|
74
|
+
*/
|
|
75
|
+
readonly signupsEnabled?: boolean
|
|
76
|
+
/**
|
|
77
|
+
* Reported through `GET /v1/public/config` so signed-out pages can draw a
|
|
78
|
+
* region switcher. Defaults to `[]` (no switcher), matching a self-hosted
|
|
79
|
+
* install that never declared regions.
|
|
80
|
+
*/
|
|
81
|
+
readonly regions?: readonly PublicRegion[]
|
|
68
82
|
}
|
|
69
83
|
|
|
70
84
|
/** Per-request values the middleware chain sets and handlers read. */
|
|
@@ -185,9 +199,19 @@ export function createApp(dependencies: AppDependencies): Hono<AppBindings> {
|
|
|
185
199
|
// exists to be visible; nothing here is sensitive.
|
|
186
200
|
const runtimeMode: RuntimeMode = dependencies.runtimeMode ?? 'production'
|
|
187
201
|
const siteName = dependencies.siteName ?? null
|
|
202
|
+
const signupsEnabled = dependencies.signupsEnabled ?? true
|
|
203
|
+
const regions = dependencies.regions ?? []
|
|
188
204
|
|
|
189
205
|
app.get(`${PUBLIC_ROUTE_PREFIX}/config`, (context) =>
|
|
190
|
-
context.json(
|
|
206
|
+
context.json(
|
|
207
|
+
{
|
|
208
|
+
runtime_mode: runtimeMode,
|
|
209
|
+
site_name: siteName,
|
|
210
|
+
signups_enabled: signupsEnabled,
|
|
211
|
+
regions,
|
|
212
|
+
},
|
|
213
|
+
200,
|
|
214
|
+
),
|
|
191
215
|
)
|
|
192
216
|
|
|
193
217
|
for (const { router } of dependencies.contributions.routers) {
|
package/src/boot.ts
CHANGED
|
@@ -77,6 +77,7 @@ export async function bootAssembly(
|
|
|
77
77
|
now: () => new Date(),
|
|
78
78
|
appBaseUrl: config.appBaseUrl,
|
|
79
79
|
secretEncryption: config.secretEncryption,
|
|
80
|
+
signupsEnabled: config.signups === 'open',
|
|
80
81
|
},
|
|
81
82
|
// `provider` picks a named sender from the runtime's registry. `'log'` is
|
|
82
83
|
// built in; `'smtp'` is registered by the built-in `smtp-email` core
|
package/src/lib/apiKeyScopes.ts
CHANGED
|
@@ -58,6 +58,10 @@ const ROUTE_SCOPE_RULES: readonly RouteScopeRule[] = [
|
|
|
58
58
|
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/roles(?:\/[^/]+)?$/u, scope: 'roles:write' },
|
|
59
59
|
{ methods: ['GET'], pattern: /^\/v1\/candidates(?:\/[^/]+)?$/u, scope: 'candidates:read' },
|
|
60
60
|
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/candidates(?:\/[^/]+)?$/u, scope: 'candidates:write' },
|
|
61
|
+
{ methods: ['GET'], pattern: /^\/v1\/(?:events(?:\/[^/]+(?:\/attendances)?)?|event-associations)$/u, scope: 'events:read' },
|
|
62
|
+
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/(?:events(?:\/[^/]+(?:\/attendances)?)?|event-associations)$/u, scope: 'events:write' },
|
|
63
|
+
{ methods: ['GET'], pattern: /^\/v1\/attendances(?:\/[^/]+)?$/u, scope: 'attendances:read' },
|
|
64
|
+
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/attendances(?:\/[^/]+)?$/u, scope: 'attendances:write' },
|
|
61
65
|
{ methods: ['GET'], pattern: /^\/v1\/decisions(?:\/[^/]+)?$/u, scope: 'decisions:read' },
|
|
62
66
|
{ methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/decisions(?:\/[^/]+)?$/u, scope: 'decisions:write' },
|
|
63
67
|
{ methods: ['GET'], pattern: /^\/v1\/plan_items(?:\/[^/]+)?$/u, scope: 'plan_items:read' },
|
|
@@ -100,6 +104,7 @@ const ROUTE_SCOPE_RULES: readonly RouteScopeRule[] = [
|
|
|
100
104
|
{ methods: ['GET'], pattern: /^\/v1\/workspaces\/[^/]+\/modules(?:\/[^/]+)?$/u, scope: 'modules:read' },
|
|
101
105
|
{ methods: ['PATCH'], pattern: /^\/v1\/workspaces\/[^/]+\/modules(?:\/[^/]+)?$/u, scope: 'modules:write' },
|
|
102
106
|
{ methods: ['POST'], pattern: /^\/v1\/workspaces\/[^/]+\/sample-data$/u, scope: 'sample_data:write' },
|
|
107
|
+
{ methods: ['POST'], pattern: /^\/v1\/workspaces\/[^/]+\/handbook\/seed$/u, scope: 'handbook:write' },
|
|
103
108
|
{ methods: ['POST'], pattern: /^\/v1\/workspaces\/[^/]+\/relink-email-domains$/u, scope: 'workspace:write' },
|
|
104
109
|
{ methods: ['GET'], pattern: /^\/v1\/mcp\/tools$/u, scope: 'search:read' },
|
|
105
110
|
]
|
|
@@ -158,6 +163,7 @@ const MCP_RESOURCE_ALIASES: Readonly<Record<string, string>> = {
|
|
|
158
163
|
consent_purposes: 'consent_purposes',
|
|
159
164
|
form_submissions: 'forms',
|
|
160
165
|
list_memberships: 'lists',
|
|
166
|
+
event_associations: 'events',
|
|
161
167
|
agent_tasks: 'agent_tasks',
|
|
162
168
|
agent_runs: 'agent_runs',
|
|
163
169
|
import_jobs: 'import_export',
|
package/src/lib/columns.ts
CHANGED
|
@@ -60,6 +60,12 @@ export interface SearchVectorPart {
|
|
|
60
60
|
* a generated column built on it.
|
|
61
61
|
*/
|
|
62
62
|
readonly array?: boolean
|
|
63
|
+
/**
|
|
64
|
+
* True for an `addresses` jsonb column. Flattened by
|
|
65
|
+
* `kelpie_addresses_search_text`, a wrapper that migration `0045` adds so the
|
|
66
|
+
* generated vector reads city and postal code without the kind keys.
|
|
67
|
+
*/
|
|
68
|
+
readonly addresses?: boolean
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
/**
|
|
@@ -78,9 +84,12 @@ export interface SearchVectorPart {
|
|
|
78
84
|
const SPLIT_ON_PUNCTUATION = sql.raw("'[^[:alnum:]]+', ' ', 'g'")
|
|
79
85
|
|
|
80
86
|
function searchVectorPart(part: SearchVectorPart): SQL {
|
|
81
|
-
const raw =
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
const raw =
|
|
88
|
+
part.addresses === true
|
|
89
|
+
? sql`coalesce(kelpie_addresses_search_text(${part.column}), '')`
|
|
90
|
+
: part.array === true
|
|
91
|
+
? sql`coalesce(kelpie_text_array_to_string(${part.column}), '')`
|
|
92
|
+
: sql`coalesce(${part.column}::text, '')`
|
|
84
93
|
const source = sql`regexp_replace(${raw}, ${SPLIT_ON_PUNCTUATION})`
|
|
85
94
|
|
|
86
95
|
return sql`setweight(to_tsvector(${sql.raw(`'${SEARCH_CONFIGURATION}'`)}, ${source}), ${sql.raw(`'${part.weight}'`)})`
|
package/src/lib/config.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PublicRegion } from '@kelpie/schemas'
|
|
1
2
|
import { z } from 'zod'
|
|
2
3
|
|
|
3
4
|
import { emailConfigSchema } from './email.ts'
|
|
@@ -24,6 +25,9 @@ export type RuntimeMode = 'development' | 'test' | 'production'
|
|
|
24
25
|
|
|
25
26
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error'
|
|
26
27
|
|
|
28
|
+
/** Whether `POST /v1/auth/signup` and external-sign-in provisioning accept a brand-new account. */
|
|
29
|
+
export type SignupsMode = 'open' | 'closed'
|
|
30
|
+
|
|
27
31
|
/**
|
|
28
32
|
* Everything the logger needs to boot: the minimum severity to emit, and the
|
|
29
33
|
* ordered list of destinations each line is written to. Stdout is one such
|
|
@@ -96,6 +100,20 @@ export interface KelpieConfig {
|
|
|
96
100
|
* it, so both webhooks and agent-tasks receive it through `services`.
|
|
97
101
|
*/
|
|
98
102
|
readonly secretEncryption: SecretEncryptionConfig | undefined
|
|
103
|
+
/**
|
|
104
|
+
* Whether the instance accepts a brand-new account, through password signup
|
|
105
|
+
* or external-sign-in provisioning. Defaults to `'open'`, so a self-hoster
|
|
106
|
+
* who never sets `SIGNUPS` sees no change in behaviour. Joining an existing
|
|
107
|
+
* workspace via invite, and workspace creation by an already-verified
|
|
108
|
+
* account, are unaffected either way.
|
|
109
|
+
*/
|
|
110
|
+
readonly signups: SignupsMode
|
|
111
|
+
/**
|
|
112
|
+
* Origins a multi-region assembly advertises on signed-out pages. Empty
|
|
113
|
+
* (the default) hides the switcher. Two or more draws it. Exposed through
|
|
114
|
+
* `GET /v1/public/config`.
|
|
115
|
+
*/
|
|
116
|
+
readonly regions: readonly PublicRegion[]
|
|
99
117
|
}
|
|
100
118
|
|
|
101
119
|
/** Thrown at boot when the environment cannot produce a valid configuration. */
|
|
@@ -129,6 +147,7 @@ const environmentSchema = z.object({
|
|
|
129
147
|
KELPIE_SITE_NAME: z.string().min(1).optional(),
|
|
130
148
|
WEB_BUNDLE_DIR: z.string().min(1).optional(),
|
|
131
149
|
TRUSTED_PROXY_HOP_COUNT: z.coerce.number().int().nonnegative().default(0),
|
|
150
|
+
SIGNUPS: z.enum(['open', 'closed']).default('open'),
|
|
132
151
|
...rateLimitConfigSchema.shape,
|
|
133
152
|
})
|
|
134
153
|
|
|
@@ -171,5 +190,7 @@ export function loadConfig(environment: Environment): KelpieConfig {
|
|
|
171
190
|
env: environment,
|
|
172
191
|
appBaseUrl: undefined,
|
|
173
192
|
secretEncryption: undefined,
|
|
193
|
+
signups: environmentResult.data.SIGNUPS,
|
|
194
|
+
regions: [],
|
|
174
195
|
}
|
|
175
196
|
}
|
package/src/lib/egress.ts
CHANGED
|
@@ -74,17 +74,21 @@ function isPrivateIpv4(ip: string): boolean {
|
|
|
74
74
|
|
|
75
75
|
/** True for an IPv6 address in a loopback, unspecified, link-local, or unique-local range. */
|
|
76
76
|
function isPrivateIpv6(ip: string): boolean {
|
|
77
|
-
|
|
77
|
+
// Normalize both DNS results and literals. URL parsing compresses IPv6 and
|
|
78
|
+
// rewrites a dotted IPv4 suffix as two hexadecimal groups.
|
|
79
|
+
const lower = new URL(`http://[${ip}]/`).hostname.slice(1, -1)
|
|
78
80
|
|
|
79
81
|
if (lower === '::1' || lower === '::') {
|
|
80
82
|
return true
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
const mapped = /^::ffff:(\
|
|
85
|
+
const mapped = /^::ffff:([\da-f]{1,4}):([\da-f]{1,4})$/u.exec(lower)
|
|
84
86
|
|
|
85
|
-
if (mapped?.[1] !== undefined) {
|
|
87
|
+
if (mapped?.[1] !== undefined && mapped[2] !== undefined) {
|
|
86
88
|
// An IPv4 address written in IPv6 form resolves by its IPv4 rules.
|
|
87
|
-
|
|
89
|
+
const high = Number.parseInt(mapped[1], 16)
|
|
90
|
+
const low = Number.parseInt(mapped[2], 16)
|
|
91
|
+
return isPrivateIpv4([high >>> 8, high & 255, low >>> 8, low & 255].join('.'))
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
const firstHextet = Number.parseInt(lower.split(':')[0] || '0', 16)
|
package/src/lib/errors.ts
CHANGED
|
@@ -89,25 +89,32 @@ export class AppError extends Error {
|
|
|
89
89
|
* A reason rather than a message, so a provider module can branch on it (to
|
|
90
90
|
* redirect somewhere useful, say) without matching on English text.
|
|
91
91
|
*/
|
|
92
|
-
export type ExternalSignInRefusal = 'email_unverified' | 'unknown_identity'
|
|
92
|
+
export type ExternalSignInRefusal = 'email_unverified' | 'unknown_identity' | 'signups_closed'
|
|
93
|
+
|
|
94
|
+
const EXTERNAL_SIGN_IN_CODE: Record<ExternalSignInRefusal, ErrorCode> = {
|
|
95
|
+
email_unverified: 'unauthorized',
|
|
96
|
+
unknown_identity: 'forbidden',
|
|
97
|
+
signups_closed: 'forbidden',
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const EXTERNAL_SIGN_IN_MESSAGE: Record<ExternalSignInRefusal, string> = {
|
|
101
|
+
email_unverified: 'The identity provider did not verify this email address',
|
|
102
|
+
unknown_identity: 'No account exists for this identity',
|
|
103
|
+
signups_closed: 'Signups are closed on this instance',
|
|
104
|
+
}
|
|
93
105
|
|
|
94
106
|
/**
|
|
95
107
|
* An external sign-in core would not complete.
|
|
96
108
|
*
|
|
97
109
|
* An `AppError`, so an uncaught one still renders as the `api.md` body. No new
|
|
98
|
-
* `ErrorCode`: refusing an unverified identity is an ordinary `401`, and
|
|
99
|
-
* refusing to provision
|
|
110
|
+
* `ErrorCode`: refusing an unverified identity is an ordinary `401`, and both
|
|
111
|
+
* refusing to provision and signups being closed are an ordinary `403`.
|
|
100
112
|
*/
|
|
101
113
|
export class ExternalSignInError extends AppError {
|
|
102
114
|
readonly reason: ExternalSignInRefusal
|
|
103
115
|
|
|
104
116
|
constructor(reason: ExternalSignInRefusal) {
|
|
105
|
-
super(
|
|
106
|
-
reason === 'email_unverified' ? 'unauthorized' : 'forbidden',
|
|
107
|
-
reason === 'email_unverified'
|
|
108
|
-
? 'The identity provider did not verify this email address'
|
|
109
|
-
: 'No account exists for this identity',
|
|
110
|
-
)
|
|
117
|
+
super(EXTERNAL_SIGN_IN_CODE[reason], EXTERNAL_SIGN_IN_MESSAGE[reason])
|
|
111
118
|
this.name = 'ExternalSignInError'
|
|
112
119
|
this.reason = reason
|
|
113
120
|
}
|
package/src/lib/ids.ts
CHANGED
|
@@ -71,6 +71,17 @@ export const idPrefixes = {
|
|
|
71
71
|
* key. Not persisted by core.
|
|
72
72
|
*/
|
|
73
73
|
event: 'ev',
|
|
74
|
+
/**
|
|
75
|
+
* A dated CRM gathering. The public prefix is `event_`; the factory key is
|
|
76
|
+
* `crmEvent` because `event` already stamps domain-event envelopes as `ev_`.
|
|
77
|
+
*/
|
|
78
|
+
crmEvent: 'event',
|
|
79
|
+
attendance: 'atnd',
|
|
80
|
+
/**
|
|
81
|
+
* Never returns over the wire: associations nest on the Event and on
|
|
82
|
+
* `GET /v1/event-associations`. Same reason as `personLink`.
|
|
83
|
+
*/
|
|
84
|
+
eventAssociation: 'elink',
|
|
74
85
|
} as const
|
|
75
86
|
|
|
76
87
|
export type ObjectKind = keyof typeof idPrefixes
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import type { PublicRegion } from '@kelpie/schemas'
|
|
2
|
+
import { publicRegionSchema } from '@kelpie/schemas'
|
|
3
|
+
|
|
1
4
|
import type { KelpieModule } from '../runtime/module.ts'
|
|
2
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ConfigurationError,
|
|
7
|
+
type Environment,
|
|
8
|
+
type KelpieConfig,
|
|
9
|
+
type LogLevel,
|
|
10
|
+
type RuntimeMode,
|
|
11
|
+
type SignupsMode,
|
|
12
|
+
} from './config.ts'
|
|
3
13
|
import type { EmailConfig } from './email.ts'
|
|
14
|
+
import { describeValidationIssue } from './errors.ts'
|
|
4
15
|
import { type ConfigValue, resolveMarkers } from './fromEnv.ts'
|
|
5
16
|
import type { LoggingDestination } from './logger.ts'
|
|
6
17
|
import type { RateLimitConfig } from './rateLimit.ts'
|
|
@@ -35,6 +46,19 @@ export interface KelpieConfigInput {
|
|
|
35
46
|
readonly siteName?: ConfigValue<string | undefined>
|
|
36
47
|
/** Optional; defaults to 0 (no proxy in front). */
|
|
37
48
|
readonly trustedProxyHopCount?: ConfigValue<number>
|
|
49
|
+
/**
|
|
50
|
+
* Whether the instance accepts a brand-new account. Optional; defaults to
|
|
51
|
+
* `'open'`, matching `loadConfig`'s default, so an assembly that never sets
|
|
52
|
+
* this sees no change in behaviour.
|
|
53
|
+
*/
|
|
54
|
+
readonly signups?: ConfigValue<SignupsMode>
|
|
55
|
+
/**
|
|
56
|
+
* Origins a multi-region assembly advertises on signed-out pages. Optional;
|
|
57
|
+
* omitted or empty hides the switcher, matching a self-hosted install.
|
|
58
|
+
* Two or more draws it. Each `id` and `origin` must be unique, and each
|
|
59
|
+
* `origin` must be an absolute `http:` or `https:` origin.
|
|
60
|
+
*/
|
|
61
|
+
readonly regions?: ConfigValue<readonly PublicRegion[]>
|
|
38
62
|
/**
|
|
39
63
|
* The deployment's base URL. Every emailed link (password reset, email
|
|
40
64
|
* verification, invite) is built from it. Required by the workspace and auth
|
|
@@ -164,6 +188,9 @@ export function resolveKelpieConfig(input: KelpieConfigInput, environment: Envir
|
|
|
164
188
|
const rateLimit = buildRateLimitConfig(resolved.rateLimit)
|
|
165
189
|
const env = mergeEnv(environment, resolved.env)
|
|
166
190
|
const secretEncryption = buildSecretEncryptionConfig(resolved.secretEncryption)
|
|
191
|
+
const regions = resolved.regions ?? []
|
|
192
|
+
|
|
193
|
+
problems.push(...describeRegionProblems(regions))
|
|
167
194
|
|
|
168
195
|
if (problems.length > 0) {
|
|
169
196
|
throw new ConfigurationError(problems)
|
|
@@ -186,6 +213,8 @@ export function resolveKelpieConfig(input: KelpieConfigInput, environment: Envir
|
|
|
186
213
|
env,
|
|
187
214
|
appBaseUrl: resolved.appBaseUrl,
|
|
188
215
|
secretEncryption,
|
|
216
|
+
signups: resolved.signups ?? 'open',
|
|
217
|
+
regions,
|
|
189
218
|
}
|
|
190
219
|
}
|
|
191
220
|
|
|
@@ -217,6 +246,8 @@ interface ResolvedInput {
|
|
|
217
246
|
readonly moduleConfigPath?: string | undefined
|
|
218
247
|
readonly siteName?: string | undefined
|
|
219
248
|
readonly trustedProxyHopCount?: number
|
|
249
|
+
readonly signups?: SignupsMode
|
|
250
|
+
readonly regions?: readonly PublicRegion[]
|
|
220
251
|
readonly appBaseUrl?: string
|
|
221
252
|
readonly secretEncryption?: ResolvedSecretEncryption
|
|
222
253
|
readonly email: ResolvedEmail
|
|
@@ -242,6 +273,43 @@ interface ResolvedRateLimit {
|
|
|
242
273
|
readonly api?: { readonly limit?: number; readonly windowSeconds?: number }
|
|
243
274
|
}
|
|
244
275
|
|
|
276
|
+
function describeRegionProblems(regions: readonly unknown[]): string[] {
|
|
277
|
+
const problems: string[] = []
|
|
278
|
+
const ids = new Set<string>()
|
|
279
|
+
const origins = new Set<string>()
|
|
280
|
+
|
|
281
|
+
for (const [index, region] of regions.entries()) {
|
|
282
|
+
const parsed = publicRegionSchema.safeParse(region)
|
|
283
|
+
|
|
284
|
+
if (!parsed.success) {
|
|
285
|
+
for (const issue of parsed.error.issues) {
|
|
286
|
+
problems.push(
|
|
287
|
+
describeValidationIssue({
|
|
288
|
+
path: ['regions', index, ...issue.path],
|
|
289
|
+
message: issue.message,
|
|
290
|
+
}),
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
continue
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (ids.has(parsed.data.id)) {
|
|
298
|
+
problems.push(`regions.${String(index)}.id: duplicate id '${parsed.data.id}'`)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
ids.add(parsed.data.id)
|
|
302
|
+
|
|
303
|
+
if (origins.has(parsed.data.origin)) {
|
|
304
|
+
problems.push(`regions.${String(index)}.origin: duplicate origin '${parsed.data.origin}'`)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
origins.add(parsed.data.origin)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return problems
|
|
311
|
+
}
|
|
312
|
+
|
|
245
313
|
function buildEmailConfig(resolved: ResolvedEmail | undefined): EmailConfig {
|
|
246
314
|
// Missing `email.provider` or `email.from` is already recorded by the
|
|
247
315
|
// walker; return a placeholder so `resolveKelpieConfig` reaches its throw
|
|
@@ -128,7 +128,7 @@ Append a Note, update summary only when clearly supported, propose Plan items on
|
|
|
128
128
|
targetTypes: ['company'],
|
|
129
129
|
placement: 'primary',
|
|
130
130
|
handbookSlugs: ['ideal-customer-profile', 'agent-faq'],
|
|
131
|
-
instructions: `Research this Company. Update description, stage, sizeBand, techStack,
|
|
131
|
+
instructions: `Research this Company. Update description, stage, sizeBand, techStack, domain when evidenced, tags, and summary.
|
|
132
132
|
Append a Note listing sources. Prefer notes over guesses for uncertain facts.`,
|
|
133
133
|
}),
|
|
134
134
|
task({
|
|
@@ -623,6 +623,28 @@ Ask before setting stage to passed.`,
|
|
|
623
623
|
instructions: `The human will paste notes after this prompt. Append a Note; propose Plan items; extract Decisions.`,
|
|
624
624
|
}),
|
|
625
625
|
|
|
626
|
+
// Event
|
|
627
|
+
task({
|
|
628
|
+
id: 'event.brief',
|
|
629
|
+
label: 'Event brief',
|
|
630
|
+
description: 'Agenda, who is registered, linked records, open Plans.',
|
|
631
|
+
targetTypes: ['event'],
|
|
632
|
+
placement: 'primary',
|
|
633
|
+
handbookSlugs: ['agent-faq'],
|
|
634
|
+
instructions: `Prepare a brief for this Event: agenda from details, who is registered, linked records, and open Plan items.
|
|
635
|
+
Do not invent attendees. Prefer pinned notes and Decisions.`,
|
|
636
|
+
}),
|
|
637
|
+
task({
|
|
638
|
+
id: 'event.follow_up',
|
|
639
|
+
label: 'Follow up',
|
|
640
|
+
description: 'Draft follow-up from attended vs no-show Attendances.',
|
|
641
|
+
targetTypes: ['event'],
|
|
642
|
+
placement: 'primary',
|
|
643
|
+
handbookSlugs: ['voice-and-tone', 'agent-faq'],
|
|
644
|
+
instructions: `Draft follow-up from this Event's Attendances. Separate attended from no-show.
|
|
645
|
+
Save drafts as Notes. Do not send email.`,
|
|
646
|
+
}),
|
|
647
|
+
|
|
626
648
|
// Candidate
|
|
627
649
|
task({
|
|
628
650
|
id: 'candidate.score',
|
|
@@ -753,6 +775,16 @@ Suggest Draft outreach or Plan items. Do not spam everyone.`,
|
|
|
753
775
|
handbookSlugs: ['how-we-sell', 'agent-faq'],
|
|
754
776
|
instructions: `Propose a week of Plan items across open Deals, Opportunities, Raises, and Partnerships.
|
|
755
777
|
Create Plan items only when clearly useful; otherwise list proposals for confirmation.`,
|
|
778
|
+
}),
|
|
779
|
+
task({
|
|
780
|
+
id: 'workspace.upcoming_events',
|
|
781
|
+
label: 'Upcoming events',
|
|
782
|
+
description: 'Scheduled Events in the next week and their attendee counts.',
|
|
783
|
+
targetTypes: ['workspace'],
|
|
784
|
+
placement: 'primary',
|
|
785
|
+
handbookSlugs: ['agent-faq'],
|
|
786
|
+
instructions: `List scheduled Events in the next week with attendee counts.
|
|
787
|
+
Use GET /v1/dashboard upcoming_events. Suggest follow-up or brief tasks where useful.`,
|
|
756
788
|
}),
|
|
757
789
|
task({
|
|
758
790
|
id: 'workspace.empty_field_sweep',
|
|
@@ -9,6 +9,7 @@ import { companies } from '../companies/schema.ts'
|
|
|
9
9
|
import { deals } from '../deals/schema.ts'
|
|
10
10
|
import { decisions } from '../decisions/schema.ts'
|
|
11
11
|
import { enquiries } from '../enquiries/schema.ts'
|
|
12
|
+
import { attendances } from '../events/schema.ts'
|
|
12
13
|
import { handbookPages } from '../handbook/schema.ts'
|
|
13
14
|
import { candidates, roles } from '../hiring/schema.ts'
|
|
14
15
|
import { notes } from '../notes/schema.ts'
|
|
@@ -443,6 +444,16 @@ async function collectRelated(
|
|
|
443
444
|
),
|
|
444
445
|
}
|
|
445
446
|
|
|
447
|
+
case 'event':
|
|
448
|
+
return {
|
|
449
|
+
person_ids: await relatedFrom(
|
|
450
|
+
db,
|
|
451
|
+
attendances,
|
|
452
|
+
attendances.personId,
|
|
453
|
+
and(eq(attendances.workspaceId, workspaceId), eq(attendances.eventId, targetId)) as SQL,
|
|
454
|
+
),
|
|
455
|
+
}
|
|
456
|
+
|
|
446
457
|
case 'handbook':
|
|
447
458
|
case 'workspace':
|
|
448
459
|
return {}
|