dayhand 0.0.0-stage → 0.14.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/LICENSE +202 -0
- package/README.md +100 -2
- package/backend/convex/_generated/api.d.ts +181 -0
- package/backend/convex/_generated/api.js +23 -0
- package/backend/convex/_generated/dataModel.d.ts +60 -0
- package/backend/convex/_generated/server.d.ts +162 -0
- package/backend/convex/_generated/server.js +101 -0
- package/backend/convex/apiKeys.ts +171 -0
- package/backend/convex/app.ts +197 -0
- package/backend/convex/auth.config.ts +28 -0
- package/backend/convex/calendar.ts +61 -0
- package/backend/convex/calendarFeeds.ts +105 -0
- package/backend/convex/callLimits.ts +67 -0
- package/backend/convex/connections.ts +28 -0
- package/backend/convex/crons.ts +18 -0
- package/backend/convex/devices.ts +224 -0
- package/backend/convex/http.ts +56 -0
- package/backend/convex/lib/activity.ts +155 -0
- package/backend/convex/lib/agentControls.ts +146 -0
- package/backend/convex/lib/agentWork.ts +250 -0
- package/backend/convex/lib/appToken.ts +68 -0
- package/backend/convex/lib/approvals.ts +311 -0
- package/backend/convex/lib/args.ts +175 -0
- package/backend/convex/lib/backlogMd.ts +309 -0
- package/backend/convex/lib/backlogOps.ts +361 -0
- package/backend/convex/lib/brief.ts +132 -0
- package/backend/convex/lib/cimd.ts +57 -0
- package/backend/convex/lib/core.ts +380 -0
- package/backend/convex/lib/dates.ts +114 -0
- package/backend/convex/lib/env.ts +19 -0
- package/backend/convex/lib/exportPlan.ts +98 -0
- package/backend/convex/lib/findTasks.ts +119 -0
- package/backend/convex/lib/frontmatter.ts +165 -0
- package/backend/convex/lib/guide.ts +286 -0
- package/backend/convex/lib/health.ts +115 -0
- package/backend/convex/lib/http.ts +27 -0
- package/backend/convex/lib/ical.ts +243 -0
- package/backend/convex/lib/importFormats.ts +429 -0
- package/backend/convex/lib/importOps.ts +145 -0
- package/backend/convex/lib/jsonSchema.ts +50 -0
- package/backend/convex/lib/limits.ts +42 -0
- package/backend/convex/lib/mcpContent.ts +220 -0
- package/backend/convex/lib/mcpProtocol.ts +128 -0
- package/backend/convex/lib/mcpTools.ts +505 -0
- package/backend/convex/lib/oauthConfig.ts +59 -0
- package/backend/convex/lib/oauthPage.ts +125 -0
- package/backend/convex/lib/oauthRules.ts +159 -0
- package/backend/convex/lib/openapi.ts +230 -0
- package/backend/convex/lib/outputSchemas.ts +2901 -0
- package/backend/convex/lib/problem.ts +69 -0
- package/backend/convex/lib/proposals.ts +172 -0
- package/backend/convex/lib/repeat.ts +185 -0
- package/backend/convex/lib/restRoutes.ts +294 -0
- package/backend/convex/lib/rrule.ts +171 -0
- package/backend/convex/lib/secrets.ts +135 -0
- package/backend/convex/lib/settingsOps.ts +47 -0
- package/backend/convex/lib/taskOps.ts +304 -0
- package/backend/convex/lib/toolResults.ts +50 -0
- package/backend/convex/lib/validators.ts +143 -0
- package/backend/convex/lib/views.ts +145 -0
- package/backend/convex/lib/vtimezone.ts +151 -0
- package/backend/convex/lib/webhookEvents.ts +160 -0
- package/backend/convex/lib/webhookSign.ts +30 -0
- package/backend/convex/lib/webhookTarget.ts +75 -0
- package/backend/convex/mcp.ts +405 -0
- package/backend/convex/migrations.ts +91 -0
- package/backend/convex/oauth.ts +388 -0
- package/backend/convex/oauthHttp.ts +381 -0
- package/backend/convex/oauthOwner.ts +173 -0
- package/backend/convex/ops.ts +205 -0
- package/backend/convex/pairing.ts +178 -0
- package/backend/convex/rest.ts +254 -0
- package/backend/convex/restAccount.ts +56 -0
- package/backend/convex/restImport.ts +73 -0
- package/backend/convex/schema.ts +435 -0
- package/backend/convex/tsconfig.json +18 -0
- package/backend/convex/webhooks.ts +339 -0
- package/backend/package-lock.json +3068 -0
- package/backend/package.json +33 -0
- package/backend/scripts/app-token.mjs +79 -0
- package/backend/scripts/fetch-local-backend.mjs +72 -0
- package/backend/scripts/gen-output-schemas.mjs +60 -0
- package/backend/scripts/jwt.mjs +67 -0
- package/backend/scripts/local-backend-lib.mjs +123 -0
- package/backend/scripts/local-backend.mjs +81 -0
- package/backend/scripts/setup-convex.mjs +158 -0
- package/backend/scripts/setup-lib.mjs +338 -0
- package/backend/scripts/setup-output.mjs +108 -0
- package/backend/scripts/setup.mjs +344 -0
- package/backend/scripts/signing-key.mjs +61 -0
- package/backend/tsconfig.json +16 -0
- package/dist/backlog/folder.js +114 -0
- package/dist/bin/dayhand.js +10 -0
- package/dist/bin/hlm.js +12 -0
- package/dist/completion/cache.js +54 -0
- package/dist/completion/engine.js +187 -0
- package/dist/completion/scripts.js +82 -0
- package/dist/config/aliases.js +25 -0
- package/dist/config/env.js +68 -0
- package/dist/config/file.js +146 -0
- package/dist/config/paths.js +58 -0
- package/dist/config/resolve.js +53 -0
- package/dist/config/url.js +26 -0
- package/dist/core/editor.js +37 -0
- package/dist/core/exit.js +59 -0
- package/dist/core/io.js +14 -0
- package/dist/core/output.js +39 -0
- package/dist/core/prompt.js +55 -0
- package/dist/core/version.js +15 -0
- package/dist/core/words.js +36 -0
- package/dist/credentials/file.js +65 -0
- package/dist/credentials/index.js +29 -0
- package/dist/credentials/keychain.js +44 -0
- package/dist/credentials/store.js +39 -0
- package/dist/docs.js +116 -0
- package/dist/fields.js +33 -0
- package/dist/globals.js +16 -0
- package/dist/guide.js +36 -0
- package/dist/help.js +56 -0
- package/dist/i18n/dates.js +84 -0
- package/dist/i18n/de.js +240 -0
- package/dist/i18n/en.js +243 -0
- package/dist/i18n/es.js +240 -0
- package/dist/i18n/fr.js +240 -0
- package/dist/i18n/index.js +67 -0
- package/dist/i18n/ja.js +240 -0
- package/dist/i18n/pl.js +245 -0
- package/dist/i18n/pt-BR.js +240 -0
- package/dist/i18n/zh-Hans.js +240 -0
- package/dist/main.js +180 -0
- package/dist/man.js +56 -0
- package/dist/registry/agents.js +67 -0
- package/dist/registry/auth.js +168 -0
- package/dist/registry/backlog.js +106 -0
- package/dist/registry/calendar.js +103 -0
- package/dist/registry/dates.js +33 -0
- package/dist/registry/filters.js +98 -0
- package/dist/registry/index.js +24 -0
- package/dist/registry/integrations.js +245 -0
- package/dist/registry/local.js +89 -0
- package/dist/registry/model.js +104 -0
- package/dist/registry/ops.js +47 -0
- package/dist/registry/owner.js +349 -0
- package/dist/registry/packaged-backend.js +59 -0
- package/dist/registry/parse.js +129 -0
- package/dist/registry/server.js +252 -0
- package/dist/registry/system.js +220 -0
- package/dist/registry/tasks.js +512 -0
- package/dist/registry/types.js +1 -0
- package/dist/registry/ui.js +51 -0
- package/dist/registry/work.js +207 -0
- package/dist/render/format.js +146 -0
- package/dist/render/model.js +29 -0
- package/dist/render/style.js +24 -0
- package/dist/render/table.js +46 -0
- package/dist/render/views.js +256 -0
- package/dist/run.js +133 -0
- package/dist/setup.js +26 -0
- package/dist/skill.js +83 -0
- package/dist/topics.js +121 -0
- package/dist/transport/client.js +196 -0
- package/dist/ui/agents.js +12 -0
- package/dist/ui/api.js +99 -0
- package/dist/ui/app.js +62 -0
- package/dist/ui/budget.js +42 -0
- package/dist/ui/controller.js +368 -0
- package/dist/ui/effects.js +166 -0
- package/dist/ui/frame.js +206 -0
- package/dist/ui/handle.js +271 -0
- package/dist/ui/health.js +22 -0
- package/dist/ui/keys.js +120 -0
- package/dist/ui/line.js +56 -0
- package/dist/ui/lists.js +58 -0
- package/dist/ui/model.js +25 -0
- package/dist/ui/quickadd.js +66 -0
- package/dist/ui/rows.js +68 -0
- package/dist/ui/run.js +54 -0
- package/dist/ui/screens/agents.js +65 -0
- package/dist/ui/screens/board.js +41 -0
- package/dist/ui/screens/detail.js +142 -0
- package/dist/ui/screens/overlay.js +58 -0
- package/dist/ui/screens/screen.js +3 -0
- package/dist/ui/screens/today.js +60 -0
- package/dist/ui/screens/week.js +76 -0
- package/dist/ui/state.js +128 -0
- package/dist/ui/text.js +70 -0
- package/dist/when/calendar.js +95 -0
- package/dist/when/parse.js +314 -0
- package/man/dayhand.1 +1163 -0
- package/man/dh.1 +1 -0
- package/package.json +56 -4
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated utilities for implementing server-side Convex query and mutation functions.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
ActionBuilder,
|
|
13
|
+
HttpActionBuilder,
|
|
14
|
+
MutationBuilder,
|
|
15
|
+
QueryBuilder,
|
|
16
|
+
GenericActionCtx,
|
|
17
|
+
GenericMutationCtx,
|
|
18
|
+
GenericQueryCtx,
|
|
19
|
+
GenericDatabaseReader,
|
|
20
|
+
GenericDatabaseWriter,
|
|
21
|
+
} from "convex/server";
|
|
22
|
+
import type { DataModel } from "./dataModel.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Typesafe environment variables.
|
|
26
|
+
*
|
|
27
|
+
* This includes platform-provided env vars and any variables declared in
|
|
28
|
+
* `convex.config.ts`.
|
|
29
|
+
*/
|
|
30
|
+
type Env = {
|
|
31
|
+
readonly CONVEX_CLOUD_URL: string;
|
|
32
|
+
readonly CONVEX_SITE_URL: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Define a query in this Convex app's public API.
|
|
37
|
+
*
|
|
38
|
+
* This function will be allowed to read your Convex database and will be accessible from the client.
|
|
39
|
+
*
|
|
40
|
+
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
41
|
+
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
42
|
+
*/
|
|
43
|
+
export declare const query: QueryBuilder<DataModel, "public">;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Define a query that is only accessible from other Convex functions (but not from the client).
|
|
47
|
+
*
|
|
48
|
+
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
|
|
49
|
+
*
|
|
50
|
+
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
51
|
+
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
52
|
+
*/
|
|
53
|
+
export declare const internalQuery: QueryBuilder<DataModel, "internal">;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Define a mutation in this Convex app's public API.
|
|
57
|
+
*
|
|
58
|
+
* This function will be allowed to modify your Convex database and will be accessible from the client.
|
|
59
|
+
*
|
|
60
|
+
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
61
|
+
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
62
|
+
*/
|
|
63
|
+
export declare const mutation: MutationBuilder<DataModel, "public">;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Define a mutation that is only accessible from other Convex functions (but not from the client).
|
|
67
|
+
*
|
|
68
|
+
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
|
|
69
|
+
*
|
|
70
|
+
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
71
|
+
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
72
|
+
*/
|
|
73
|
+
export declare const internalMutation: MutationBuilder<DataModel, "internal">;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Define an action in this Convex app's public API.
|
|
77
|
+
*
|
|
78
|
+
* An action is a function which can execute any JavaScript code, including non-deterministic
|
|
79
|
+
* code and code with side-effects, like calling third-party services.
|
|
80
|
+
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
|
|
81
|
+
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
|
|
82
|
+
*
|
|
83
|
+
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
|
|
84
|
+
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
|
|
85
|
+
*/
|
|
86
|
+
export declare const action: ActionBuilder<DataModel, "public">;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Define an action that is only accessible from other Convex functions (but not from the client).
|
|
90
|
+
*
|
|
91
|
+
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
|
92
|
+
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
|
|
93
|
+
*/
|
|
94
|
+
export declare const internalAction: ActionBuilder<DataModel, "internal">;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Define an HTTP action.
|
|
98
|
+
*
|
|
99
|
+
* The wrapped function will be used to respond to HTTP requests received
|
|
100
|
+
* by a Convex deployment if the requests matches the path and method where
|
|
101
|
+
* this action is routed. Be sure to route your httpAction in `convex/http.js`.
|
|
102
|
+
*
|
|
103
|
+
* @param func - The function. It receives an {@link ActionCtx} as its first argument
|
|
104
|
+
* and a Fetch API `Request` object as its second.
|
|
105
|
+
* @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
|
|
106
|
+
*/
|
|
107
|
+
export declare const httpAction: HttpActionBuilder;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Typesafe environment variables.
|
|
111
|
+
*
|
|
112
|
+
* This includes platform-provided env vars and any variables declared in
|
|
113
|
+
* `convex.config.ts`.
|
|
114
|
+
*/
|
|
115
|
+
export declare const env: Env;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* A set of services for use within Convex query functions.
|
|
119
|
+
*
|
|
120
|
+
* The query context is passed as the first argument to any Convex query
|
|
121
|
+
* function run on the server.
|
|
122
|
+
*
|
|
123
|
+
* This differs from the {@link MutationCtx} because all of the services are
|
|
124
|
+
* read-only.
|
|
125
|
+
*/
|
|
126
|
+
export type QueryCtx = GenericQueryCtx<DataModel>;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* A set of services for use within Convex mutation functions.
|
|
130
|
+
*
|
|
131
|
+
* The mutation context is passed as the first argument to any Convex mutation
|
|
132
|
+
* function run on the server.
|
|
133
|
+
*/
|
|
134
|
+
export type MutationCtx = GenericMutationCtx<DataModel>;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* A set of services for use within Convex action functions.
|
|
138
|
+
*
|
|
139
|
+
* The action context is passed as the first argument to any Convex action
|
|
140
|
+
* function run on the server.
|
|
141
|
+
*/
|
|
142
|
+
export type ActionCtx = GenericActionCtx<DataModel>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* An interface to read from the database within Convex query functions.
|
|
146
|
+
*
|
|
147
|
+
* The two entry points are {@link DatabaseReader.get}, which fetches a single
|
|
148
|
+
* document by its {@link Id}, or {@link DatabaseReader.query}, which starts
|
|
149
|
+
* building a query.
|
|
150
|
+
*/
|
|
151
|
+
export type DatabaseReader = GenericDatabaseReader<DataModel>;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* An interface to read from and write to the database within Convex mutation
|
|
155
|
+
* functions.
|
|
156
|
+
*
|
|
157
|
+
* Convex guarantees that all writes within a single mutation are
|
|
158
|
+
* executed atomically, so you never have to worry about partial writes leaving
|
|
159
|
+
* your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control)
|
|
160
|
+
* for the guarantees Convex provides your functions.
|
|
161
|
+
*/
|
|
162
|
+
export type DatabaseWriter = GenericDatabaseWriter<DataModel>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated utilities for implementing server-side Convex query and mutation functions.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
actionGeneric,
|
|
13
|
+
httpActionGeneric,
|
|
14
|
+
queryGeneric,
|
|
15
|
+
mutationGeneric,
|
|
16
|
+
internalActionGeneric,
|
|
17
|
+
internalMutationGeneric,
|
|
18
|
+
internalQueryGeneric,
|
|
19
|
+
} from "convex/server";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Define a query in this Convex app's public API.
|
|
23
|
+
*
|
|
24
|
+
* This function will be allowed to read your Convex database and will be accessible from the client.
|
|
25
|
+
*
|
|
26
|
+
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
27
|
+
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
28
|
+
*/
|
|
29
|
+
export const query = queryGeneric;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Define a query that is only accessible from other Convex functions (but not from the client).
|
|
33
|
+
*
|
|
34
|
+
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
|
|
35
|
+
*
|
|
36
|
+
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
|
37
|
+
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
|
38
|
+
*/
|
|
39
|
+
export const internalQuery = internalQueryGeneric;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Define a mutation in this Convex app's public API.
|
|
43
|
+
*
|
|
44
|
+
* This function will be allowed to modify your Convex database and will be accessible from the client.
|
|
45
|
+
*
|
|
46
|
+
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
47
|
+
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
48
|
+
*/
|
|
49
|
+
export const mutation = mutationGeneric;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Define a mutation that is only accessible from other Convex functions (but not from the client).
|
|
53
|
+
*
|
|
54
|
+
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
|
|
55
|
+
*
|
|
56
|
+
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
|
57
|
+
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
|
58
|
+
*/
|
|
59
|
+
export const internalMutation = internalMutationGeneric;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Define an action in this Convex app's public API.
|
|
63
|
+
*
|
|
64
|
+
* An action is a function which can execute any JavaScript code, including non-deterministic
|
|
65
|
+
* code and code with side-effects, like calling third-party services.
|
|
66
|
+
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
|
|
67
|
+
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
|
|
68
|
+
*
|
|
69
|
+
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
|
|
70
|
+
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
|
|
71
|
+
*/
|
|
72
|
+
export const action = actionGeneric;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Define an action that is only accessible from other Convex functions (but not from the client).
|
|
76
|
+
*
|
|
77
|
+
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
|
78
|
+
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
|
|
79
|
+
*/
|
|
80
|
+
export const internalAction = internalActionGeneric;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Define an HTTP action.
|
|
84
|
+
*
|
|
85
|
+
* The wrapped function will be used to respond to HTTP requests received
|
|
86
|
+
* by a Convex deployment if the requests matches the path and method where
|
|
87
|
+
* this action is routed. Be sure to route your httpAction in `convex/http.js`.
|
|
88
|
+
*
|
|
89
|
+
* @param func - The function. It receives an {@link ActionCtx} as its first argument
|
|
90
|
+
* and a Fetch API `Request` object as its second.
|
|
91
|
+
* @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
|
|
92
|
+
*/
|
|
93
|
+
export const httpAction = httpActionGeneric;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Typesafe environment variables.
|
|
97
|
+
*
|
|
98
|
+
* This includes platform-provided env vars and any variables declared in
|
|
99
|
+
* `convex.config.ts`.
|
|
100
|
+
*/
|
|
101
|
+
export const env = process.env;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Personal API keys for the agent (MCP and CLI) and for integrations (REST,
|
|
2
|
+
// docs/api.md). Only the SHA-256 hash is stored; the key is shown once, when
|
|
3
|
+
// it is created. A key's kind (cli or agent) is the actor of the changes made
|
|
4
|
+
// with it; the default is agent. Since 0.8.0 a key has a scope (read, write
|
|
5
|
+
// or full; absent = full) and may expire. A key created with another key
|
|
6
|
+
// (POST /api/v1/keys) only reads or writes and works only while that key does.
|
|
7
|
+
import { v } from "convex/values";
|
|
8
|
+
import { internal } from "./_generated/api";
|
|
9
|
+
import type { Doc, Id } from "./_generated/dataModel";
|
|
10
|
+
import { internalAction, internalMutation, internalQuery, type QueryCtx } from "./_generated/server";
|
|
11
|
+
import { args } from "./lib/args";
|
|
12
|
+
import { fail, identityOf, recordEvent } from "./lib/core";
|
|
13
|
+
import { randomBase64url, sha256Hex } from "./lib/secrets";
|
|
14
|
+
import { identityFields as identityArgs, keyKindValidator, keyScopeValidator, type KeyKind, type KeyScope } from "./lib/validators";
|
|
15
|
+
|
|
16
|
+
// New keys start with dayhand_; keys from before the rename keep helm_ and still work (they are found by their hash).
|
|
17
|
+
const KEY_PREFIX = "dayhand_";
|
|
18
|
+
const MAX_NAME = 100;
|
|
19
|
+
const DEFAULT_KIND: KeyKind = "agent";
|
|
20
|
+
const DEFAULT_SCOPE: KeyScope = "full";
|
|
21
|
+
|
|
22
|
+
/** Kind of a stored key; keys from before kinds existed are agent keys. */
|
|
23
|
+
const kindOf = (key: { kind?: KeyKind }): KeyKind => key.kind ?? DEFAULT_KIND;
|
|
24
|
+
/** Scope of a stored key; keys from before scopes existed may do everything. */
|
|
25
|
+
export const scopeOf = (key: { scope?: KeyScope }): KeyScope => key.scope ?? DEFAULT_SCOPE;
|
|
26
|
+
|
|
27
|
+
const SCOPE_RANK: Record<KeyScope, number> = { read: 0, write: 1, full: 2 };
|
|
28
|
+
/** Whether a key of scope `has` may do what needs `needs` (read < write < full). */
|
|
29
|
+
export const scopeCovers = (has: KeyScope, needs: KeyScope) => SCOPE_RANK[has] >= SCOPE_RANK[needs];
|
|
30
|
+
|
|
31
|
+
export const hashKey = sha256Hex;
|
|
32
|
+
|
|
33
|
+
function newKey(): string {
|
|
34
|
+
// Actions have a real random source (mutations are deterministic).
|
|
35
|
+
return KEY_PREFIX + randomBase64url(32);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type CreatedKey = {
|
|
39
|
+
id: Id<"apiKeys">;
|
|
40
|
+
name: string;
|
|
41
|
+
kind: KeyKind;
|
|
42
|
+
scope: KeyScope;
|
|
43
|
+
expiresAt: number | null;
|
|
44
|
+
key: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Domain operation createApiKey: returns the plain key exactly once. */
|
|
48
|
+
export const create = internalAction({
|
|
49
|
+
args: { ...identityArgs, ...args.createApiKey },
|
|
50
|
+
handler: async (ctx, input): Promise<CreatedKey> => {
|
|
51
|
+
const key = newKey();
|
|
52
|
+
const id: Id<"apiKeys"> = await ctx.runMutation(internal.apiKeys.insert, { ...input, hash: await hashKey(key) });
|
|
53
|
+
return {
|
|
54
|
+
id,
|
|
55
|
+
name: input.name.trim(),
|
|
56
|
+
kind: input.kind ?? DEFAULT_KIND,
|
|
57
|
+
scope: input.scope ?? DEFAULT_SCOPE,
|
|
58
|
+
expiresAt: input.expiresAt ?? null,
|
|
59
|
+
key,
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export const insert = internalMutation({
|
|
65
|
+
args: { ...identityArgs, ...args.createApiKey, hash: v.string() },
|
|
66
|
+
handler: async (ctx, input) => {
|
|
67
|
+
const name = input.name.trim();
|
|
68
|
+
if (!name || name.length > MAX_NAME) fail("invalid_argument", `name must be 1 to ${MAX_NAME} characters`);
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
if (input.expiresAt !== undefined && (!Number.isFinite(input.expiresAt) || input.expiresAt <= now)) {
|
|
71
|
+
fail("invalid_argument", "expiresAt must be a time in the future (Unix milliseconds)");
|
|
72
|
+
}
|
|
73
|
+
const kind = input.kind ?? DEFAULT_KIND;
|
|
74
|
+
const scope = input.scope ?? DEFAULT_SCOPE;
|
|
75
|
+
// A key made with a key: never more than read or write, and never beyond its parent.
|
|
76
|
+
const parentKeyId = input.keyId;
|
|
77
|
+
if (parentKeyId !== undefined) {
|
|
78
|
+
if (scope === "full") fail("invalid_argument", "A key created with an API key can only have scope read or write");
|
|
79
|
+
const parent = await ctx.db.get(parentKeyId);
|
|
80
|
+
if (parent?.expiresAt !== undefined && (input.expiresAt === undefined || input.expiresAt > parent.expiresAt)) {
|
|
81
|
+
fail("invalid_argument", "The new key must expire no later than the key that creates it");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const id = await ctx.db.insert("apiKeys", {
|
|
85
|
+
userId: input.userId,
|
|
86
|
+
name,
|
|
87
|
+
kind,
|
|
88
|
+
scope,
|
|
89
|
+
expiresAt: input.expiresAt,
|
|
90
|
+
parentKeyId,
|
|
91
|
+
hash: input.hash,
|
|
92
|
+
createdAt: now,
|
|
93
|
+
});
|
|
94
|
+
const after = { keyId: id, name, kind, scope, ...(input.expiresAt !== undefined ? { expiresAt: input.expiresAt } : {}) };
|
|
95
|
+
await recordEvent(ctx, identityOf(input), { action: "apiKey.create", after });
|
|
96
|
+
return id;
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/** Domain operation revokeApiKey: the key (and every key it created) stops working on the next request. */
|
|
101
|
+
export const revoke = internalMutation({
|
|
102
|
+
args: { ...identityArgs, ...args.revokeApiKey },
|
|
103
|
+
handler: async (ctx, input) => {
|
|
104
|
+
const { userId, keyId } = input;
|
|
105
|
+
const key = await ctx.db.get(keyId);
|
|
106
|
+
if (!key || key.userId !== userId) fail("not_found", `API key ${keyId} not found`);
|
|
107
|
+
if (key.revokedAt !== undefined) return { id: key._id, revokedAt: key.revokedAt };
|
|
108
|
+
const revokedAt = Date.now();
|
|
109
|
+
await ctx.db.patch(key._id, { revokedAt });
|
|
110
|
+
await recordEvent(ctx, identityOf(input), { action: "apiKey.revoke", after: { keyId, name: key.name } });
|
|
111
|
+
return { id: key._id, revokedAt };
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
export type KeyView = {
|
|
116
|
+
id: Id<"apiKeys">;
|
|
117
|
+
name: string;
|
|
118
|
+
kind: KeyKind;
|
|
119
|
+
scope: KeyScope;
|
|
120
|
+
createdAt: number;
|
|
121
|
+
expiresAt: number | null;
|
|
122
|
+
revokedAt: number | null;
|
|
123
|
+
createdWith: Id<"apiKeys"> | null;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const keyView = (k: Doc<"apiKeys">): KeyView => ({
|
|
127
|
+
id: k._id,
|
|
128
|
+
name: k.name,
|
|
129
|
+
kind: kindOf(k),
|
|
130
|
+
scope: scopeOf(k),
|
|
131
|
+
createdAt: k.createdAt,
|
|
132
|
+
expiresAt: k.expiresAt ?? null,
|
|
133
|
+
revokedAt: k.revokedAt ?? null,
|
|
134
|
+
createdWith: k.parentKeyId ?? null,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
/** Key metadata for the settings screen and GET /api/v1/keys; never the hash. */
|
|
138
|
+
export async function listKeys(ctx: QueryCtx, userId: string): Promise<KeyView[]> {
|
|
139
|
+
const keys = await ctx.db
|
|
140
|
+
.query("apiKeys")
|
|
141
|
+
.withIndex("by_user", (q) => q.eq("userId", userId))
|
|
142
|
+
.collect();
|
|
143
|
+
return keys.map(keyView);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const list = internalQuery({
|
|
147
|
+
args: identityArgs,
|
|
148
|
+
handler: (ctx, { userId }) => listKeys(ctx, userId),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
/** A key works when it is not revoked, not expired, and the key that created it still works. */
|
|
152
|
+
const works = (key: Doc<"apiKeys"> | null, now: number): key is Doc<"apiKeys"> =>
|
|
153
|
+
key !== null && key.revokedAt === undefined && (key.expiresAt === undefined || key.expiresAt > now);
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Resolves the owner of a working key from its hash; null when unknown, revoked or expired.
|
|
157
|
+
* `now` comes from the caller so a cached result never outlives an expiry.
|
|
158
|
+
*/
|
|
159
|
+
export const verify = internalQuery({
|
|
160
|
+
args: { hash: v.string(), now: v.optional(v.number()) },
|
|
161
|
+
handler: async (ctx, { hash, now }) => {
|
|
162
|
+
const at = now ?? Date.now();
|
|
163
|
+
const key = await ctx.db
|
|
164
|
+
.query("apiKeys")
|
|
165
|
+
.withIndex("by_hash", (q) => q.eq("hash", hash))
|
|
166
|
+
.unique();
|
|
167
|
+
if (!works(key, at)) return null;
|
|
168
|
+
if (key.parentKeyId !== undefined && !works(await ctx.db.get(key.parentKeyId), at)) return null;
|
|
169
|
+
return { userId: key.userId, keyId: key._id, name: key.name, kind: kindOf(key), scope: scopeOf(key), expiresAt: key.expiresAt ?? null };
|
|
170
|
+
},
|
|
171
|
+
});
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Thin layer for the Dayhand app: the caller is the authenticated user and
|
|
2
|
+
// the actor is "app". Every function delegates to the domain layer.
|
|
3
|
+
import { internal } from "./_generated/api";
|
|
4
|
+
import type { Id } from "./_generated/dataModel";
|
|
5
|
+
import { v } from "convex/values";
|
|
6
|
+
import { action, mutation, query } from "./_generated/server";
|
|
7
|
+
import { listKeys, type CreatedKey } from "./apiKeys";
|
|
8
|
+
import { getFeed, revokeFeed, type CalendarFeed } from "./calendarFeeds";
|
|
9
|
+
import {
|
|
10
|
+
decideRequest,
|
|
11
|
+
listConnections as listConnectionsOp,
|
|
12
|
+
lookupRequest,
|
|
13
|
+
revokeConnection as revokeConnectionOp,
|
|
14
|
+
} from "./oauthOwner";
|
|
15
|
+
import { args } from "./lib/args";
|
|
16
|
+
import { fail, type Identity } from "./lib/core";
|
|
17
|
+
import { exportPlan as exportPlanOp } from "./lib/exportPlan";
|
|
18
|
+
import { scopeValidator } from "./lib/oauthConfig";
|
|
19
|
+
import { getSettings as getSettingsOp, updateSettings as updateSettingsOp } from "./lib/settingsOps";
|
|
20
|
+
import { releaseTask } from "./lib/agentWork";
|
|
21
|
+
import { getAgentControls as getAgentControlsOp, setAgentsPaused, setToolPolicy as setToolPolicyOp } from "./lib/agentControls";
|
|
22
|
+
import { decideApproval as decideApprovalOp, listApprovals as listApprovalsOp } from "./lib/approvals";
|
|
23
|
+
import { planHealth as planHealthOp } from "./lib/health";
|
|
24
|
+
import { TOOLS } from "./lib/mcpTools";
|
|
25
|
+
import {
|
|
26
|
+
acceptProposal as acceptProposalOp,
|
|
27
|
+
listProposals as listProposalsOp,
|
|
28
|
+
mergeProposal as mergeProposalOp,
|
|
29
|
+
rejectProposal as rejectProposalOp,
|
|
30
|
+
} from "./lib/proposals";
|
|
31
|
+
import {
|
|
32
|
+
addTask,
|
|
33
|
+
archiveTask,
|
|
34
|
+
completeTask,
|
|
35
|
+
rescheduleTask,
|
|
36
|
+
restoreTask,
|
|
37
|
+
setNow as setNowOp,
|
|
38
|
+
setStatus as setStatusOp,
|
|
39
|
+
softDeleteTask,
|
|
40
|
+
updateTask,
|
|
41
|
+
} from "./lib/taskOps";
|
|
42
|
+
import {
|
|
43
|
+
board as boardOp,
|
|
44
|
+
getTask as getTaskOp,
|
|
45
|
+
listAll as listAllOp,
|
|
46
|
+
listToday as listTodayOp,
|
|
47
|
+
listTrash as listTrashOp,
|
|
48
|
+
} from "./lib/views";
|
|
49
|
+
|
|
50
|
+
type AuthCtx = { auth: { getUserIdentity: () => Promise<{ tokenIdentifier: string } | null> } };
|
|
51
|
+
|
|
52
|
+
async function appUser(ctx: AuthCtx): Promise<Identity> {
|
|
53
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
54
|
+
if (!identity) fail("unauthenticated", "Sign in required");
|
|
55
|
+
return { userId: identity.tokenIdentifier, actor: "app" };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const add = mutation({ args: args.add, handler: async (ctx, a) => addTask(ctx, await appUser(ctx), a) });
|
|
59
|
+
export const update = mutation({
|
|
60
|
+
args: args.update,
|
|
61
|
+
handler: async (ctx, a) => updateTask(ctx, await appUser(ctx), a),
|
|
62
|
+
});
|
|
63
|
+
export const setStatus = mutation({
|
|
64
|
+
args: args.setStatus,
|
|
65
|
+
handler: async (ctx, a) => setStatusOp(ctx, await appUser(ctx), a),
|
|
66
|
+
});
|
|
67
|
+
export const setNow = mutation({ args: args.setNow, handler: async (ctx, a) => setNowOp(ctx, await appUser(ctx), a) });
|
|
68
|
+
export const complete = mutation({
|
|
69
|
+
args: args.complete,
|
|
70
|
+
handler: async (ctx, a) => completeTask(ctx, await appUser(ctx), a),
|
|
71
|
+
});
|
|
72
|
+
export const reschedule = mutation({
|
|
73
|
+
args: args.reschedule,
|
|
74
|
+
handler: async (ctx, a) => rescheduleTask(ctx, await appUser(ctx), a),
|
|
75
|
+
});
|
|
76
|
+
export const archive = mutation({
|
|
77
|
+
args: args.archive,
|
|
78
|
+
handler: async (ctx, a) => archiveTask(ctx, await appUser(ctx), a),
|
|
79
|
+
});
|
|
80
|
+
export const softDelete = mutation({
|
|
81
|
+
args: args.softDelete,
|
|
82
|
+
handler: async (ctx, a) => softDeleteTask(ctx, await appUser(ctx), a),
|
|
83
|
+
});
|
|
84
|
+
export const restore = mutation({
|
|
85
|
+
args: args.restore,
|
|
86
|
+
handler: async (ctx, a) => restoreTask(ctx, await appUser(ctx), a),
|
|
87
|
+
});
|
|
88
|
+
// The owner takes a task over from an agent: the claim ends at once (owner wins).
|
|
89
|
+
export const release = mutation({
|
|
90
|
+
args: args.release,
|
|
91
|
+
handler: async (ctx, a) => releaseTask(ctx, await appUser(ctx), a),
|
|
92
|
+
});
|
|
93
|
+
export const updateSettings = mutation({
|
|
94
|
+
args: args.updateSettings,
|
|
95
|
+
handler: async (ctx, a) => updateSettingsOp(ctx, await appUser(ctx), a),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Approval requests (docs/agent-work.md#5-approval-requests): the phone lists them and decides.
|
|
99
|
+
export const listApprovals = query({
|
|
100
|
+
args: args.listApprovals,
|
|
101
|
+
handler: async (ctx, a) => listApprovalsOp(ctx, await appUser(ctx), a),
|
|
102
|
+
});
|
|
103
|
+
export const decideApproval = mutation({
|
|
104
|
+
args: args.decideApproval,
|
|
105
|
+
handler: async (ctx, a) => decideApprovalOp(ctx, await appUser(ctx), a),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// The agent switch and tool policies (docs/agent-work.md#6-pausing-agents-and-tool-policies).
|
|
109
|
+
export const getAgentControls = query({ args: {}, handler: async (ctx) => getAgentControlsOp(ctx, await appUser(ctx)) });
|
|
110
|
+
export const pauseAgents = mutation({
|
|
111
|
+
args: args.pauseAgents,
|
|
112
|
+
handler: async (ctx, a) => setAgentsPaused(ctx, await appUser(ctx), a),
|
|
113
|
+
});
|
|
114
|
+
export const setToolPolicy = mutation({
|
|
115
|
+
args: args.setToolPolicy,
|
|
116
|
+
handler: async (ctx, a) =>
|
|
117
|
+
setToolPolicyOp(ctx, await appUser(ctx), a, TOOLS.filter((t) => t.kind === "write" && !t.ownerOnly).map((t) => t.name)),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// The proposal inbox (docs/agent-work.md#7-the-proposal-inbox).
|
|
121
|
+
export const listProposals = query({ args: {}, handler: async (ctx) => listProposalsOp(ctx, await appUser(ctx)) });
|
|
122
|
+
export const acceptProposal = mutation({
|
|
123
|
+
args: args.acceptProposal,
|
|
124
|
+
handler: async (ctx, a) => acceptProposalOp(ctx, await appUser(ctx), a),
|
|
125
|
+
});
|
|
126
|
+
export const mergeProposal = mutation({
|
|
127
|
+
args: args.mergeProposal,
|
|
128
|
+
handler: async (ctx, a) => mergeProposalOp(ctx, await appUser(ctx), a),
|
|
129
|
+
});
|
|
130
|
+
export const rejectProposal = mutation({
|
|
131
|
+
args: args.rejectProposal,
|
|
132
|
+
handler: async (ctx, a) => rejectProposalOp(ctx, await appUser(ctx), a),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// The plan health report (docs/agent-work.md#8-the-plan-health-report).
|
|
136
|
+
export const planHealth = query({ args: args.planHealth, handler: async (ctx, a) => planHealthOp(ctx, await appUser(ctx), a) });
|
|
137
|
+
|
|
138
|
+
export const listToday = query({
|
|
139
|
+
args: args.listToday,
|
|
140
|
+
handler: async (ctx, a) => listTodayOp(ctx, await appUser(ctx), a),
|
|
141
|
+
});
|
|
142
|
+
export const listAll = query({ args: args.listAll, handler: async (ctx, a) => listAllOp(ctx, await appUser(ctx), a) });
|
|
143
|
+
export const listTrash = query({ args: args.listTrash, handler: async (ctx) => listTrashOp(ctx, await appUser(ctx)) });
|
|
144
|
+
export const board = query({ args: args.board, handler: async (ctx, a) => boardOp(ctx, await appUser(ctx), a) });
|
|
145
|
+
export const getTask = query({ args: args.getTask, handler: async (ctx, a) => getTaskOp(ctx, await appUser(ctx), a) });
|
|
146
|
+
export const exportPlan = query({
|
|
147
|
+
args: args.exportPlan,
|
|
148
|
+
handler: async (ctx, a) => exportPlanOp(ctx, await appUser(ctx), a),
|
|
149
|
+
});
|
|
150
|
+
export const getSettings = query({ args: args.getSettings, handler: async (ctx) => getSettingsOp(ctx, await appUser(ctx)) });
|
|
151
|
+
|
|
152
|
+
export const createApiKey = action({
|
|
153
|
+
args: args.createApiKey,
|
|
154
|
+
handler: async (ctx, a): Promise<CreatedKey> => ctx.runAction(internal.apiKeys.create, { ...(await appUser(ctx)), ...a }),
|
|
155
|
+
});
|
|
156
|
+
export const revokeApiKey = mutation({
|
|
157
|
+
args: args.revokeApiKey,
|
|
158
|
+
handler: async (ctx, a): Promise<{ id: Id<"apiKeys">; revokedAt: number }> =>
|
|
159
|
+
ctx.runMutation(internal.apiKeys.revoke, { ...(await appUser(ctx)), ...a }),
|
|
160
|
+
});
|
|
161
|
+
export const listApiKeys = query({
|
|
162
|
+
args: args.listApiKeys,
|
|
163
|
+
handler: async (ctx) => listKeys(ctx, (await appUser(ctx)).userId),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// The calendar feed (calendarFeeds.ts): its addresses, turning it on, a new address, off.
|
|
167
|
+
export const getCalendarFeed = query({
|
|
168
|
+
args: {},
|
|
169
|
+
handler: async (ctx): Promise<CalendarFeed | null> => getFeed(ctx, await appUser(ctx)),
|
|
170
|
+
});
|
|
171
|
+
export const createCalendarFeed = action({
|
|
172
|
+
args: { replace: v.optional(v.boolean()) },
|
|
173
|
+
handler: async (ctx, a): Promise<CalendarFeed> => ctx.runAction(internal.calendarFeeds.create, { ...(await appUser(ctx)), ...a }),
|
|
174
|
+
});
|
|
175
|
+
export const revokeCalendarFeed = mutation({
|
|
176
|
+
args: {},
|
|
177
|
+
handler: async (ctx): Promise<{ revoked: boolean }> => revokeFeed(ctx, await appUser(ctx)),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Agent connections (OAuth, docs/oauth.md): the phone approves a connection by the
|
|
181
|
+
// code its browser page shows, and lists and revokes connections.
|
|
182
|
+
export const lookupConnectionRequest = mutation({
|
|
183
|
+
args: { code: v.string() },
|
|
184
|
+
handler: async (ctx, { code }) => lookupRequest(ctx, await appUser(ctx), code),
|
|
185
|
+
});
|
|
186
|
+
export const decideConnectionRequest = mutation({
|
|
187
|
+
args: { code: v.string(), approve: v.boolean(), scopes: v.optional(v.array(scopeValidator)) },
|
|
188
|
+
handler: async (ctx, a) => decideRequest(ctx, await appUser(ctx), a),
|
|
189
|
+
});
|
|
190
|
+
export const listConnections = query({
|
|
191
|
+
args: {},
|
|
192
|
+
handler: async (ctx) => listConnectionsOp(ctx, (await appUser(ctx)).userId),
|
|
193
|
+
});
|
|
194
|
+
export const revokeConnection = mutation({
|
|
195
|
+
args: { connectionId: v.string() },
|
|
196
|
+
handler: async (ctx, { connectionId }) => revokeConnectionOp(ctx, await appUser(ctx), connectionId),
|
|
197
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// App sign-in: a custom JWT (ES256) whose public key is a JWKS data URI in
|
|
2
|
+
// deployment environment variables, so no external identity provider is
|
|
3
|
+
// needed. `npm run backend:start` generates a local key pair and sets both.
|
|
4
|
+
// Deployments from before the rename have them as HELM_* (lib/env.ts). Convex
|
|
5
|
+
// refuses a push when this file reads a variable the deployment does not have,
|
|
6
|
+
// so the old name is read only when the new one is missing. The audience
|
|
7
|
+
// stays "helm": tokens already on phones carry it.
|
|
8
|
+
function variable(name: string): string {
|
|
9
|
+
try {
|
|
10
|
+
const value = process.env[`DAYHAND_${name}`];
|
|
11
|
+
if (value) return value;
|
|
12
|
+
} catch {
|
|
13
|
+
// The deployment has no DAYHAND_<name>.
|
|
14
|
+
}
|
|
15
|
+
return process.env[`HELM_${name}`]!;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
providers: [
|
|
20
|
+
{
|
|
21
|
+
type: "customJwt",
|
|
22
|
+
applicationID: "helm",
|
|
23
|
+
issuer: variable("JWT_ISSUER"),
|
|
24
|
+
jwks: variable("JWKS"),
|
|
25
|
+
algorithm: "ES256",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|