hoja-cli 0.21.4__py3-none-any.whl
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.
- VERSION +1 -0
- google_workspace/__init__.py +19 -0
- google_workspace/client.py +253 -0
- google_workspace/config.py +39 -0
- google_workspace/errors.py +19 -0
- google_workspace/mcp_server.py +100 -0
- google_workspace/oauth.py +131 -0
- google_workspace/principal.py +83 -0
- google_workspace/py.typed +0 -0
- google_workspace/tools.py +64 -0
- google_workspace/writes.py +44 -0
- hoja/__init__.py +29 -0
- hoja/__main__.py +6 -0
- hoja/auth.py +42 -0
- hoja/cli.py +161 -0
- hoja/cli_state.py +55 -0
- hoja/client.py +268 -0
- hoja/commands/__init__.py +4 -0
- hoja/commands/auth_cmd.py +271 -0
- hoja/commands/comment.py +149 -0
- hoja/commands/cycle.py +75 -0
- hoja/commands/init.py +108 -0
- hoja/commands/issue.py +398 -0
- hoja/commands/label.py +163 -0
- hoja/commands/memory.py +240 -0
- hoja/commands/project.py +56 -0
- hoja/commands/raw.py +50 -0
- hoja/commands/team.py +102 -0
- hoja/commands/update.py +162 -0
- hoja/config.py +362 -0
- hoja/errors.py +107 -0
- hoja/filters.py +150 -0
- hoja/graphql/__init__.py +5 -0
- hoja/graphql/fragments.py +138 -0
- hoja/graphql/mutations.py +160 -0
- hoja/graphql/queries.py +245 -0
- hoja/mcp/__init__.py +27 -0
- hoja/mcp/content.py +151 -0
- hoja/mcp/prompts.py +298 -0
- hoja/mcp/resources.py +213 -0
- hoja/mcp/server.py +189 -0
- hoja/memory_board.py +370 -0
- hoja/models/__init__.py +37 -0
- hoja/models/base.py +64 -0
- hoja/models/cycle.py +32 -0
- hoja/models/enums.py +62 -0
- hoja/models/issue.py +99 -0
- hoja/models/label.py +17 -0
- hoja/models/project.py +60 -0
- hoja/models/team.py +31 -0
- hoja/models/user.py +32 -0
- hoja/models/workflow.py +65 -0
- hoja/output.py +410 -0
- hoja/skill_content/commands.md +222 -0
- hoja/skill_content/examples.md +193 -0
- hoja/skill_content/filters.md +101 -0
- hoja/skill_content/output-formats.md +92 -0
- hoja/skill_content/overview.md +123 -0
- hoja/skill_content/workflows.md +174 -0
- hoja_cli-0.21.4.dist-info/METADATA +279 -0
- hoja_cli-0.21.4.dist-info/RECORD +107 -0
- hoja_cli-0.21.4.dist-info/WHEEL +4 -0
- hoja_cli-0.21.4.dist-info/entry_points.txt +4 -0
- hoja_cli-0.21.4.dist-info/licenses/LICENSE +21 -0
- hoja_server/README.md +135 -0
- hoja_server/__init__.py +11 -0
- hoja_server/abac.py +116 -0
- hoja_server/agent_chat.py +280 -0
- hoja_server/agent_conversations.py +91 -0
- hoja_server/agent_durable.py +332 -0
- hoja_server/agent_prompt.py +117 -0
- hoja_server/agent_threads.py +96 -0
- hoja_server/agent_uploads.py +60 -0
- hoja_server/app.py +2189 -0
- hoja_server/cadence.py +231 -0
- hoja_server/cli.py +191 -0
- hoja_server/cloudflare_realtime.py +110 -0
- hoja_server/comment_policy.py +70 -0
- hoja_server/connector_actions.py +119 -0
- hoja_server/connectors_api.py +375 -0
- hoja_server/db.py +1816 -0
- hoja_server/denials.py +18 -0
- hoja_server/deployment.py +28 -0
- hoja_server/email_adapter.py +94 -0
- hoja_server/google_login.py +217 -0
- hoja_server/google_oidc.py +342 -0
- hoja_server/identity_transfer.py +210 -0
- hoja_server/mcp_oauth.py +623 -0
- hoja_server/mcp_tools.py +579 -0
- hoja_server/meeting_policy.py +179 -0
- hoja_server/meetings.py +1332 -0
- hoja_server/meetings_api.py +909 -0
- hoja_server/model_providers.py +205 -0
- hoja_server/realtime.py +690 -0
- hoja_server/resolvers.py +1281 -0
- hoja_server/schema.graphql +477 -0
- hoja_server/schema_sdl.py +8 -0
- hoja_server/secrets_box.py +39 -0
- hoja_server/signup_config.py +292 -0
- hoja_server/signup_gate.py +393 -0
- hoja_server/signup_policy.py +97 -0
- hoja_server/signup_store.py +577 -0
- hoja_server/store.py +2179 -0
- hoja_server/sync.py +249 -0
- hoja_server/tenant_context.py +131 -0
- hoja_server/turnstile.py +81 -0
- hoja_server/writer.py +1262 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
# Linear-compatible GraphQL SDL — the subset hoja actually uses.
|
|
2
|
+
# Field names are EXACT camelCase; hoja's Pydantic models alias to these.
|
|
3
|
+
|
|
4
|
+
scalar DateTime
|
|
5
|
+
scalar TimelessDate
|
|
6
|
+
scalar JSON
|
|
7
|
+
|
|
8
|
+
type Organization {
|
|
9
|
+
id: ID!
|
|
10
|
+
name: String!
|
|
11
|
+
urlKey: String!
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type ApiKey {
|
|
15
|
+
id: ID!
|
|
16
|
+
label: String
|
|
17
|
+
access: String
|
|
18
|
+
hint: String
|
|
19
|
+
expiresAt: DateTime
|
|
20
|
+
lastUsedAt: DateTime
|
|
21
|
+
createdAt: DateTime
|
|
22
|
+
revokedAt: DateTime
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type ApiKeyPayload {
|
|
26
|
+
success: Boolean!
|
|
27
|
+
apiKey: ApiKey
|
|
28
|
+
secret: String
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type UserPreferencesPayload {
|
|
32
|
+
success: Boolean!
|
|
33
|
+
theme: String
|
|
34
|
+
fontSize: String
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
type NotificationPreferencesPayload {
|
|
39
|
+
success: Boolean!
|
|
40
|
+
issueCreated: Boolean!
|
|
41
|
+
issueUpdated: Boolean!
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type WorkspaceMembership {
|
|
45
|
+
id: ID!
|
|
46
|
+
organizationId: ID!
|
|
47
|
+
name: String!
|
|
48
|
+
urlKey: String!
|
|
49
|
+
role: String!
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type OrganizationPayload {
|
|
53
|
+
success: Boolean!
|
|
54
|
+
organization: Organization
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type UserPayload {
|
|
58
|
+
success: Boolean!
|
|
59
|
+
user: User
|
|
60
|
+
apiKey: String
|
|
61
|
+
inviteCode: String
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
input InitiativeCreateInput { name: String! summary: String status: String priority: Int ownerId: String targetDate: TimelessDate projectIds: [String!] }
|
|
65
|
+
input InitiativeUpdateInput { name: String summary: String status: String priority: Int ownerId: String targetDate: TimelessDate projectIds: [String!] }
|
|
66
|
+
|
|
67
|
+
input WorkspaceUpdateInput {
|
|
68
|
+
name: String
|
|
69
|
+
urlKey: String
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
input WorkspaceCreateInput {
|
|
73
|
+
name: String!
|
|
74
|
+
urlKey: String!
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
input MemberInviteInput {
|
|
78
|
+
email: String!
|
|
79
|
+
name: String!
|
|
80
|
+
admin: Boolean = false
|
|
81
|
+
teamIds: [String!]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type Query {
|
|
85
|
+
viewer: User
|
|
86
|
+
myApiKeys: [ApiKey!]!
|
|
87
|
+
myPreferences: UserPreferencesPayload!
|
|
88
|
+
myNotificationPreferences: NotificationPreferencesPayload!
|
|
89
|
+
issueLabelSettings: [IssueLabel!]!
|
|
90
|
+
myWorkspaces: [WorkspaceMembership!]!
|
|
91
|
+
organizationMembers(first: Int = 100): UserConnection
|
|
92
|
+
pendingMembershipInvitations: [PendingMembershipInvitation!]!
|
|
93
|
+
workspace: Organization
|
|
94
|
+
teams(first: Int = 50, after: String, filter: TeamFilter): TeamConnection
|
|
95
|
+
team(id: String!): Team
|
|
96
|
+
issues(filter: IssueFilter, first: Int = 50, after: String, orderBy: PaginationOrderBy): IssueConnection
|
|
97
|
+
issue(id: String!): Issue
|
|
98
|
+
myIssueActivity(first: Int = 50, after: String): MyIssueActivityConnection
|
|
99
|
+
inboxNotifications(first: Int = 50, after: String, unreadOnly: Boolean = false, archived: Boolean = false): InboxNotificationConnection
|
|
100
|
+
inboxUnreadCount: Int!
|
|
101
|
+
projects(filter: ProjectFilter, first: Int = 50, after: String): ProjectConnection
|
|
102
|
+
initiatives(first: Int = 50, after: String, status: String): InitiativeConnection
|
|
103
|
+
initiative(id: String!): Initiative
|
|
104
|
+
projectUpdates(first: Int = 50, after: String, filter: ProjectUpdateFilter): ProjectUpdateConnection
|
|
105
|
+
project(id: String!): Project
|
|
106
|
+
workflowStates(filter: WorkflowStateFilter, first: Int = 100, after: String): WorkflowStateConnection
|
|
107
|
+
cycles(filter: CycleFilter, first: Int = 100, after: String): CycleConnection
|
|
108
|
+
cycle(id: String!): Cycle
|
|
109
|
+
teamCycleCadence(teamId: String!): TeamCycleCadence!
|
|
110
|
+
issueLabels(filter: IssueLabelFilter, first: Int = 100, after: String): IssueLabelConnection
|
|
111
|
+
searchIssues(term: String!, first: Int = 20): IssueConnection
|
|
112
|
+
rateLimitStatus: RateLimitPayload
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
type Mutation {
|
|
116
|
+
apiKeyCreate(label: String!, access: String = "read_write", expiresAt: DateTime): ApiKeyPayload
|
|
117
|
+
apiKeyRevoke(id: String!): Boolean!
|
|
118
|
+
userPreferenceSet(key: String!, value: String!): UserPreferencesPayload!
|
|
119
|
+
profileNameUpdate(name: String!): UserPayload
|
|
120
|
+
notificationPreferenceSet(kind: String!, enabled: Boolean!): NotificationPreferencesPayload!
|
|
121
|
+
workspaceCreate(input: WorkspaceCreateInput!): OrganizationPayload
|
|
122
|
+
teamCreate(input: TeamCreateInput!): TeamPayload
|
|
123
|
+
teamUpdate(id: String!, input: TeamUpdateInput!): TeamPayload
|
|
124
|
+
teamArchive(id: String!): DeletePayload
|
|
125
|
+
teamDelete(id: String!, deleteIssues: Boolean = false): DeletePayload
|
|
126
|
+
workspaceSwitch(organizationId: String!): OrganizationPayload
|
|
127
|
+
membershipInvitationCreate(userId: String!): UserPayload
|
|
128
|
+
membershipInvitationAccept(code: String!, inviteePassword: String!): OrganizationPayload
|
|
129
|
+
membershipInvitationRevoke(userId: String!): DeletePayload
|
|
130
|
+
memberDeactivate(userId: String!): UserPayload
|
|
131
|
+
memberReactivate(userId: String!): UserPayload
|
|
132
|
+
workspaceUpdate(input: WorkspaceUpdateInput!): OrganizationPayload
|
|
133
|
+
memberInvite(input: MemberInviteInput!): UserPayload
|
|
134
|
+
memberRoleUpdate(userId: String!, admin: Boolean!): UserPayload
|
|
135
|
+
teamMemberAdd(teamId: String!, userId: String!, role: String = "member"): UserPayload
|
|
136
|
+
teamMemberRemove(teamId: String!, userId: String!): UserPayload
|
|
137
|
+
issueCreate(input: IssueCreateInput!): IssuePayload
|
|
138
|
+
issueUpdate(id: String!, input: IssueUpdateInput!): IssuePayload
|
|
139
|
+
notificationMarkRead(id: String!): Boolean!
|
|
140
|
+
notificationArchive(id: String!): Boolean!
|
|
141
|
+
notificationMarkAllRead: Boolean!
|
|
142
|
+
issueDelete(id: String!): DeletePayload
|
|
143
|
+
issueArchive(id: String!): DeletePayload
|
|
144
|
+
issueSubscribe(id: String!): Boolean!
|
|
145
|
+
issueUnsubscribe(id: String!): Boolean!
|
|
146
|
+
commentCreate(input: CommentCreateInput!): CommentPayload
|
|
147
|
+
commentUpdate(id: String!, input: CommentUpdateInput!): CommentPayload
|
|
148
|
+
commentDelete(id: String!): DeletePayload
|
|
149
|
+
issueLabelCreate(input: IssueLabelCreateInput!): IssueLabelPayload
|
|
150
|
+
issueLabelDelete(id: String!): DeletePayload
|
|
151
|
+
issueLabelUpdate(id: String!, input: IssueLabelUpdateInput!): IssueLabelPayload
|
|
152
|
+
projectCreate(input: ProjectCreateInput!): ProjectPayload
|
|
153
|
+
projectUpdate(id: String!, input: ProjectUpdateInput!): ProjectPayload
|
|
154
|
+
projectUpdateCreate(input: ProjectUpdateCreateInput!): ProjectUpdatePayload
|
|
155
|
+
projectArchive(id: String!): DeletePayload
|
|
156
|
+
teamCycleCadenceEnable(teamId: String!, weekday: Int!): TeamCycleCadence!
|
|
157
|
+
cycleUpdate(id: String!, input: CycleUpdateInput!): CyclePayload
|
|
158
|
+
initiativeCreate(input: InitiativeCreateInput!): InitiativePayload
|
|
159
|
+
initiativeUpdate(id: String!, input: InitiativeUpdateInput!): InitiativePayload
|
|
160
|
+
initiativeArchive(id: String!): DeletePayload
|
|
161
|
+
attachmentCreate(input: AttachmentCreateInput!): AttachmentPayload
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
enum PaginationOrderBy { createdAt updatedAt }
|
|
165
|
+
|
|
166
|
+
type PageInfo {
|
|
167
|
+
hasNextPage: Boolean!
|
|
168
|
+
hasPreviousPage: Boolean!
|
|
169
|
+
startCursor: String
|
|
170
|
+
endCursor: String
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
type InboxNotification {
|
|
174
|
+
id: ID!
|
|
175
|
+
issue: Issue!
|
|
176
|
+
actor: User
|
|
177
|
+
kind: String!
|
|
178
|
+
createdAt: DateTime!
|
|
179
|
+
readAt: DateTime
|
|
180
|
+
archivedAt: DateTime
|
|
181
|
+
}
|
|
182
|
+
type InboxNotificationConnection { nodes: [InboxNotification!]! pageInfo: PageInfo! }
|
|
183
|
+
|
|
184
|
+
type User {
|
|
185
|
+
id: ID!
|
|
186
|
+
name: String
|
|
187
|
+
displayName: String
|
|
188
|
+
email: String
|
|
189
|
+
active: Boolean
|
|
190
|
+
isMe: Boolean
|
|
191
|
+
admin: Boolean
|
|
192
|
+
avatarUrl: String
|
|
193
|
+
url: String
|
|
194
|
+
timezone: String
|
|
195
|
+
statusEmoji: String
|
|
196
|
+
statusLabel: String
|
|
197
|
+
createdAt: DateTime
|
|
198
|
+
updatedAt: DateTime
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
type Team {
|
|
202
|
+
id: ID!
|
|
203
|
+
name: String!
|
|
204
|
+
key: String!
|
|
205
|
+
description: String
|
|
206
|
+
color: String
|
|
207
|
+
icon: String
|
|
208
|
+
visibility: String!
|
|
209
|
+
parentTeamId: ID
|
|
210
|
+
parent: Team
|
|
211
|
+
children(first: Int = 100): TeamConnection
|
|
212
|
+
timezone: String
|
|
213
|
+
createdAt: DateTime
|
|
214
|
+
updatedAt: DateTime
|
|
215
|
+
states(first: Int = 100): WorkflowStateConnection
|
|
216
|
+
members(first: Int = 100): UserConnection
|
|
217
|
+
cycles(first: Int = 50): CycleConnection
|
|
218
|
+
activeCycle: Cycle
|
|
219
|
+
autoAddStarted: Boolean
|
|
220
|
+
autoAddCompleted: Boolean
|
|
221
|
+
autoAddBacklog: Boolean
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
type WorkflowState {
|
|
225
|
+
id: ID!
|
|
226
|
+
name: String!
|
|
227
|
+
color: String
|
|
228
|
+
description: String
|
|
229
|
+
position: Float
|
|
230
|
+
type: String!
|
|
231
|
+
createdAt: DateTime
|
|
232
|
+
updatedAt: DateTime
|
|
233
|
+
team: Team
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
type IssueLabel {
|
|
237
|
+
id: ID!
|
|
238
|
+
name: String!
|
|
239
|
+
color: String
|
|
240
|
+
description: String
|
|
241
|
+
createdAt: DateTime
|
|
242
|
+
updatedAt: DateTime
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
type TeamCycleCadence { enabled: Boolean! weekday: Int timezone: String! policyVersion: Int! activeCycle: Cycle upcomingCycles: [Cycle!]! }
|
|
246
|
+
|
|
247
|
+
type Cycle {
|
|
248
|
+
id: ID!
|
|
249
|
+
name: String
|
|
250
|
+
description: String
|
|
251
|
+
number: Int
|
|
252
|
+
startsAt: DateTime
|
|
253
|
+
endsAt: DateTime
|
|
254
|
+
completedAt: DateTime
|
|
255
|
+
progress: Float
|
|
256
|
+
createdAt: DateTime
|
|
257
|
+
updatedAt: DateTime
|
|
258
|
+
team: Team
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
type Project {
|
|
262
|
+
id: ID!
|
|
263
|
+
name: String!
|
|
264
|
+
description: String
|
|
265
|
+
slugId: String
|
|
266
|
+
icon: String
|
|
267
|
+
color: String
|
|
268
|
+
state: String
|
|
269
|
+
progress: Float
|
|
270
|
+
startDate: TimelessDate
|
|
271
|
+
targetDate: TimelessDate
|
|
272
|
+
url: String
|
|
273
|
+
lead: User
|
|
274
|
+
creator: User
|
|
275
|
+
members(first: Int = 100): UserConnection
|
|
276
|
+
teams(first: Int = 100): TeamConnection
|
|
277
|
+
"Events on this project's issues (created, updated, comments, archived), newest first."
|
|
278
|
+
activity(first: Int = 50, after: String, eventTypes: [String!]): MyIssueActivityConnection
|
|
279
|
+
health: String
|
|
280
|
+
createdAt: DateTime
|
|
281
|
+
updatedAt: DateTime
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
input ProjectUpdateFilter { project: ProjectFilter }
|
|
285
|
+
|
|
286
|
+
type ProjectUpdate {
|
|
287
|
+
id: ID!
|
|
288
|
+
project: Project!
|
|
289
|
+
status: String
|
|
290
|
+
body: String!
|
|
291
|
+
createdAt: DateTime!
|
|
292
|
+
actor: User
|
|
293
|
+
progress: Float
|
|
294
|
+
health: String
|
|
295
|
+
}
|
|
296
|
+
type ProjectUpdatePayload { success: Boolean! projectUpdate: ProjectUpdate }
|
|
297
|
+
|
|
298
|
+
type Comment {
|
|
299
|
+
id: ID!
|
|
300
|
+
body: String!
|
|
301
|
+
url: String
|
|
302
|
+
user: User
|
|
303
|
+
createdAt: DateTime
|
|
304
|
+
updatedAt: DateTime
|
|
305
|
+
"When the author last changed the body; null if never edited."
|
|
306
|
+
editedAt: DateTime
|
|
307
|
+
"Whether the viewer may edit this comment (author only)."
|
|
308
|
+
canEdit: Boolean!
|
|
309
|
+
"Whether the viewer may delete this comment (author or workspace admin)."
|
|
310
|
+
canDelete: Boolean!
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
type Issue {
|
|
314
|
+
id: ID!
|
|
315
|
+
identifier: String!
|
|
316
|
+
title: String!
|
|
317
|
+
description: String
|
|
318
|
+
priority: Int
|
|
319
|
+
priorityLabel: String
|
|
320
|
+
estimate: Float
|
|
321
|
+
url: String
|
|
322
|
+
branchName: String
|
|
323
|
+
number: Int
|
|
324
|
+
createdAt: DateTime
|
|
325
|
+
updatedAt: DateTime
|
|
326
|
+
dueDate: DateTime
|
|
327
|
+
completedAt: DateTime
|
|
328
|
+
canceledAt: DateTime
|
|
329
|
+
startedAt: DateTime
|
|
330
|
+
stateChangedAt: DateTime
|
|
331
|
+
snoozedUntilAt: DateTime
|
|
332
|
+
state: WorkflowState
|
|
333
|
+
assignee: User
|
|
334
|
+
creator: User
|
|
335
|
+
team: Team
|
|
336
|
+
project: Project
|
|
337
|
+
cycle: Cycle
|
|
338
|
+
parent: Issue
|
|
339
|
+
children(first: Int = 50, after: String): IssueConnection
|
|
340
|
+
labels(first: Int = 50): IssueLabelConnection
|
|
341
|
+
subscribers(first: Int = 50): UserConnection
|
|
342
|
+
comments(first: Int = 50): CommentConnection
|
|
343
|
+
attachments(first: Int = 50): AttachmentConnection
|
|
344
|
+
activity(first: Int = 50, after: String): IssueActivityConnection
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
type UserConnection { nodes: [User!]! pageInfo: PageInfo! }
|
|
348
|
+
type TeamPayload { success: Boolean! team: Team }
|
|
349
|
+
type TeamConnection { nodes: [Team!]! pageInfo: PageInfo! }
|
|
350
|
+
type WorkflowStateConnection { nodes: [WorkflowState!]! pageInfo: PageInfo! }
|
|
351
|
+
type IssueConnection { nodes: [Issue!]! pageInfo: PageInfo! }
|
|
352
|
+
type IssueLabelConnection { nodes: [IssueLabel!]! pageInfo: PageInfo! }
|
|
353
|
+
type ProjectConnection { nodes: [Project!]! pageInfo: PageInfo! }
|
|
354
|
+
type InitiativeConnection { nodes: [Initiative!]! pageInfo: PageInfo! }
|
|
355
|
+
type Initiative { id: ID! name: String! summary: String status: String! priority: Int! owner: User creator: User targetDate: TimelessDate createdAt: DateTime! updatedAt: DateTime! projects: ProjectConnection! }
|
|
356
|
+
type InitiativePayload { success: Boolean! initiative: Initiative }
|
|
357
|
+
type ProjectUpdateConnection { nodes: [ProjectUpdate!]! pageInfo: PageInfo! }
|
|
358
|
+
type CycleConnection { nodes: [Cycle!]! pageInfo: PageInfo! }
|
|
359
|
+
type CommentConnection { nodes: [Comment!]! pageInfo: PageInfo! }
|
|
360
|
+
type IssueActivity { id: ID! eventType: String! actor: User changes: JSON createdAt: DateTime! }
|
|
361
|
+
type IssueActivityConnection { nodes: [IssueActivity!]! pageInfo: PageInfo! }
|
|
362
|
+
type MyIssueActivity { id: ID! issue: Issue! eventType: String! actor: User changes: JSON createdAt: DateTime! }
|
|
363
|
+
type MyIssueActivityConnection { nodes: [MyIssueActivity!]! pageInfo: PageInfo! }
|
|
364
|
+
|
|
365
|
+
type IssuePayload { success: Boolean! issue: Issue }
|
|
366
|
+
type CommentPayload { success: Boolean! comment: Comment }
|
|
367
|
+
type IssueLabelPayload { success: Boolean! issueLabel: IssueLabel }
|
|
368
|
+
type ProjectPayload { success: Boolean! project: Project }
|
|
369
|
+
type DeletePayload { success: Boolean! }
|
|
370
|
+
type PendingMembershipInvitation { userId: ID! name: String email: String expiresAt: DateTime }
|
|
371
|
+
type AttachmentPayload { success: Boolean! attachment: Attachment }
|
|
372
|
+
type AttachmentConnection { nodes: [Attachment!]! pageInfo: PageInfo! }
|
|
373
|
+
type Attachment {
|
|
374
|
+
id: ID!
|
|
375
|
+
url: String!
|
|
376
|
+
title: String!
|
|
377
|
+
subtitle: String
|
|
378
|
+
issue: Issue
|
|
379
|
+
createdAt: DateTime
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
type RateLimitPayload {
|
|
383
|
+
identifier: String
|
|
384
|
+
kind: String
|
|
385
|
+
limit: Int
|
|
386
|
+
remaining: Int
|
|
387
|
+
requestsPerHour: Int
|
|
388
|
+
resetsAt: DateTime
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
# ---- Filter inputs (comparators hoja emits) ----
|
|
392
|
+
input StringComparator {
|
|
393
|
+
eq: String
|
|
394
|
+
neq: String
|
|
395
|
+
in: [String!]
|
|
396
|
+
nin: [String!]
|
|
397
|
+
contains: String
|
|
398
|
+
containsIgnoreCase: String
|
|
399
|
+
eqIgnoreCase: String
|
|
400
|
+
}
|
|
401
|
+
input IDComparator { eq: ID in: [ID!] neq: ID }
|
|
402
|
+
input NumberComparator { eq: Float neq: Float gt: Float lt: Float gte: Float lte: Float in: [Float!] }
|
|
403
|
+
input DateComparator { eq: DateTime neq: DateTime gt: DateTime lt: DateTime gte: DateTime lte: DateTime }
|
|
404
|
+
input BooleanComparator { eq: Boolean neq: Boolean }
|
|
405
|
+
|
|
406
|
+
input TeamFilter { key: StringComparator id: IDComparator name: StringComparator }
|
|
407
|
+
input UserFilter { id: IDComparator email: StringComparator displayName: StringComparator name: StringComparator }
|
|
408
|
+
input WorkflowStateFilter { name: StringComparator type: StringComparator team: TeamFilter }
|
|
409
|
+
input CycleFilter { id: IDComparator name: StringComparator team: TeamFilter isActive: BooleanComparator isNext: BooleanComparator }
|
|
410
|
+
input ProjectRelFilter { id: IDComparator name: StringComparator }
|
|
411
|
+
input LabelRelFilter { name: StringComparator }
|
|
412
|
+
|
|
413
|
+
input IssueFilter {
|
|
414
|
+
identifier: StringComparator
|
|
415
|
+
team: TeamFilter
|
|
416
|
+
state: WorkflowStateFilter
|
|
417
|
+
assignee: UserFilter
|
|
418
|
+
creator: UserFilter
|
|
419
|
+
project: ProjectRelFilter
|
|
420
|
+
cycle: CycleFilter
|
|
421
|
+
parent: IssueParentFilter
|
|
422
|
+
labels: LabelRelFilter
|
|
423
|
+
priority: NumberComparator
|
|
424
|
+
number: NumberComparator
|
|
425
|
+
title: StringComparator
|
|
426
|
+
description: StringComparator
|
|
427
|
+
updatedAt: DateComparator
|
|
428
|
+
createdAt: DateComparator
|
|
429
|
+
dueDate: DateComparator
|
|
430
|
+
and: [IssueFilter!]
|
|
431
|
+
or: [IssueFilter!]
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
input ProjectFilter { id: IDComparator slugId: StringComparator name: StringComparator state: StringComparator team: TeamFilter }
|
|
435
|
+
input IssueParentFilter { id: IDComparator }
|
|
436
|
+
input IssueLabelFilter { team: TeamFilter name: StringComparator }
|
|
437
|
+
|
|
438
|
+
# ---- Mutation inputs ----
|
|
439
|
+
input TeamUpdateInput { name: String key: String description: String autoAddStarted: Boolean autoAddCompleted: Boolean autoAddBacklog: Boolean visibility: String parentTeamId: String }
|
|
440
|
+
input CycleUpdateInput { name: String description: String startsAt: DateTime endsAt: DateTime }
|
|
441
|
+
type CyclePayload { success: Boolean! cycle: Cycle }
|
|
442
|
+
input TeamCreateInput { name: String! key: String! description: String color: String icon: String visibility: String parentTeamId: String timezone: String memberIds: [String!] }
|
|
443
|
+
input IssueCreateInput {
|
|
444
|
+
title: String!
|
|
445
|
+
teamId: String!
|
|
446
|
+
description: String
|
|
447
|
+
priority: Int
|
|
448
|
+
stateId: String
|
|
449
|
+
assigneeId: String
|
|
450
|
+
projectId: String
|
|
451
|
+
cycleId: String
|
|
452
|
+
parentId: String
|
|
453
|
+
estimate: Float
|
|
454
|
+
dueDate: DateTime
|
|
455
|
+
labelIds: [String!]
|
|
456
|
+
}
|
|
457
|
+
input IssueUpdateInput {
|
|
458
|
+
title: String
|
|
459
|
+
description: String
|
|
460
|
+
priority: Int
|
|
461
|
+
stateId: String
|
|
462
|
+
assigneeId: String
|
|
463
|
+
projectId: String
|
|
464
|
+
cycleId: String
|
|
465
|
+
parentId: String
|
|
466
|
+
estimate: Float
|
|
467
|
+
dueDate: DateTime
|
|
468
|
+
labelIds: [String!]
|
|
469
|
+
}
|
|
470
|
+
input CommentCreateInput { issueId: String! body: String! }
|
|
471
|
+
input CommentUpdateInput { body: String }
|
|
472
|
+
input IssueLabelUpdateInput { name: String color: String description: String }
|
|
473
|
+
input IssueLabelCreateInput { name: String! teamId: String color: String description: String }
|
|
474
|
+
input ProjectCreateInput { name: String! description: String teamIds: [String!] leadId: String state: String color: String icon: String startDate: TimelessDate targetDate: TimelessDate }
|
|
475
|
+
input ProjectUpdateInput { name: String description: String leadId: String state: String color: String icon: String startDate: TimelessDate targetDate: TimelessDate teamIds: [String!] }
|
|
476
|
+
input ProjectUpdateCreateInput { projectId: String! body: String! status: String }
|
|
477
|
+
input AttachmentCreateInput { issueId: String! url: String! title: String! subtitle: String }
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Hoja GraphQL schema text, exposed to the agent so it can discover fields."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from hoja_server.agent_conversations import SDL as CONVERSATION_SDL
|
|
6
|
+
from hoja_server.model_providers import SDL as PROVIDER_SDL
|
|
7
|
+
|
|
8
|
+
SCHEMA_SDL = (Path(__file__).parent / "schema.graphql").read_text() + "\n" + PROVIDER_SDL + "\n" + CONVERSATION_SDL
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Encrypt provider credentials at rest with a server-held master key."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import hashlib
|
|
7
|
+
import os
|
|
8
|
+
import secrets
|
|
9
|
+
|
|
10
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
11
|
+
|
|
12
|
+
KEY_ENV = "HOJA_SECRET_KEY"
|
|
13
|
+
_NONCE_BYTES = 12
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SecretBoxError(RuntimeError):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _master_key() -> bytes:
|
|
21
|
+
raw = os.environ.get(KEY_ENV, "")
|
|
22
|
+
if not raw:
|
|
23
|
+
raise SecretBoxError(f"{KEY_ENV} must be set to store provider credentials.")
|
|
24
|
+
return hashlib.sha256(raw.encode()).digest()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def encrypt_secret(plaintext: str) -> str:
|
|
28
|
+
nonce = secrets.token_bytes(_NONCE_BYTES)
|
|
29
|
+
sealed = AESGCM(_master_key()).encrypt(nonce, plaintext.encode(), None)
|
|
30
|
+
return base64.b64encode(nonce + sealed).decode()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def decrypt_secret(token: str) -> str:
|
|
34
|
+
blob = base64.b64decode(token)
|
|
35
|
+
nonce, sealed = blob[:_NONCE_BYTES], blob[_NONCE_BYTES:]
|
|
36
|
+
try:
|
|
37
|
+
return AESGCM(_master_key()).decrypt(nonce, sealed, None).decode()
|
|
38
|
+
except Exception as exc:
|
|
39
|
+
raise SecretBoxError("Stored provider credential could not be decrypted.") from exc
|