@nekro-nxt/contracts 0.1.0 → 0.2.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/src/community.ts CHANGED
@@ -32,11 +32,18 @@ export const CommunityAccountSchema = z
32
32
  .strict()
33
33
  export type CommunityAccount = z.output<typeof CommunityAccountSchema>
34
34
 
35
+ export const CommunityEnvironmentSchema = z.enum(['production', 'staging', 'development'])
36
+ export type CommunityEnvironment = z.output<typeof CommunityEnvironmentSchema>
37
+
35
38
  export const CommunityStatusSchema = z
36
39
  .object({
37
40
  communityUrl: z.string().url(),
41
+ /** 社区自我声明的环境;连不上或旧版社区时为 null。 */
42
+ environment: CommunityEnvironmentSchema.nullable(),
38
43
  account: CommunityAccountSchema.nullable(),
39
44
  signedInAt: z.number().int().nullable(),
45
+ /** 最近一次更新检查发现的可更新扩展数量(只读缓存,不发起网络请求)。 */
46
+ updatesAvailable: z.number().int().nonnegative(),
40
47
  })
41
48
  .strict()
42
49
  export type CommunityStatus = z.output<typeof CommunityStatusSchema>
@@ -50,6 +57,8 @@ export const CommunityPermissionItemSchema = z
50
57
  })
51
58
  .strict()
52
59
 
60
+ export type CommunityPermissionItem = z.output<typeof CommunityPermissionItemSchema>
61
+
53
62
  export const CommunityReleaseSchema = z
54
63
  .object({
55
64
  id: CommunityReleaseIdSchema,
@@ -64,6 +73,13 @@ export const CommunityReleaseSchema = z
64
73
  .strict()
65
74
  export type CommunityRelease = z.output<typeof CommunityReleaseSchema>
66
75
 
76
+ /** 社区为官方扩展保留的发布者标识:官方扩展由多位官方成员维护,对外不显示个人账号。 */
77
+ export const COMMUNITY_OFFICIAL_HANDLE = 'nekro-nxt'
78
+
79
+ /** 发布者的展示文字:官方扩展显示「NekroNXT 官方」,其余显示 @账号。 */
80
+ export const communityPublisherLabel = (handle: string): string =>
81
+ handle === COMMUNITY_OFFICIAL_HANDLE ? 'NekroNXT 官方' : `@${handle}`
82
+
67
83
  export const CommunityExtensionSummarySchema = z
68
84
  .object({
69
85
  id: ExtensionIdSchema,
@@ -71,6 +87,8 @@ export const CommunityExtensionSummarySchema = z
71
87
  displayName: z.string().max(200),
72
88
  summary: z.string().max(1000),
73
89
  tags: z.array(z.string().max(40)).max(16),
90
+ /** 由社区后台认定的官方扩展;此时发布者是社区保留的官方主页(`COMMUNITY_OFFICIAL_HANDLE`)。 */
91
+ official: z.boolean(),
74
92
  publisher: CommunityAccountSchema,
75
93
  latest: CommunityReleaseSchema.nullable(),
76
94
  downloads: z.number().int().nonnegative(),
@@ -95,6 +113,136 @@ export const CommunityExtensionDetailSchema = CommunityExtensionSummarySchema.ex
95
113
  }).strict()
96
114
  export type CommunityExtensionDetail = z.output<typeof CommunityExtensionDetailSchema>
97
115
 
116
+ export const CommunityEndpointSchema = z
117
+ .object({
118
+ url: z.string().url(),
119
+ source: z.enum(['setting', 'environment', 'default']),
120
+ defaultUrl: z.string().url(),
121
+ environmentUrl: z.string().url().nullable(),
122
+ /** 当前地址是未加密的非本机地址。 */
123
+ insecure: z.boolean(),
124
+ })
125
+ .strict()
126
+ export type CommunityEndpoint = z.output<typeof CommunityEndpointSchema>
127
+
128
+ export const CommunitySourceSchema = z
129
+ .object({
130
+ kind: z.literal('community'),
131
+ communityUrl: z.string().url(),
132
+ releaseId: CommunityReleaseIdSchema,
133
+ publisherHandle: z.string().min(1).max(80),
134
+ installedAt: z.number().int().nonnegative(),
135
+ })
136
+ .strict()
137
+ export type CommunitySource = z.output<typeof CommunitySourceSchema>
138
+
139
+ export const CommunityInstalledItemSchema = z
140
+ .object({
141
+ extensionId: ExtensionIdSchema,
142
+ displayName: z.string(),
143
+ revisionId: z.string(),
144
+ source: CommunitySourceSchema,
145
+ /** 来源与当前社区地址一致时才能检查更新。 */
146
+ sameCommunity: z.boolean(),
147
+ /** 社区仍有这个条目。 */
148
+ found: z.boolean(),
149
+ delisted: z.boolean(),
150
+ delistedReason: z.string().nullable(),
151
+ releaseWithdrawn: z.boolean(),
152
+ latest: CommunityReleaseSchema.nullable(),
153
+ updateAvailable: z.boolean(),
154
+ addedPermissions: z.array(CommunityPermissionItemSchema),
155
+ })
156
+ .strict()
157
+ export type CommunityInstalledItem = z.output<typeof CommunityInstalledItemSchema>
158
+
159
+ export const CommunityInstalledSchema = z
160
+ .object({ checkedAt: z.number().int().nullable(), items: z.array(CommunityInstalledItemSchema) })
161
+ .strict()
162
+
163
+ export const CommunityMyExtensionSchema = CommunityExtensionSummarySchema.extend({
164
+ releases: z.array(CommunityReleaseSchema.extend({ withdrawn: z.boolean() }).strict()),
165
+ delisted: z.boolean(),
166
+ delistedReason: z.string().nullable(),
167
+ }).strict()
168
+ export type CommunityMyExtension = z.output<typeof CommunityMyExtensionSchema>
169
+
170
+ export const COMMUNITY_REVIEW_DIMENSIONS = [
171
+ 'security',
172
+ 'transparency',
173
+ 'reliability',
174
+ 'context_cost',
175
+ 'usability',
176
+ 'maintainability',
177
+ 'compliance',
178
+ ] as const
179
+
180
+ export const COMMUNITY_REVIEW_DIMENSION_LABELS: Readonly<Record<(typeof COMMUNITY_REVIEW_DIMENSIONS)[number], string>> =
181
+ {
182
+ security: '安全',
183
+ transparency: '源码透明',
184
+ reliability: '可靠性',
185
+ context_cost: '上下文与成本',
186
+ usability: '可用性',
187
+ maintainability: '可维护性',
188
+ compliance: '合规',
189
+ }
190
+
191
+ /** 作者看到的完整审查报告。 */
192
+ export const CommunityReviewReportSchema = z
193
+ .object({
194
+ releaseId: CommunityReleaseIdSchema,
195
+ status: CommunityReviewStatusSchema,
196
+ grade: CommunityGradeSchema.nullable(),
197
+ deterministic: z.array(
198
+ z
199
+ .object({
200
+ severity: z.enum(['info', 'warning', 'risk', 'block']),
201
+ title: z.string(),
202
+ detail: z.string(),
203
+ file: z.string().optional(),
204
+ line: z.number().int().optional(),
205
+ })
206
+ .strict(),
207
+ ),
208
+ ai: z
209
+ .object({
210
+ summary: z.string(),
211
+ verdict: z.string(),
212
+ grade: CommunityGradeSchema,
213
+ dimensions: z.array(
214
+ z
215
+ .object({ key: z.enum(COMMUNITY_REVIEW_DIMENSIONS), grade: CommunityGradeSchema, notes: z.string() })
216
+ .strict(),
217
+ ),
218
+ findings: z.array(
219
+ z
220
+ .object({
221
+ dimension: z.enum(COMMUNITY_REVIEW_DIMENSIONS),
222
+ severity: z.enum(['info', 'suggestion', 'warning', 'risk', 'critical']),
223
+ title: z.string(),
224
+ detail: z.string(),
225
+ file: z.string().optional(),
226
+ line: z.number().int().optional(),
227
+ suggestion: z.string().optional(),
228
+ })
229
+ .strict(),
230
+ ),
231
+ exploitability: z.string().optional(),
232
+ })
233
+ .strict()
234
+ .nullable(),
235
+ model: z.string().nullable(),
236
+ error: z.string().nullable(),
237
+ decisions: z.array(
238
+ z.object({ decision: z.string(), note: z.string(), by: z.string(), createdAt: z.number().int() }).strict(),
239
+ ),
240
+ finishedAt: z.number().int().nullable(),
241
+ reportUrl: z.string().url(),
242
+ })
243
+ .strict()
244
+ export type CommunityReviewReport = z.output<typeof CommunityReviewReportSchema>
245
+
98
246
  /** 安装者看到的审查标签:只描述审查发现,不做「安全」担保。 */
99
247
  export const communityReviewLabel = (
100
248
  status: CommunityReviewStatus,
package/src/host-api.ts CHANGED
@@ -36,6 +36,11 @@ import {
36
36
  CommunityReleaseIdSchema,
37
37
  CommunityReviewStatusSchema,
38
38
  CommunityStatusSchema,
39
+ CommunitySourceSchema,
40
+ CommunityEndpointSchema,
41
+ CommunityInstalledSchema,
42
+ CommunityMyExtensionSchema,
43
+ CommunityReviewReportSchema,
39
44
  } from './community.js'
40
45
  import {
41
46
  DshNxtHostUiSchema,
@@ -1169,6 +1174,8 @@ export const HostSnapshotSchema = z
1169
1174
  ui: ExtensionUiContributionsSchema.default(EMPTY_EXTENSION_UI_CONTRIBUTIONS),
1170
1175
  configSchema: ConfigSchemaDocumentSchema.optional(),
1171
1176
  createdAt: z.number().int().safe().nonnegative(),
1177
+ /** 从社区安装的保存记录的来源;本机保存与文件导入没有来源。 */
1178
+ source: CommunitySourceSchema.optional(),
1172
1179
  scope: z.enum(['agent', 'host-adapter', 'host-ui']),
1173
1180
  contributions: z.array(z.string()),
1174
1181
  verification: z
@@ -2539,6 +2546,84 @@ export const HostApiContracts = {
2539
2546
  error: HostApiErrorSchema,
2540
2547
  }),
2541
2548
  /** 下载社区发布并按包摘要校验,结果与本地文件导入的检查相同,再经 `commitExtensionImport` 确认。 */
2549
+ getCommunityEndpoint: defineContract({
2550
+ method: 'GET',
2551
+ path: '/api/community/endpoint',
2552
+ params: EmptyParamsSchema,
2553
+ request: NoRequestBodySchema,
2554
+ response: CommunityEndpointSchema,
2555
+ error: HostApiErrorSchema,
2556
+ }),
2557
+ /** 修改社区地址;`url: null` 恢复为环境变量或默认地址。非加密的局域网地址需要确认风险。 */
2558
+ updateCommunityEndpoint: defineContract({
2559
+ method: 'PUT',
2560
+ path: '/api/community/endpoint',
2561
+ params: EmptyParamsSchema,
2562
+ request: z.object({ url: z.string().max(300).nullable(), acknowledgeInsecure: z.boolean() }).strict(),
2563
+ response: CommunityEndpointSchema,
2564
+ error: HostApiErrorSchema,
2565
+ }),
2566
+ testCommunityEndpoint: defineContract({
2567
+ timeoutMs: 30_000,
2568
+ method: 'POST',
2569
+ path: '/api/community/endpoint/test',
2570
+ params: EmptyParamsSchema,
2571
+ request: z.object({ url: z.string().max(300), acknowledgeInsecure: z.boolean() }).strict(),
2572
+ response: z
2573
+ .object({
2574
+ ok: z.boolean(),
2575
+ message: z.string(),
2576
+ name: z.string().optional(),
2577
+ environment: z.enum(['production', 'staging', 'development']).optional(),
2578
+ })
2579
+ .strict(),
2580
+ error: HostApiErrorSchema,
2581
+ }),
2582
+ listCommunityInstalled: defineContract({
2583
+ timeoutMs: 30_000,
2584
+ method: 'GET',
2585
+ path: '/api/community/installed',
2586
+ params: z.object({ refresh: z.enum(['1']).optional() }).strict(),
2587
+ request: NoRequestBodySchema,
2588
+ response: CommunityInstalledSchema,
2589
+ error: HostApiErrorSchema,
2590
+ }),
2591
+ listCommunityMine: defineContract({
2592
+ timeoutMs: 30_000,
2593
+ method: 'GET',
2594
+ path: '/api/community/mine',
2595
+ params: EmptyParamsSchema,
2596
+ request: NoRequestBodySchema,
2597
+ response: z.object({ items: z.array(CommunityMyExtensionSchema) }).strict(),
2598
+ error: HostApiErrorSchema,
2599
+ }),
2600
+ getCommunityReleaseReview: defineContract({
2601
+ timeoutMs: 30_000,
2602
+ method: 'GET',
2603
+ path: '/api/community/releases/:releaseId/review',
2604
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2605
+ request: NoRequestBodySchema,
2606
+ response: CommunityReviewReportSchema,
2607
+ error: HostApiErrorSchema,
2608
+ }),
2609
+ requestCommunityReview: defineContract({
2610
+ timeoutMs: 30_000,
2611
+ method: 'POST',
2612
+ path: '/api/community/releases/:releaseId/review',
2613
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2614
+ request: NoRequestBodySchema,
2615
+ response: z.object({ ok: z.literal(true) }).strict(),
2616
+ error: HostApiErrorSchema,
2617
+ }),
2618
+ withdrawCommunityRelease: defineContract({
2619
+ timeoutMs: 30_000,
2620
+ method: 'POST',
2621
+ path: '/api/community/releases/:releaseId/withdraw',
2622
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2623
+ request: NoRequestBodySchema,
2624
+ response: z.object({ ok: z.literal(true) }).strict(),
2625
+ error: HostApiErrorSchema,
2626
+ }),
2542
2627
  importCommunityRelease: defineContract({
2543
2628
  timeoutMs: 120_000,
2544
2629
  method: 'POST',