@nekro-nxt/contracts 0.4.0 → 0.6.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/README.md +2 -0
- package/dist/index.mjs +3092 -164
- package/dist/index.mjs.map +1 -1
- package/lib/types/commands.d.ts +408 -0
- package/lib/types/commands.d.ts.map +1 -0
- package/lib/types/community-feedback-host.d.ts +442 -0
- package/lib/types/community-feedback-host.d.ts.map +1 -0
- package/lib/types/community-feedback.d.ts +1072 -0
- package/lib/types/community-feedback.d.ts.map +1 -0
- package/lib/types/community-personas.d.ts +4 -0
- package/lib/types/community-personas.d.ts.map +1 -1
- package/lib/types/community.d.ts +260 -13
- package/lib/types/community.d.ts.map +1 -1
- package/lib/types/config-schema.d.ts +16 -2
- package/lib/types/config-schema.d.ts.map +1 -1
- package/lib/types/credentials.d.ts +328 -0
- package/lib/types/credentials.d.ts.map +1 -0
- package/lib/types/domain.d.ts +44 -2
- package/lib/types/domain.d.ts.map +1 -1
- package/lib/types/extension-capabilities.d.ts +58 -3
- package/lib/types/extension-capabilities.d.ts.map +1 -1
- package/lib/types/extension-ui.d.ts +46 -4
- package/lib/types/extension-ui.d.ts.map +1 -1
- package/lib/types/host-api.d.ts +10805 -2887
- package/lib/types/host-api.d.ts.map +1 -1
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/linked-accounts.d.ts +100 -0
- package/lib/types/linked-accounts.d.ts.map +1 -0
- package/lib/types/logs.d.ts +355 -0
- package/lib/types/logs.d.ts.map +1 -0
- package/lib/types/response-timing.d.ts +60 -0
- package/lib/types/response-timing.d.ts.map +1 -0
- package/lib/types/runtime-compatibility.d.ts +2 -2
- package/package.json +1 -1
- package/src/commands.ts +366 -0
- package/src/community-feedback-host.ts +121 -0
- package/src/community-feedback.ts +289 -0
- package/src/community.ts +63 -1
- package/src/config-schema.ts +33 -2
- package/src/credentials.ts +260 -0
- package/src/domain.ts +74 -8
- package/src/extension-capabilities.ts +78 -4
- package/src/extension-ui.ts +3 -0
- package/src/host-api.ts +1873 -163
- package/src/index.ts +8 -0
- package/src/linked-accounts.ts +77 -0
- package/src/logs.ts +138 -0
- package/src/management-api.ts +1 -1
- package/src/response-timing.ts +77 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const COMMUNITY_FEEDBACK_STATUSES = [
|
|
4
|
+
'new',
|
|
5
|
+
'needs_info',
|
|
6
|
+
'triaged',
|
|
7
|
+
'planned',
|
|
8
|
+
'fixed',
|
|
9
|
+
'duplicate',
|
|
10
|
+
'wontfix',
|
|
11
|
+
'withdrawn',
|
|
12
|
+
] as const
|
|
13
|
+
export const CommunityFeedbackStatusSchema = z.enum(COMMUNITY_FEEDBACK_STATUSES)
|
|
14
|
+
export type CommunityFeedbackStatus = z.output<typeof CommunityFeedbackStatusSchema>
|
|
15
|
+
export const COMMUNITY_FEEDBACK_STATUS_LABELS: Readonly<Record<CommunityFeedbackStatus, string>> = {
|
|
16
|
+
new: '新反馈',
|
|
17
|
+
needs_info: '需要补充',
|
|
18
|
+
triaged: '已确认',
|
|
19
|
+
planned: '已计划',
|
|
20
|
+
fixed: '已修复',
|
|
21
|
+
duplicate: '重复',
|
|
22
|
+
wontfix: '不处理',
|
|
23
|
+
withdrawn: '已撤回',
|
|
24
|
+
}
|
|
25
|
+
export const CommunityFeedbackKindSchema = z.enum(['bug', 'suggestion', 'question'])
|
|
26
|
+
export const CommunityFeedbackIdSchema = z
|
|
27
|
+
.string()
|
|
28
|
+
.regex(/^fbk_[0-9a-z]{26}$/u)
|
|
29
|
+
.brand<'CommunityFeedbackId'>()
|
|
30
|
+
export const CommunityFeedbackRefSchema = z.string().regex(/^FB-[1-9][0-9]*$/u)
|
|
31
|
+
export const CommunityFeedbackTargetSchema = z.union([
|
|
32
|
+
z.literal('nxt'),
|
|
33
|
+
z.object({ extensionId: z.string().regex(/^ext_[0-9A-Za-z]+$/u), version: z.string().max(120).optional() }).strict(),
|
|
34
|
+
])
|
|
35
|
+
const version = z.string().trim().min(1).max(120)
|
|
36
|
+
const timestamp = z.number().int().nonnegative()
|
|
37
|
+
|
|
38
|
+
export const CommunityFeedbackExtensionDiagnosticSchema = z
|
|
39
|
+
.object({
|
|
40
|
+
extensionId: z.string().max(100),
|
|
41
|
+
version,
|
|
42
|
+
accessLevel: z.string().max(80).optional(),
|
|
43
|
+
activation: z.enum(['host', 'agent']).optional(),
|
|
44
|
+
})
|
|
45
|
+
.strict()
|
|
46
|
+
export const CommunityFeedbackDiagnosticEntrySchema = z
|
|
47
|
+
.object({
|
|
48
|
+
kind: z.enum(['compatibility', 'extension', 'connection']),
|
|
49
|
+
summary: z.string().max(2000),
|
|
50
|
+
extensionId: z.string().max(100).optional(),
|
|
51
|
+
})
|
|
52
|
+
.strict()
|
|
53
|
+
|
|
54
|
+
/** An explicit allowlist: credentials, account grants and arbitrary host configuration never cross this boundary. */
|
|
55
|
+
export const CommunityFeedbackDiagnosticsSchema = z
|
|
56
|
+
.object({
|
|
57
|
+
nxtVersion: version.optional(),
|
|
58
|
+
releaseChannel: z.enum(['stable', 'preview', 'development']).optional(),
|
|
59
|
+
hostKind: z.enum(['desktop', 'server']).optional(),
|
|
60
|
+
platform: z.string().max(80).optional(),
|
|
61
|
+
arch: z.string().max(40).optional(),
|
|
62
|
+
dshVersion: version.optional(),
|
|
63
|
+
adapters: z.array(z.string().max(100)).max(64).optional(),
|
|
64
|
+
agent: z
|
|
65
|
+
.object({
|
|
66
|
+
accessLevel: z.string().max(80),
|
|
67
|
+
subagents: z.boolean(),
|
|
68
|
+
fileTools: z.boolean(),
|
|
69
|
+
webSearch: z.boolean(),
|
|
70
|
+
dynamicCreation: z.boolean(),
|
|
71
|
+
developmentShell: z.boolean(),
|
|
72
|
+
unrestrictedFileAccess: z.boolean(),
|
|
73
|
+
scheduledTasks: z.boolean(),
|
|
74
|
+
codeRun: z.boolean(),
|
|
75
|
+
commandNetwork: z.boolean(),
|
|
76
|
+
communityFeedbackAuto: z.boolean(),
|
|
77
|
+
})
|
|
78
|
+
.strict()
|
|
79
|
+
.optional(),
|
|
80
|
+
extensions: z.array(CommunityFeedbackExtensionDiagnosticSchema).max(256).optional(),
|
|
81
|
+
entries: z.array(CommunityFeedbackDiagnosticEntrySchema).max(32).optional(),
|
|
82
|
+
instanceId: z.string().min(1).max(100).optional(),
|
|
83
|
+
})
|
|
84
|
+
.strict()
|
|
85
|
+
export type CommunityFeedbackDiagnostics = z.output<typeof CommunityFeedbackDiagnosticsSchema>
|
|
86
|
+
|
|
87
|
+
export const CommunityFeedbackExcerptSchema = z
|
|
88
|
+
.array(
|
|
89
|
+
z
|
|
90
|
+
.object({
|
|
91
|
+
speaker: z.string().max(80),
|
|
92
|
+
text: z.string().max(6000),
|
|
93
|
+
})
|
|
94
|
+
.strict(),
|
|
95
|
+
)
|
|
96
|
+
.max(8)
|
|
97
|
+
export type CommunityFeedbackExcerpt = z.output<typeof CommunityFeedbackExcerptSchema>
|
|
98
|
+
|
|
99
|
+
export const CommunityFeedbackCreateSchema = z
|
|
100
|
+
.object({
|
|
101
|
+
clientRequestId: z.string().min(1).max(160),
|
|
102
|
+
kind: CommunityFeedbackKindSchema,
|
|
103
|
+
target: CommunityFeedbackTargetSchema,
|
|
104
|
+
title: z.string().trim().min(1).max(120),
|
|
105
|
+
body: z.string().trim().min(1).max(8000),
|
|
106
|
+
diagnostics: CommunityFeedbackDiagnosticsSchema.default({}),
|
|
107
|
+
excerpt: CommunityFeedbackExcerptSchema.optional(),
|
|
108
|
+
excerptSharedWithAuthor: z.boolean().default(false),
|
|
109
|
+
source: z.enum(['assistant', 'assistant_auto', 'form', 'web']),
|
|
110
|
+
instanceId: z.string().min(1).max(100).optional(),
|
|
111
|
+
})
|
|
112
|
+
.strict()
|
|
113
|
+
export type CommunityFeedbackCreate = z.output<typeof CommunityFeedbackCreateSchema>
|
|
114
|
+
export const CommunityFeedbackMessageInputSchema = z
|
|
115
|
+
.object({
|
|
116
|
+
body: z.string().trim().min(1).max(4000),
|
|
117
|
+
audience: z.enum(['everyone', 'staff']).default('everyone'),
|
|
118
|
+
})
|
|
119
|
+
.strict()
|
|
120
|
+
export const CommunityFeedbackSupplementSchema = z
|
|
121
|
+
.object({
|
|
122
|
+
diagnostics: CommunityFeedbackDiagnosticsSchema.optional(),
|
|
123
|
+
excerpt: CommunityFeedbackExcerptSchema.optional(),
|
|
124
|
+
excerptSharedWithAuthor: z.boolean().optional(),
|
|
125
|
+
})
|
|
126
|
+
.strict()
|
|
127
|
+
export const CommunityFeedbackWithdrawSchema = z.object({ deleteText: z.boolean().default(false) }).strict()
|
|
128
|
+
export const CommunityFeedbackConfirmSchema = z.object({ clientVersion: version }).strict()
|
|
129
|
+
export const CommunityFeedbackReopenSchema = z.object({ note: z.string().trim().min(1).max(2000) }).strict()
|
|
130
|
+
export const CommunityFeedbackAffectedSchema = z
|
|
131
|
+
.object({
|
|
132
|
+
diagnostics: CommunityFeedbackDiagnosticsSchema.optional(),
|
|
133
|
+
nxtVersion: version.optional(),
|
|
134
|
+
})
|
|
135
|
+
.strict()
|
|
136
|
+
export const CommunityFeedbackTransitionSchema = z
|
|
137
|
+
.object({
|
|
138
|
+
status: CommunityFeedbackStatusSchema,
|
|
139
|
+
fixedIn: version.optional(),
|
|
140
|
+
duplicateOf: CommunityFeedbackRefSchema.optional(),
|
|
141
|
+
note: z.string().trim().max(2000).optional(),
|
|
142
|
+
})
|
|
143
|
+
.strict()
|
|
144
|
+
export type CommunityFeedbackTransition = z.output<typeof CommunityFeedbackTransitionSchema>
|
|
145
|
+
export const CommunityFeedbackLabelsSchema = z
|
|
146
|
+
.object({
|
|
147
|
+
labels: z.array(z.string().trim().min(1).max(40)).max(20),
|
|
148
|
+
priority: z.enum(['low', 'normal', 'high']).nullable(),
|
|
149
|
+
})
|
|
150
|
+
.strict()
|
|
151
|
+
export const CommunityFeedbackLinkSchema = z
|
|
152
|
+
.object({
|
|
153
|
+
githubUrl: z
|
|
154
|
+
.string()
|
|
155
|
+
.url()
|
|
156
|
+
.max(1000)
|
|
157
|
+
.refine((value) => /^https:\/\/github\.com\/[^/]+\/[^/]+\/issues\/[1-9][0-9]*$/u.test(value))
|
|
158
|
+
.nullable(),
|
|
159
|
+
})
|
|
160
|
+
.strict()
|
|
161
|
+
export const CommunityFeedbackVisibilitySchema = z.object({ visibility: z.enum(['private', 'public']) }).strict()
|
|
162
|
+
|
|
163
|
+
export const CommunityFeedbackEventDataSchema = z
|
|
164
|
+
.object({
|
|
165
|
+
from: CommunityFeedbackStatusSchema.optional(),
|
|
166
|
+
to: CommunityFeedbackStatusSchema.optional(),
|
|
167
|
+
fixedIn: version.nullable().optional(),
|
|
168
|
+
duplicateOf: CommunityFeedbackRefSchema.nullable().optional(),
|
|
169
|
+
note: z.string().max(2000).optional(),
|
|
170
|
+
labels: z.array(z.string()).optional(),
|
|
171
|
+
priority: z.enum(['low', 'normal', 'high']).nullable().optional(),
|
|
172
|
+
visibility: z.enum(['private', 'public']).optional(),
|
|
173
|
+
clientVersion: version.optional(),
|
|
174
|
+
messageId: z.string().optional(),
|
|
175
|
+
audience: z.enum(['everyone', 'staff']).optional(),
|
|
176
|
+
deleteText: z.boolean().optional(),
|
|
177
|
+
affected: z.boolean().optional(),
|
|
178
|
+
})
|
|
179
|
+
.strict()
|
|
180
|
+
export const CommunityFeedbackEventSchema = z.object({
|
|
181
|
+
seq: z.number().int().positive(),
|
|
182
|
+
ref: CommunityFeedbackRefSchema,
|
|
183
|
+
type: z.enum([
|
|
184
|
+
'created',
|
|
185
|
+
'message',
|
|
186
|
+
'status_changed',
|
|
187
|
+
'labeled',
|
|
188
|
+
'priority_changed',
|
|
189
|
+
'linked_issue',
|
|
190
|
+
'duplicate_marked',
|
|
191
|
+
'fixed_in_set',
|
|
192
|
+
'visibility_changed',
|
|
193
|
+
'affected_added',
|
|
194
|
+
'withdrawn',
|
|
195
|
+
'reopened',
|
|
196
|
+
'supplemented',
|
|
197
|
+
'fix_confirmed',
|
|
198
|
+
]),
|
|
199
|
+
actorKind: z.enum(['user', 'admin', 'author', 'token', 'system']).optional(),
|
|
200
|
+
actor: z.string().optional(),
|
|
201
|
+
tokenId: z.string().optional(),
|
|
202
|
+
data: CommunityFeedbackEventDataSchema,
|
|
203
|
+
createdAt: timestamp,
|
|
204
|
+
title: z.string().optional(),
|
|
205
|
+
status: CommunityFeedbackStatusSchema.optional(),
|
|
206
|
+
})
|
|
207
|
+
export type CommunityFeedbackEvent = z.output<typeof CommunityFeedbackEventSchema>
|
|
208
|
+
export const CommunityFeedbackMessageSchema = z.object({
|
|
209
|
+
id: z.string(),
|
|
210
|
+
authorKind: z.enum(['user', 'admin', 'author', 'system']),
|
|
211
|
+
author: z.string().nullable(),
|
|
212
|
+
audience: z.enum(['everyone', 'staff']),
|
|
213
|
+
body: z.string(),
|
|
214
|
+
createdAt: timestamp,
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
export const CommunityFeedbackSummarySchema = z.object({
|
|
218
|
+
id: CommunityFeedbackIdSchema,
|
|
219
|
+
ref: CommunityFeedbackRefSchema,
|
|
220
|
+
title: z.string(),
|
|
221
|
+
kind: CommunityFeedbackKindSchema.optional(),
|
|
222
|
+
target: CommunityFeedbackTargetSchema.optional(),
|
|
223
|
+
status: CommunityFeedbackStatusSchema,
|
|
224
|
+
fixedIn: version.nullable(),
|
|
225
|
+
affectedCount: z.number().int().nonnegative(),
|
|
226
|
+
source: CommunityFeedbackCreateSchema.shape.source.optional(),
|
|
227
|
+
confirmedCount: z.number().int().nonnegative().optional(),
|
|
228
|
+
createdAt: timestamp.optional(),
|
|
229
|
+
updatedAt: timestamp.optional(),
|
|
230
|
+
})
|
|
231
|
+
export type CommunityFeedbackSummary = z.output<typeof CommunityFeedbackSummarySchema>
|
|
232
|
+
|
|
233
|
+
/** Optional private fields are emitted only by the server's role-specific projection. */
|
|
234
|
+
export const CommunityFeedbackSchema = CommunityFeedbackSummarySchema.extend({
|
|
235
|
+
access: z.enum(['owner', 'admin', 'author', 'subscriber', 'public']),
|
|
236
|
+
body: z.string().optional(),
|
|
237
|
+
diagnostics: CommunityFeedbackDiagnosticsSchema.optional(),
|
|
238
|
+
excerpt: CommunityFeedbackExcerptSchema.nullable().optional(),
|
|
239
|
+
excerptSharedWithAuthor: z.boolean().optional(),
|
|
240
|
+
messages: z.array(CommunityFeedbackMessageSchema).optional(),
|
|
241
|
+
events: z.array(CommunityFeedbackEventSchema).optional(),
|
|
242
|
+
submitter: z.string().optional(),
|
|
243
|
+
instanceId: z.string().nullable().optional(),
|
|
244
|
+
priority: z.enum(['low', 'normal', 'high']).nullable().optional(),
|
|
245
|
+
labels: z.array(z.string()).optional(),
|
|
246
|
+
visibility: z.enum(['private', 'public']).optional(),
|
|
247
|
+
duplicateOf: CommunityFeedbackRefSchema.nullable().optional(),
|
|
248
|
+
githubUrl: z.string().nullable().optional(),
|
|
249
|
+
closedAt: timestamp.nullable().optional(),
|
|
250
|
+
purgedAt: timestamp.nullable().optional(),
|
|
251
|
+
confirmed: z.boolean().default(false),
|
|
252
|
+
affected: z.boolean().default(false),
|
|
253
|
+
affectedDiagnostics: z
|
|
254
|
+
.array(
|
|
255
|
+
z.object({
|
|
256
|
+
user: z.string(),
|
|
257
|
+
diagnostics: CommunityFeedbackDiagnosticsSchema.nullable(),
|
|
258
|
+
nxtVersion: version.nullable(),
|
|
259
|
+
createdAt: timestamp,
|
|
260
|
+
}),
|
|
261
|
+
)
|
|
262
|
+
.optional(),
|
|
263
|
+
})
|
|
264
|
+
export type CommunityFeedback = z.output<typeof CommunityFeedbackSchema>
|
|
265
|
+
export const CommunityFeedbackListSchema = z.object({
|
|
266
|
+
items: z.array(CommunityFeedbackSummarySchema),
|
|
267
|
+
cursor: z.string().nullable(),
|
|
268
|
+
})
|
|
269
|
+
export const CommunityFeedbackChangesSchema = z.object({
|
|
270
|
+
events: z.array(CommunityFeedbackEventSchema),
|
|
271
|
+
cursor: z.number().int().nonnegative(),
|
|
272
|
+
hasMore: z.boolean(),
|
|
273
|
+
})
|
|
274
|
+
export type CommunityFeedbackChanges = z.output<typeof CommunityFeedbackChangesSchema>
|
|
275
|
+
export const CommunityFeedbackDigestSchema = z.object({
|
|
276
|
+
since: timestamp,
|
|
277
|
+
counts: z.object({
|
|
278
|
+
status: z.record(CommunityFeedbackStatusSchema, z.number().int().nonnegative()),
|
|
279
|
+
kind: z.record(CommunityFeedbackKindSchema, z.number().int().nonnegative()),
|
|
280
|
+
target: z.record(z.enum(['nxt', 'extension']), z.number().int().nonnegative()),
|
|
281
|
+
extensions: z.record(z.string(), z.number().int().nonnegative()),
|
|
282
|
+
}),
|
|
283
|
+
newItems: z.array(CommunityFeedbackSummarySchema),
|
|
284
|
+
awaitingReply: z.array(CommunityFeedbackSummarySchema),
|
|
285
|
+
mostAffected: z.array(CommunityFeedbackSummarySchema),
|
|
286
|
+
unconfirmedFixes: z.array(CommunityFeedbackSummarySchema),
|
|
287
|
+
fixedCount: z.number().int().nonnegative(),
|
|
288
|
+
})
|
|
289
|
+
export type CommunityFeedbackDigest = z.output<typeof CommunityFeedbackDigestSchema>
|
package/src/community.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
-
import { ExtensionIdSchema } from './domain.js'
|
|
2
|
+
import { ExtensionIdSchema, ExtensionRevisionIdSchema } from './domain.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* NekroNXT 社区在本机的投影。Host 代理社区开放接口并把结果归一到这些结构;社区新增的字段在 Host 处丢弃,
|
|
@@ -90,6 +90,8 @@ export const CommunityExtensionSortSchema = z.enum(['updated', 'score', 'downloa
|
|
|
90
90
|
export const CommunityReleaseIdSchema = z.string().regex(/^rel_[0-9A-Za-z]+$/u)
|
|
91
91
|
|
|
92
92
|
export const CommunityAccountSchema = z.object({
|
|
93
|
+
/** Immutable community identity; absent when connected to a community predating feedback support. */
|
|
94
|
+
id: z.string().min(1).max(100).optional(),
|
|
93
95
|
handle: z.string().min(1).max(80),
|
|
94
96
|
displayName: z.string().max(200),
|
|
95
97
|
avatarUrl: z.string().url().nullable(),
|
|
@@ -100,6 +102,7 @@ export const CommunityEnvironmentSchema = z.enum(['production', 'staging', 'deve
|
|
|
100
102
|
export type CommunityEnvironment = z.output<typeof CommunityEnvironmentSchema>
|
|
101
103
|
|
|
102
104
|
export const CommunityStatusSchema = z.object({
|
|
105
|
+
features: z.array(z.string()).optional(),
|
|
103
106
|
communityUrl: z.string().url(),
|
|
104
107
|
/** 社区自我声明的环境;连不上或旧版社区时为 null。 */
|
|
105
108
|
environment: CommunityEnvironmentSchema.nullable(),
|
|
@@ -131,6 +134,12 @@ export const CommunityReleaseSchema = z.object({
|
|
|
131
134
|
requiresSdk: z.number().int().positive().nullable(),
|
|
132
135
|
notes: z.string().max(2000),
|
|
133
136
|
createdAt: z.number().int(),
|
|
137
|
+
/** Release number on the community line; absent from older communities. */
|
|
138
|
+
number: z.number().int().positive().optional(),
|
|
139
|
+
/** Revision the release contains and its lineage, so NekroNXT can tell fast-forward from divergence. */
|
|
140
|
+
revisionId: ExtensionRevisionIdSchema.optional(),
|
|
141
|
+
parentRevisionId: ExtensionRevisionIdSchema.nullable().optional(),
|
|
142
|
+
ancestry: z.array(ExtensionRevisionIdSchema).max(256).optional(),
|
|
134
143
|
})
|
|
135
144
|
export type CommunityRelease = z.output<typeof CommunityReleaseSchema>
|
|
136
145
|
|
|
@@ -157,6 +166,20 @@ export const CommunityExtensionSummarySchema = z.object({
|
|
|
157
166
|
downloads: z.number().int().nonnegative(),
|
|
158
167
|
updatedAt: z.number().int(),
|
|
159
168
|
pageUrl: z.string().url(),
|
|
169
|
+
/**
|
|
170
|
+
* The extension and version this one is a branch of; null for an original line or an older community. Without a
|
|
171
|
+
* displayName the origin is not public on the community.
|
|
172
|
+
*/
|
|
173
|
+
branchOf: z
|
|
174
|
+
.object({
|
|
175
|
+
extensionId: ExtensionIdSchema,
|
|
176
|
+
revisionId: ExtensionRevisionIdSchema.nullable(),
|
|
177
|
+
displayName: z.string().max(200).nullable(),
|
|
178
|
+
})
|
|
179
|
+
.nullable()
|
|
180
|
+
.optional(),
|
|
181
|
+
/** Public branches made from this extension. */
|
|
182
|
+
branchCount: z.number().int().nonnegative().optional(),
|
|
160
183
|
})
|
|
161
184
|
export type CommunityExtensionSummary = z.output<typeof CommunityExtensionSummarySchema>
|
|
162
185
|
|
|
@@ -180,6 +203,13 @@ export const CommunityExtensionDetailSchema = CommunityExtensionSummarySchema.ex
|
|
|
180
203
|
})
|
|
181
204
|
export type CommunityExtensionDetail = z.output<typeof CommunityExtensionDetailSchema>
|
|
182
205
|
|
|
206
|
+
/**
|
|
207
|
+
* 本机缓存的社区目录读取附带的时间:`fetchedAt` 是这份内容从社区取回的时间;`stale` 表示社区这次没能读取,
|
|
208
|
+
* 返回的是较早的一份。
|
|
209
|
+
*/
|
|
210
|
+
export const CommunityFreshnessSchema = z.object({ fetchedAt: z.number().int().nonnegative(), stale: z.boolean() })
|
|
211
|
+
export type CommunityFreshness = z.output<typeof CommunityFreshnessSchema>
|
|
212
|
+
|
|
183
213
|
/**
|
|
184
214
|
* 发布时随包提交的条目信息(作者自己写的介绍)。首次发布用于建条目;之后提供了哪项就覆盖哪项,未提供的保持不变。
|
|
185
215
|
*/
|
|
@@ -220,6 +250,38 @@ export const CommunitySourceSchema = z.object({
|
|
|
220
250
|
})
|
|
221
251
|
export type CommunitySource = z.output<typeof CommunitySourceSchema>
|
|
222
252
|
|
|
253
|
+
/**
|
|
254
|
+
* How this machine's current version of one extension relates to the community's newest public release, with the
|
|
255
|
+
* action that relation calls for. `role` says whether this machine may publish the line or only follows it.
|
|
256
|
+
*/
|
|
257
|
+
export const CommunitySyncItemSchema = z.object({
|
|
258
|
+
extensionId: ExtensionIdSchema,
|
|
259
|
+
displayName: z.string(),
|
|
260
|
+
communityUrl: z.string().url(),
|
|
261
|
+
role: z.enum(['owner', 'subscriber']),
|
|
262
|
+
currentRevisionId: ExtensionRevisionIdSchema,
|
|
263
|
+
currentVersionLabel: z.string(),
|
|
264
|
+
/** Release the current version is, when it is one. */
|
|
265
|
+
currentRelease: z.object({ id: CommunityReleaseIdSchema, number: z.number().int().positive().nullable() }).nullable(),
|
|
266
|
+
/** `remote-ahead`: update; `local-ahead`: publish (owner) or local changes (subscriber); `diverged`: merge first. */
|
|
267
|
+
status: z.enum(['same', 'remote-ahead', 'local-ahead', 'diverged', 'unrelated', 'unknown']),
|
|
268
|
+
found: z.boolean(),
|
|
269
|
+
delisted: z.boolean(),
|
|
270
|
+
delistedReason: z.string().nullable(),
|
|
271
|
+
/** The release the current version came from was withdrawn by its author. */
|
|
272
|
+
releaseWithdrawn: z.boolean(),
|
|
273
|
+
latest: CommunityReleaseSchema.nullable(),
|
|
274
|
+
/** The user chose to keep local changes over `latest`; it is not offered again. */
|
|
275
|
+
dismissed: z.boolean(),
|
|
276
|
+
addedPermissions: z.array(CommunityPermissionItemSchema),
|
|
277
|
+
})
|
|
278
|
+
export type CommunitySyncItem = z.output<typeof CommunitySyncItemSchema>
|
|
279
|
+
|
|
280
|
+
export const CommunitySyncSchema = z.object({
|
|
281
|
+
checkedAt: z.number().int().nullable(),
|
|
282
|
+
items: z.array(CommunitySyncItemSchema),
|
|
283
|
+
})
|
|
284
|
+
|
|
223
285
|
export const CommunityInstalledItemSchema = z.object({
|
|
224
286
|
extensionId: ExtensionIdSchema,
|
|
225
287
|
displayName: z.string(),
|
package/src/config-schema.ts
CHANGED
|
@@ -6,8 +6,9 @@ import { JsonValueSchema, type JsonValue } from './domain.js'
|
|
|
6
6
|
* product form can render and the Host can validate without executing Schema callbacks.
|
|
7
7
|
*
|
|
8
8
|
* Supported nodes: object (dict), string, number, natural, percent, boolean, const, union of consts (enum) and
|
|
9
|
-
* array of primitives. `meta.role: 'secret'` marks a write-only credential. NekroNXT reads
|
|
10
|
-
* `hint` (help text), `advanced` (collapsed by default)
|
|
9
|
+
* array of primitives. `meta.role: 'secret'` marks a write-only credential. NekroNXT reads four extra meta keys:
|
|
10
|
+
* `hint` (help text), `advanced` (collapsed by default), `visibleWhen` (show only when a sibling equals a value) and
|
|
11
|
+
* `agentWritable: false` (only an administrator may change the field, in the console).
|
|
11
12
|
*/
|
|
12
13
|
export const CONFIG_SCHEMA_MAX_BYTES = 64 * 1024
|
|
13
14
|
|
|
@@ -27,6 +28,8 @@ export const ConfigSchemaMetaSchema = z
|
|
|
27
28
|
pattern: z.object({ source: z.string().max(500), flags: z.string().max(8).optional() }).optional(),
|
|
28
29
|
advanced: z.boolean().optional(),
|
|
29
30
|
group: z.string().max(40).optional(),
|
|
31
|
+
/** `false` keeps the field out of the agent's reach: only an administrator changes it, in the console. */
|
|
32
|
+
agentWritable: z.boolean().optional(),
|
|
30
33
|
visibleWhen: z
|
|
31
34
|
.object({ key: z.string().min(1).max(64), equals: JsonValueSchema })
|
|
32
35
|
.strict()
|
|
@@ -105,9 +108,27 @@ export interface ConfigField {
|
|
|
105
108
|
readonly advanced: boolean
|
|
106
109
|
readonly group?: string
|
|
107
110
|
readonly options?: readonly { readonly value: JsonValue; readonly label: string }[]
|
|
111
|
+
/** Declared `meta.agentWritable: false`: only an administrator changes it. */
|
|
112
|
+
readonly adminOnly: boolean
|
|
108
113
|
readonly node: ConfigSchemaNode
|
|
109
114
|
}
|
|
110
115
|
|
|
116
|
+
/**
|
|
117
|
+
* How much a change to one field matters when an agent makes it: `admin` fields are changed in the console only,
|
|
118
|
+
* `secret` fields hold credentials, `network` fields decide where the extension may connect, the rest are `normal`.
|
|
119
|
+
*/
|
|
120
|
+
export type ConfigFieldLevel = 'normal' | 'network' | 'secret' | 'admin'
|
|
121
|
+
|
|
122
|
+
export const configFieldLevel = (
|
|
123
|
+
field: Pick<ConfigField, 'key' | 'kind' | 'adminOnly'>,
|
|
124
|
+
networkFields: ReadonlySet<string> | readonly string[],
|
|
125
|
+
): ConfigFieldLevel => {
|
|
126
|
+
if (field.adminOnly) return 'admin'
|
|
127
|
+
if (field.kind === 'secret') return 'secret'
|
|
128
|
+
const network = networkFields instanceof Set ? networkFields : new Set(networkFields)
|
|
129
|
+
return network.has(field.key) ? 'network' : 'normal'
|
|
130
|
+
}
|
|
131
|
+
|
|
111
132
|
export const configDescription = (meta: ConfigSchemaMeta | undefined): string | undefined => {
|
|
112
133
|
const description = meta?.description
|
|
113
134
|
if (typeof description === 'string') return description
|
|
@@ -155,10 +176,17 @@ export const configFields = (document: ConfigSchemaDocument): readonly ConfigFie
|
|
|
155
176
|
advanced: node.meta?.advanced === true,
|
|
156
177
|
...(node.meta?.group === undefined ? {} : { group: node.meta.group }),
|
|
157
178
|
...(options === undefined ? {} : { options }),
|
|
179
|
+
adminOnly: node.meta?.agentWritable === false,
|
|
158
180
|
node,
|
|
159
181
|
}
|
|
160
182
|
})
|
|
161
183
|
|
|
184
|
+
/** Fields declared `meta.agentWritable: false`. */
|
|
185
|
+
export const adminOnlyConfigKeys = (document: ConfigSchemaDocument): readonly string[] =>
|
|
186
|
+
configFields(document)
|
|
187
|
+
.filter((field) => field.adminOnly)
|
|
188
|
+
.map((field) => field.key)
|
|
189
|
+
|
|
162
190
|
export const configSecretKeys = (document: ConfigSchemaDocument): readonly string[] =>
|
|
163
191
|
configFields(document)
|
|
164
192
|
.filter((field) => field.kind === 'secret')
|
|
@@ -302,6 +330,8 @@ interface FieldOptions<Value extends JsonValue> {
|
|
|
302
330
|
readonly required?: boolean
|
|
303
331
|
readonly advanced?: boolean
|
|
304
332
|
readonly group?: string
|
|
333
|
+
/** `false` for billing or moderation switches only an administrator should change. */
|
|
334
|
+
readonly agentWritable?: boolean
|
|
305
335
|
}
|
|
306
336
|
|
|
307
337
|
const fieldMeta = <Value extends JsonValue>(title: string, options: FieldOptions<Value> = {}): ConfigSchemaMeta => ({
|
|
@@ -311,6 +341,7 @@ const fieldMeta = <Value extends JsonValue>(title: string, options: FieldOptions
|
|
|
311
341
|
...(options.required === undefined ? {} : { required: options.required }),
|
|
312
342
|
...(options.advanced === undefined ? {} : { advanced: options.advanced }),
|
|
313
343
|
...(options.group === undefined ? {} : { group: options.group }),
|
|
344
|
+
...(options.agentWritable === undefined ? {} : { agentWritable: options.agentWritable }),
|
|
314
345
|
})
|
|
315
346
|
|
|
316
347
|
/** Builds serialized Schemastery documents without depending on the Schemastery runtime. */
|