@nekro-nxt/contracts 0.5.0 → 0.7.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.
@@ -0,0 +1,260 @@
1
+ import { z } from 'zod'
2
+ import { AgentIdSchema, ChannelIdSchema, CredentialIdSchema, ExtensionIdSchema } from './domain.js'
3
+ import { ExtensionNetworkDomainSchema } from './extension-capabilities.js'
4
+
5
+ /**
6
+ * Named credentials (智能体扩展配置与凭据 C1–C5): one entry is added once and reused by extensions, MCP servers and
7
+ * commands. The value is write-only; every view below carries metadata only. Accounts on platforms such as GitHub are
8
+ * linked accounts (`linked-accounts.ts`), granted per agent, not credentials.
9
+ */
10
+
11
+ export const CREDENTIAL_NAME_MAX_CHARS = 64
12
+ export const CREDENTIAL_DESCRIPTION_MAX_CHARS = 500
13
+ export const CREDENTIAL_VALUE_MAX_CHARS = 16_384
14
+ /** Values shorter than this are not hidden from tool results; the form warns when one is saved. */
15
+ export const CREDENTIAL_REDACTABLE_MIN_CHARS = 8
16
+
17
+ /** How a credential reaches code: `request` only as a placeholder filled by `nxt.http.fetch`; `program` as the value. */
18
+ export const CredentialUsageSchema = z.enum(['request', 'program'])
19
+ export type CredentialUsage = z.output<typeof CredentialUsageSchema>
20
+
21
+ /** Agents that may use a credential: every agent, or the listed ones. */
22
+ export const CredentialAgentScopeSchema = z.union([
23
+ z.object({ all: z.literal(true) }).strict(),
24
+ z.object({ agentIds: z.array(AgentIdSchema).max(256) }).strict(),
25
+ ])
26
+ export type CredentialAgentScope = z.output<typeof CredentialAgentScopeSchema>
27
+
28
+ /** Shown in redaction markers (`[凭据:名称]`), so brackets and control characters are not allowed. */
29
+ export const CredentialNameSchema = z
30
+ .string()
31
+ .trim()
32
+ .min(1, '名称不能为空。')
33
+ .max(CREDENTIAL_NAME_MAX_CHARS, `名称最多 ${CREDENTIAL_NAME_MAX_CHARS} 个字符。`)
34
+ // eslint-disable-next-line no-control-regex -- Control characters are exactly what the name must not contain.
35
+ .regex(/^[^\u0000-\u001f\u007f[\]]+$/u, '名称不能包含方括号或控制字符。')
36
+
37
+ export const CredentialDescriptionSchema = z
38
+ .string()
39
+ .trim()
40
+ .max(CREDENTIAL_DESCRIPTION_MAX_CHARS, `说明最多 ${CREDENTIAL_DESCRIPTION_MAX_CHARS} 个字符。`)
41
+
42
+ export const CredentialDomainsSchema = z.array(ExtensionNetworkDomainSchema).max(32)
43
+
44
+ /**
45
+ * Names a credential's environment variable cannot take: the shell, the tools' own configuration, and the variables
46
+ * linked accounts give commands (`GH_TOKEN` and the `gh` settings).
47
+ */
48
+ const RESERVED_COMMAND_ENV = new Set([
49
+ 'PATH',
50
+ 'HOME',
51
+ 'USER',
52
+ 'LOGNAME',
53
+ 'SHELL',
54
+ 'PWD',
55
+ 'OLDPWD',
56
+ 'TMPDIR',
57
+ 'TEMP',
58
+ 'TMP',
59
+ 'LANG',
60
+ 'TERM',
61
+ 'NODE_OPTIONS',
62
+ 'NODE_PATH',
63
+ 'GH_TOKEN',
64
+ 'GH_CONFIG_DIR',
65
+ 'GH_PROMPT_DISABLED',
66
+ 'GH_NO_UPDATE_NOTIFIER',
67
+ 'GH_NO_EXTENSION_UPDATE_NOTIFIER',
68
+ 'GIT_TERMINAL_PROMPT',
69
+ 'GIT_CONFIG_COUNT',
70
+ ])
71
+ const RESERVED_COMMAND_ENV_PREFIXES = ['LC_', 'LD_', 'DYLD_', 'GIT_CONFIG_', 'NEKRO_', 'DSH_']
72
+
73
+ /** The environment variable a program credential reaches commands under. */
74
+ export const CredentialEnvNameSchema = z
75
+ .string()
76
+ .trim()
77
+ .max(64)
78
+ .regex(/^[A-Z_][A-Z0-9_]*$/u, '环境变量名只能使用大写字母、数字和下划线,且不能以数字开头。')
79
+ .refine(
80
+ (name) =>
81
+ !RESERVED_COMMAND_ENV.has(name) && !RESERVED_COMMAND_ENV_PREFIXES.some((prefix) => name.startsWith(prefix)),
82
+ '这个环境变量名由系统使用,请换一个。',
83
+ )
84
+
85
+ export const CredentialValueSchema = z.string().min(1).max(CREDENTIAL_VALUE_MAX_CHARS)
86
+
87
+ /** What uses one credential right now: extension secret fields bound to it and agents whose commands receive it. */
88
+ export const CredentialUsersSchema = z
89
+ .object({
90
+ extensions: z.array(
91
+ z
92
+ .object({
93
+ extensionId: ExtensionIdSchema,
94
+ name: z.string(),
95
+ /** Agents whose own configuration binds it; empty when only the host configuration does. */
96
+ agentIds: z.array(AgentIdSchema),
97
+ host: z.boolean(),
98
+ })
99
+ .strict(),
100
+ ),
101
+ /** Agents with commands enabled whose commands receive it. */
102
+ commandAgents: z.array(AgentIdSchema),
103
+ })
104
+ .strict()
105
+ export type CredentialUsers = z.output<typeof CredentialUsersSchema>
106
+
107
+ export const CredentialViewSchema = z
108
+ .object({
109
+ id: CredentialIdSchema,
110
+ name: CredentialNameSchema,
111
+ description: z.string(),
112
+ usage: CredentialUsageSchema,
113
+ domains: z.array(z.string()),
114
+ agentScope: CredentialAgentScopeSchema,
115
+ autoUse: z.boolean(),
116
+ /** Program credentials: the environment variable agents' commands receive it under, or `null` when they do not. */
117
+ envName: z.string().nullable(),
118
+ /** Agents an administrator let use it while automatic use is off. */
119
+ approvedAgentIds: z.array(AgentIdSchema),
120
+ /** False when the stored value is too short to hide from tool results, or cannot be read. */
121
+ redactable: z.boolean(),
122
+ /** False when the stored value cannot be read any more; the entry stays so it can be replaced. */
123
+ available: z.boolean(),
124
+ usedBy: CredentialUsersSchema,
125
+ createdAt: z.number().int().nonnegative(),
126
+ updatedAt: z.number().int().nonnegative(),
127
+ lastUsedAt: z.number().int().nonnegative().nullable(),
128
+ })
129
+ .strict()
130
+ export type CredentialView = z.output<typeof CredentialViewSchema>
131
+
132
+ /** A model provider listed with the credentials; `open` lets agents and extensions call it through the host. */
133
+ export const ModelProviderAccessSchema = z
134
+ .object({
135
+ provider: z.string().min(1),
136
+ name: z.string(),
137
+ open: z.boolean(),
138
+ })
139
+ .strict()
140
+ export type ModelProviderAccess = z.output<typeof ModelProviderAccessSchema>
141
+
142
+ /**
143
+ * An agent asked for a credential that does not exist yet. The settings link pre-fills the add form; saving a
144
+ * credential with this name wakes the agent in the channel it asked from.
145
+ */
146
+ export const CredentialRequestViewSchema = z
147
+ .object({
148
+ id: z.string().min(1).max(64),
149
+ name: CredentialNameSchema,
150
+ purpose: z.string(),
151
+ domains: z.array(z.string()),
152
+ usage: CredentialUsageSchema,
153
+ agentId: AgentIdSchema,
154
+ channelId: ChannelIdSchema.nullable(),
155
+ createdAt: z.number().int().nonnegative(),
156
+ /** Settings route that opens the add form pre-filled, see {@link credentialRequestRoute}. */
157
+ route: z.string(),
158
+ })
159
+ .strict()
160
+ export type CredentialRequestView = z.output<typeof CredentialRequestViewSchema>
161
+
162
+ export const CredentialsViewSchema = z
163
+ .object({
164
+ credentials: z.array(CredentialViewSchema),
165
+ modelProviders: z.array(ModelProviderAccessSchema),
166
+ requests: z.array(CredentialRequestViewSchema),
167
+ })
168
+ .strict()
169
+ export type CredentialsView = z.output<typeof CredentialsViewSchema>
170
+
171
+ export const CreateCredentialRequestSchema = z
172
+ .object({
173
+ name: CredentialNameSchema,
174
+ description: CredentialDescriptionSchema.default(''),
175
+ agentScope: CredentialAgentScopeSchema.default({ all: true }),
176
+ autoUse: z.boolean().default(true),
177
+ usage: CredentialUsageSchema.default('request'),
178
+ domains: CredentialDomainsSchema.default([]),
179
+ /** Program credentials only. */
180
+ envName: CredentialEnvNameSchema.optional(),
181
+ value: CredentialValueSchema,
182
+ /** The agent request this form was opened from, if any. */
183
+ requestId: z.string().min(1).max(64).optional(),
184
+ })
185
+ .strict()
186
+ export type CreateCredentialRequest = z.input<typeof CreateCredentialRequestSchema>
187
+
188
+ /** Metadata changes; omitted fields keep their value. */
189
+ export const UpdateCredentialRequestSchema = z
190
+ .object({
191
+ name: CredentialNameSchema.optional(),
192
+ description: CredentialDescriptionSchema.optional(),
193
+ usage: CredentialUsageSchema.optional(),
194
+ domains: CredentialDomainsSchema.optional(),
195
+ agentScope: CredentialAgentScopeSchema.optional(),
196
+ autoUse: z.boolean().optional(),
197
+ /** `null` stops giving it to commands; only program credentials take one. */
198
+ envName: CredentialEnvNameSchema.nullable().optional(),
199
+ /** Replaces the agents allowed to use it while automatic use is off, for example to withdraw an approval. */
200
+ approvedAgentIds: z.array(AgentIdSchema).max(256).optional(),
201
+ })
202
+ .strict()
203
+ export type UpdateCredentialRequest = z.output<typeof UpdateCredentialRequestSchema>
204
+
205
+ export const ReplaceCredentialValueRequestSchema = z.object({ value: CredentialValueSchema }).strict()
206
+ export type ReplaceCredentialValueRequest = z.output<typeof ReplaceCredentialValueRequestSchema>
207
+
208
+ /** Removing a credential that is in use needs `confirm`; without it the result lists who uses it. */
209
+ export const RemoveCredentialRequestSchema = z.object({ confirm: z.boolean().default(false) }).strict()
210
+
211
+ export const RemoveCredentialResultSchema = z.discriminatedUnion('removed', [
212
+ z.object({ removed: z.literal(true), view: CredentialsViewSchema }).strict(),
213
+ z.object({ removed: z.literal(false), usedBy: CredentialUsersSchema }).strict(),
214
+ ])
215
+ export type RemoveCredentialResult = z.output<typeof RemoveCredentialResultSchema>
216
+
217
+ export const CredentialConsumerKindSchema = z.enum(['extension', 'command', 'mcp', 'model'])
218
+
219
+ /** One recorded use. The newest 500 per credential are kept, none older than 90 days. */
220
+ export const CredentialUseSchema = z
221
+ .object({
222
+ at: z.number().int().nonnegative(),
223
+ agentId: AgentIdSchema.nullable(),
224
+ channelId: ChannelIdSchema.nullable(),
225
+ requesterMemberId: z.string().nullable(),
226
+ consumerKind: CredentialConsumerKindSchema,
227
+ consumerId: z.string(),
228
+ /** Extension display name or MCP server name, when it is still known. */
229
+ consumerName: z.string().nullable(),
230
+ targetHost: z.string().nullable(),
231
+ })
232
+ .strict()
233
+ export type CredentialUse = z.output<typeof CredentialUseSchema>
234
+
235
+ export const CredentialUsesViewSchema = z.object({ uses: z.array(CredentialUseSchema) }).strict()
236
+
237
+ export const SetModelProviderAccessRequestSchema = z.object({ open: z.boolean() }).strict()
238
+
239
+ /**
240
+ * The settings route that opens the add form pre-filled from an agent's request: the credential section with `add=1`
241
+ * plus `name`, `purpose`, `domains` (comma separated), `usage` and `request` (the request id) as query parameters.
242
+ */
243
+ export const credentialRequestRoute = (request: {
244
+ readonly id: string
245
+ readonly name: string
246
+ readonly purpose: string
247
+ readonly domains: readonly string[]
248
+ readonly usage: CredentialUsage
249
+ }): string => {
250
+ const query = new URLSearchParams({ add: '1', name: request.name, request: request.id, usage: request.usage })
251
+ if (request.purpose !== '') query.set('purpose', request.purpose)
252
+ if (request.domains.length > 0) query.set('domains', request.domains.join(','))
253
+ return `/settings/credentials?${query.toString()}`
254
+ }
255
+
256
+ /** Secret fields of an extension configuration layer bound to a named credential, by field. */
257
+ export const BoundCredentialsSchema = z.record(z.string(), CredentialIdSchema)
258
+
259
+ /** Binds secret fields to named credentials; `null` removes the binding and leaves the field unset. */
260
+ export const CredentialBindingsRequestSchema = z.record(z.string(), CredentialIdSchema.nullable())
package/src/domain.ts CHANGED
@@ -18,6 +18,15 @@ export const MessageDeliverySummarySchema = z
18
18
  )
19
19
  .max(20),
20
20
  omittedIssues: z.number().int().nonnegative().optional(),
21
+ /** Extensions that changed the message before it was sent, the content they produced and what they appended. */
22
+ processing: z
23
+ .object({
24
+ processedBy: z.array(z.string()),
25
+ parts: z.array(z.unknown()).optional(),
26
+ followUps: z.array(z.string()).optional(),
27
+ })
28
+ .strict()
29
+ .optional(),
21
30
  })
22
31
  .strict()
23
32
 
@@ -50,9 +59,13 @@ export const HostUiPageInstanceIdSchema = brandedId('hup', 'HostUiPageInstanceId
50
59
  export const AuthoringTaskIdSchema = brandedId('aut', 'AuthoringTaskId')
51
60
  export const AuthoringAttemptIdSchema = brandedId('aua', 'AuthoringAttemptId')
52
61
  export const SubConversationIdSchema = brandedId('cnv', 'SubConversationId')
62
+ export const CredentialIdSchema = brandedId('crd', 'CredentialId')
63
+ export const LinkedAccountIdSchema = brandedId('acc', 'LinkedAccountId')
53
64
 
54
65
  export type AgentId = z.infer<typeof AgentIdSchema>
55
66
  export type SubConversationId = z.infer<typeof SubConversationIdSchema>
67
+ export type CredentialId = z.infer<typeof CredentialIdSchema>
68
+ export type LinkedAccountId = z.infer<typeof LinkedAccountIdSchema>
56
69
  export type AgentRevisionId = z.infer<typeof AgentRevisionIdSchema>
57
70
  export type ConnectionId = z.infer<typeof ConnectionIdSchema>
58
71
  export type ConnectionEventId = z.infer<typeof ConnectionEventIdSchema>
@@ -228,8 +241,8 @@ export interface DshPluginActivationRecord {
228
241
  export interface DshPluginDiagnosticRecord {
229
242
  readonly entryId: DshPluginEntryId
230
243
  readonly targetKey: string
231
- readonly status: 'active' | 'load-failed' | 'restore-failed' | 'dispose-failed'
232
- readonly phase: 'import' | 'apply' | 'update' | 'dispose' | 'restore'
244
+ readonly status: 'active' | 'pending' | 'load-failed' | 'restore-failed' | 'dispose-failed'
245
+ readonly phase: 'import' | 'dependency' | 'apply' | 'update' | 'dispose' | 'restore'
233
246
  readonly message?: string
234
247
  readonly observedAt: number
235
248
  }