@zhin.js/host-http 1.2.1 → 1.3.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/lib/access-control.d.ts +50 -5
- package/lib/access-control.js +167 -13
- package/lib/console-rest-pages-helpers.js +24 -2
- package/lib/console-rest-pages.d.ts +4 -1
- package/lib/console-rest-pages.js +49 -23
- package/lib/console-rpc-extended/contracts.d.ts +92 -15
- package/lib/console-rpc-extended/dispatcher.js +10 -3
- package/lib/console-rpc-extended/endpoint-rpc.js +21 -4
- package/lib/console-rpc-extended/index.d.ts +2 -1
- package/lib/console-rpc-extended/index.js +1 -0
- package/lib/console-rpc-extended/login-rpc.d.ts +25 -2
- package/lib/console-rpc-extended/login-rpc.js +276 -16
- package/lib/console-rpc-extended/schedule-rpc.d.ts +1 -1
- package/lib/console-rpc-extended/schedule-rpc.js +78 -12
- package/lib/console-rpc-handlers.d.ts +11 -1
- package/lib/console-rpc-handlers.js +221 -12
- package/lib/console-rpc-types.d.ts +2 -0
- package/lib/console-rpc-types.js +43 -0
- package/lib/console-rpc.d.ts +29 -1
- package/lib/console-rpc.js +33 -7
- package/lib/http-host.js +5 -4
- package/lib/index.d.ts +5 -3
- package/lib/index.js +4 -2
- package/lib/project-files.d.ts +7 -0
- package/lib/project-files.js +88 -11
- package/lib/unknown-rpc.d.ts +12 -0
- package/lib/unknown-rpc.js +33 -0
- package/package.json +3 -3
- package/src/access-control.ts +209 -14
- package/src/console-rest-pages-helpers.ts +27 -3
- package/src/console-rest-pages.ts +52 -15
- package/src/console-rpc-extended/contracts.ts +83 -16
- package/src/console-rpc-extended/dispatcher.ts +10 -3
- package/src/console-rpc-extended/endpoint-rpc.ts +28 -4
- package/src/console-rpc-extended/index.ts +4 -0
- package/src/console-rpc-extended/login-rpc.ts +250 -17
- package/src/console-rpc-extended/schedule-rpc.ts +67 -3
- package/src/console-rpc-handlers.ts +198 -11
- package/src/console-rpc-types.ts +45 -0
- package/src/console-rpc.ts +63 -7
- package/src/http-host.ts +6 -2
- package/src/index.ts +15 -0
- package/src/project-files.ts +95 -11
- package/src/unknown-rpc.ts +44 -0
|
@@ -25,10 +25,17 @@ const INTROSPECTION_PAGE_SIZES = {
|
|
|
25
25
|
endpoints: 30,
|
|
26
26
|
bindings: 30,
|
|
27
27
|
mcp: 30,
|
|
28
|
+
'recent-turns': 20,
|
|
28
29
|
} as const;
|
|
29
30
|
|
|
30
31
|
type IntrospectionKind = keyof typeof INTROSPECTION_PAGE_SIZES;
|
|
31
32
|
|
|
33
|
+
/** Owning plugin of a capability row (`ConsoleIntrospectionOwner`, #1250): searchable by `q`. */
|
|
34
|
+
const OWNER_FIELDS: ReadonlyArray<(item: unknown) => string | undefined> = [
|
|
35
|
+
(item) => stringProp(item, 'pluginInstance'),
|
|
36
|
+
(item) => stringProp(item, 'pluginPackage'),
|
|
37
|
+
];
|
|
38
|
+
|
|
32
39
|
// ---------------------------------------------------------------------------
|
|
33
40
|
// Runtime snapshot introspection
|
|
34
41
|
// ---------------------------------------------------------------------------
|
|
@@ -41,7 +48,8 @@ export function registerIntrospectionRoutes(
|
|
|
41
48
|
): void {
|
|
42
49
|
const collectors: Array<{
|
|
43
50
|
kind: IntrospectionKind;
|
|
44
|
-
collect: (introspection: ConsoleAgentIntrospection | undefined) =>
|
|
51
|
+
collect: (introspection: ConsoleAgentIntrospection | undefined) =>
|
|
52
|
+
{ rows: readonly unknown[]; note?: string } | Promise<{ rows: readonly unknown[]; note?: string }>;
|
|
45
53
|
fields: Array<(item: unknown) => string | undefined>;
|
|
46
54
|
}> = [
|
|
47
55
|
{
|
|
@@ -51,6 +59,7 @@ export function registerIntrospectionRoutes(
|
|
|
51
59
|
(c) => stringProp(c, 'pattern'),
|
|
52
60
|
(c) => stringProp(c, 'desc'),
|
|
53
61
|
(c) => stringProp(c, 'plugin'),
|
|
62
|
+
...OWNER_FIELDS,
|
|
54
63
|
],
|
|
55
64
|
},
|
|
56
65
|
{
|
|
@@ -62,6 +71,7 @@ export function registerIntrospectionRoutes(
|
|
|
62
71
|
(m) => stringProp(m, 'phase'),
|
|
63
72
|
(m) => stringProp(m, 'target'),
|
|
64
73
|
(m) => stringProp(m, 'source'),
|
|
74
|
+
...OWNER_FIELDS,
|
|
65
75
|
],
|
|
66
76
|
},
|
|
67
77
|
{
|
|
@@ -71,6 +81,7 @@ export function registerIntrospectionRoutes(
|
|
|
71
81
|
(c) => stringProp(c, 'name'),
|
|
72
82
|
(c) => stringProp(c, 'owner'),
|
|
73
83
|
(c) => stringProp(c, 'source'),
|
|
84
|
+
...OWNER_FIELDS,
|
|
74
85
|
],
|
|
75
86
|
},
|
|
76
87
|
{
|
|
@@ -87,11 +98,22 @@ export function registerIntrospectionRoutes(
|
|
|
87
98
|
},
|
|
88
99
|
{
|
|
89
100
|
kind: 'bindings',
|
|
90
|
-
collect: (value) => ({ rows: value?.bindings?.() ?? [] }),
|
|
101
|
+
collect: async (value) => ({ rows: await value?.bindings?.() ?? [] }),
|
|
91
102
|
fields: [
|
|
92
103
|
(a) => stringProp(a, 'name'),
|
|
93
104
|
(a) => stringProp(a, 'provider'),
|
|
94
105
|
(a) => stringProp(a, 'model'),
|
|
106
|
+
(a) => stringProp(a, 'displayName'),
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
// #1250: the Agent workbench's recent conversations come from the turn trace archive, newest first.
|
|
111
|
+
kind: 'recent-turns',
|
|
112
|
+
collect: async (value) => ({ rows: await value?.recentTurns?.() ?? [] }),
|
|
113
|
+
fields: [
|
|
114
|
+
(t) => stringProp(t, 'sessionDisplayName'),
|
|
115
|
+
(t) => stringProp(t, 'sessionKey'),
|
|
116
|
+
(t) => stringProp(t, 'status'),
|
|
95
117
|
],
|
|
96
118
|
},
|
|
97
119
|
{
|
|
@@ -101,6 +123,7 @@ export function registerIntrospectionRoutes(
|
|
|
101
123
|
(t) => stringProp(t, 'name'),
|
|
102
124
|
(t) => stringProp(t, 'source'),
|
|
103
125
|
(t) => stringProp(t, 'description'),
|
|
126
|
+
...OWNER_FIELDS,
|
|
104
127
|
],
|
|
105
128
|
},
|
|
106
129
|
{
|
|
@@ -114,6 +137,7 @@ export function registerIntrospectionRoutes(
|
|
|
114
137
|
(section) => stringProp(section, 'layer'),
|
|
115
138
|
(section) => stringProp(section, 'retention'),
|
|
116
139
|
(section) => stringProp(section, 'source'),
|
|
140
|
+
...OWNER_FIELDS,
|
|
117
141
|
],
|
|
118
142
|
},
|
|
119
143
|
{
|
|
@@ -136,7 +160,7 @@ export function registerIntrospectionRoutes(
|
|
|
136
160
|
const lease = kind === 'endpoints' ? null : ctx.acquireAgentRuntime?.() ?? null;
|
|
137
161
|
try {
|
|
138
162
|
const introspection = lease?.value.introspection;
|
|
139
|
-
const { rows, note } = collect(introspection);
|
|
163
|
+
const { rows, note } = await collect(introspection);
|
|
140
164
|
const query = url.searchParams;
|
|
141
165
|
const filter = query.get('filter')?.trim() || undefined;
|
|
142
166
|
const filtered = filterByFields([...rows], filter, fields);
|
|
@@ -17,6 +17,15 @@ import {
|
|
|
17
17
|
isValidNpmPackageName,
|
|
18
18
|
} from './marketplace-registry.js';
|
|
19
19
|
import type { ConsoleMessageEventData } from '@zhin.js/console-protocol';
|
|
20
|
+
import {
|
|
21
|
+
informativeKeywords,
|
|
22
|
+
isLocale,
|
|
23
|
+
normalizePluginCategory,
|
|
24
|
+
packageAuthorName,
|
|
25
|
+
pickLocalizedText,
|
|
26
|
+
readPluginMarketplaceMetadata,
|
|
27
|
+
type PluginLocalizedText,
|
|
28
|
+
} from '@zhin.js/plugin-runtime';
|
|
20
29
|
import {
|
|
21
30
|
clampPositiveInt,
|
|
22
31
|
decodePathParam,
|
|
@@ -65,7 +74,10 @@ export interface ConsoleAgentIntrospection {
|
|
|
65
74
|
commands?(): readonly unknown[];
|
|
66
75
|
middlewares?(): readonly unknown[];
|
|
67
76
|
components?(): readonly unknown[];
|
|
68
|
-
bindings
|
|
77
|
+
/** Agent bindings with the provider / model they actually run on (may read the provider graph). */
|
|
78
|
+
bindings?(): readonly unknown[] | Promise<readonly unknown[]>;
|
|
79
|
+
/** Recent Agent turns from the trace archive, newest first (#1250). */
|
|
80
|
+
recentTurns?(): readonly unknown[] | Promise<readonly unknown[]>;
|
|
69
81
|
tools?(): readonly unknown[];
|
|
70
82
|
promptSections?(): readonly unknown[];
|
|
71
83
|
mcp?(): { rows: readonly unknown[]; note?: string } | readonly unknown[];
|
|
@@ -356,6 +368,11 @@ function registerLogsRoutes(
|
|
|
356
368
|
}, { summary: 'Cleanup old system logs', tags: ['console', 'logs'] });
|
|
357
369
|
}
|
|
358
370
|
|
|
371
|
+
/** Locale-keyed texts from an untrusted registry entry (reuses the published-manifest reader). */
|
|
372
|
+
function localizedTextOf(value: unknown): PluginLocalizedText | undefined {
|
|
373
|
+
return readPluginMarketplaceMetadata({ metadata: { displayNameI18n: value } }).displayNameI18n;
|
|
374
|
+
}
|
|
375
|
+
|
|
359
376
|
// ---------------------------------------------------------------------------
|
|
360
377
|
// marketplace(legacy marketplace-rest-api.ts;plugins.json + npmmirror)
|
|
361
378
|
// ---------------------------------------------------------------------------
|
|
@@ -390,10 +407,17 @@ function registerMarketplaceRoutes(
|
|
|
390
407
|
const versions = meta.versions as Record<string, Record<string, unknown>> | undefined;
|
|
391
408
|
const latestInfo = latest && versions ? versions[latest] : undefined;
|
|
392
409
|
const time = meta.time as Record<string, string> | undefined;
|
|
410
|
+
// Marketplace fields come from the published manifest (`zhin.metadata`); the author is the
|
|
411
|
+
// package's author / maintainers, never the publishing account (`_npmUser`, a release bot).
|
|
412
|
+
const market = readPluginMarketplaceMetadata(latestInfo?.zhin);
|
|
393
413
|
return {
|
|
394
414
|
name: meta.name,
|
|
395
415
|
version: latest,
|
|
396
|
-
|
|
416
|
+
...(market.displayName ? { displayName: market.displayName } : {}),
|
|
417
|
+
...(market.displayNameI18n ? { displayNameI18n: market.displayNameI18n } : {}),
|
|
418
|
+
...(market.descriptionI18n ? { descriptionI18n: market.descriptionI18n } : {}),
|
|
419
|
+
...(market.category ? { category: market.category } : {}),
|
|
420
|
+
description: (latestInfo?.description as string | undefined) || meta.description || '',
|
|
397
421
|
readme: meta.readme || '',
|
|
398
422
|
license: meta.license || latestInfo?.license || '',
|
|
399
423
|
homepage: meta.homepage || latestInfo?.homepage || '',
|
|
@@ -401,11 +425,8 @@ function registerMarketplaceRoutes(
|
|
|
401
425
|
(meta.repository as { url?: string } | undefined)?.url ||
|
|
402
426
|
(latestInfo?.repository as { url?: string } | undefined)?.url ||
|
|
403
427
|
'',
|
|
404
|
-
author:
|
|
405
|
-
|
|
406
|
-
? meta.author
|
|
407
|
-
: (meta.author as { name?: string } | undefined)?.name || '',
|
|
408
|
-
keywords: (latestInfo?.keywords as string[]) || [],
|
|
428
|
+
author: packageAuthorName(latestInfo ?? {}) ?? packageAuthorName(meta) ?? '',
|
|
429
|
+
keywords: informativeKeywords(latestInfo?.keywords),
|
|
409
430
|
engines: latestInfo?.engines || {},
|
|
410
431
|
peerDependencies: latestInfo?.peerDependencies || {},
|
|
411
432
|
versions: Object.keys(versions || {}),
|
|
@@ -421,32 +442,48 @@ function registerMarketplaceRoutes(
|
|
|
421
442
|
100,
|
|
422
443
|
Math.max(1, parseInt(url.searchParams.get('size') ?? url.searchParams.get('limit') ?? '20', 10) || 20),
|
|
423
444
|
);
|
|
424
|
-
const
|
|
445
|
+
const rawCategory = url.searchParams.get('category') || undefined;
|
|
446
|
+
const category = rawCategory === undefined ? undefined : normalizePluginCategory(rawCategory) ?? rawCategory;
|
|
425
447
|
const official = url.searchParams.get('official') ?? undefined;
|
|
448
|
+
const rawLocale = url.searchParams.get('locale');
|
|
449
|
+
const locale = isLocale(rawLocale) ? rawLocale : undefined;
|
|
426
450
|
const searchKeyword = q.trim().toLowerCase();
|
|
427
451
|
|
|
428
452
|
try {
|
|
429
453
|
const allPlugins = (await registry.list()) as readonly Record<string, unknown>[];
|
|
430
|
-
let plugins = allPlugins.map((p) =>
|
|
454
|
+
let plugins = allPlugins.map((p) => {
|
|
455
|
+
const displayNameI18n = localizedTextOf(p.displayNameI18n);
|
|
456
|
+
const descriptionI18n = localizedTextOf(p.descriptionI18n);
|
|
457
|
+
const displayName = typeof p.displayName === 'string' ? p.displayName : '';
|
|
458
|
+
const description = typeof p.description === 'string' ? p.description : '';
|
|
459
|
+
return {
|
|
431
460
|
name: p.name,
|
|
432
|
-
|
|
461
|
+
// `locale` resolves the display texts for the caller; translations stay available either way.
|
|
462
|
+
displayName: locale ? pickLocalizedText(displayNameI18n, locale, displayName) ?? '' : displayName,
|
|
463
|
+
...(displayNameI18n ? { displayNameI18n } : {}),
|
|
433
464
|
version: p.version || '',
|
|
434
|
-
description:
|
|
465
|
+
description: locale ? pickLocalizedText(descriptionI18n, locale, description) ?? '' : description,
|
|
466
|
+
...(descriptionI18n ? { descriptionI18n } : {}),
|
|
435
467
|
author: p.author || '',
|
|
436
468
|
isOfficial: typeof p.name === 'string' && /^@zhin\.js\/[a-z0-9][a-z0-9._-]*$/.test(p.name),
|
|
437
469
|
official: typeof p.name === 'string' && /^@zhin\.js\/[a-z0-9][a-z0-9._-]*$/.test(p.name),
|
|
438
|
-
category: p.category
|
|
439
|
-
keywords: (p.tags
|
|
470
|
+
category: normalizePluginCategory(p.category) ?? 'utility',
|
|
471
|
+
keywords: informativeKeywords(p.tags),
|
|
440
472
|
npm: p.npm || `https://www.npmjs.com/package/${p.name}`,
|
|
441
473
|
date: p.lastUpdate || '',
|
|
442
474
|
downloads: p.downloads || { weekly: 0, monthly: 0 },
|
|
443
475
|
readme: p.readme || '',
|
|
444
476
|
license: p.license || '',
|
|
445
|
-
|
|
477
|
+
};
|
|
478
|
+
});
|
|
446
479
|
|
|
447
480
|
if (searchKeyword) {
|
|
448
481
|
plugins = plugins.filter((p) => {
|
|
449
|
-
const haystack = [
|
|
482
|
+
const haystack = [
|
|
483
|
+
p.name, p.displayName, p.description,
|
|
484
|
+
...Object.values(p.displayNameI18n ?? {}), ...Object.values(p.descriptionI18n ?? {}),
|
|
485
|
+
...p.keywords,
|
|
486
|
+
]
|
|
450
487
|
.join(' ')
|
|
451
488
|
.toLowerCase();
|
|
452
489
|
return haystack.includes(searchKeyword);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConsoleAdapterLoginDeclaration, ConsoleLoginFlow, ConsoleLoginOutcome } from '@zhin.js/console-protocol';
|
|
1
2
|
interface LoginEndpointIdentity {
|
|
2
3
|
readonly canonicalId: string;
|
|
3
4
|
readonly instanceId: string;
|
|
@@ -21,25 +22,36 @@ export interface ConsoleRpcExtendedCtx {
|
|
|
21
22
|
databaseHost?: { models: { get(name: string): unknown } };
|
|
22
23
|
/** Narrow generation-bound persistent-job port; null when Assistant is disabled. */
|
|
23
24
|
resolveScheduleEngine?: () => ConsoleScheduleEngine | null | undefined;
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
cancel(id: string, reason?: string, endpointIdentity?: LoginEndpointIdentity): boolean;
|
|
38
|
-
};
|
|
25
|
+
/**
|
|
26
|
+
* Resolves a persistent job's delivery target (#1250): `{ resolved, display?, reason? }`, where
|
|
27
|
+
* `resolved: false` means the job's results cannot be delivered (no target / unknown account).
|
|
28
|
+
*/
|
|
29
|
+
describeScheduleTarget?: (notify: unknown) => ConsoleScheduleTarget;
|
|
30
|
+
/** LoginAssist list/submit/cancel(刷新后仍可消费扫码/滑块待办);ADR 0058 flow methods are optional. */
|
|
31
|
+
loginAssist?: ConsoleLoginAssistPort;
|
|
32
|
+
/** ADR 0058: the Endpoint's login declaration and lease-scoped login control (`null` = unknown Endpoint). */
|
|
33
|
+
withEndpointLogin?: <T>(
|
|
34
|
+
adapter: string,
|
|
35
|
+
endpointKey: string,
|
|
36
|
+
run: (login: ConsoleEndpointLoginAccess) => T | Promise<T>,
|
|
37
|
+
) => Promise<T | null>;
|
|
39
38
|
/** Authenticated Root adapter; mutation DTOs deliberately contain no principal/decision fields. */
|
|
40
39
|
workroomProfileControl?: ConsoleWorkroomProfileControlPort;
|
|
41
40
|
/** Authenticated HTTP principal; never accepted from the RPC body. */
|
|
42
41
|
authenticatedPrincipal?: Readonly<{ principalId: string }>;
|
|
42
|
+
/**
|
|
43
|
+
* The Endpoint the caller was authorized for when its grant covers this call only at an adapter /
|
|
44
|
+
* endpoint scope (never set for global grants, owners or in-process callers). Handlers that act on
|
|
45
|
+
* login tasks or flows restrict themselves to it; set by the dispatcher, never read from the body.
|
|
46
|
+
*/
|
|
47
|
+
authorizedEndpoint?: Readonly<{ adapter: string; endpointKey: string }>;
|
|
48
|
+
/**
|
|
49
|
+
* Whether the caller holds `endpoint.operate` on one Endpoint (set by the dispatcher from the
|
|
50
|
+
* authenticated principal, never read from the body). Login read RPCs return live challenge
|
|
51
|
+
* material (QR image, sensitive blocks, verification links, legacy `payload.image` / `url`) only
|
|
52
|
+
* when it says yes; absent (in-process callers without a principal) = trusted.
|
|
53
|
+
*/
|
|
54
|
+
canOperateEndpoint?: (adapter: string, endpointKey: string) => boolean;
|
|
43
55
|
workroomKnowledgeControl?: ConsoleWorkroomKnowledgeControlPort;
|
|
44
56
|
}
|
|
45
57
|
|
|
@@ -98,7 +110,49 @@ export interface ConsoleScheduleEngine {
|
|
|
98
110
|
listJobs(): Promise<Record<string, unknown>[]>;
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
|
|
113
|
+
/** `data` next to `error` carries a machine-readable code (e.g. `{ code: 'unsupported' }`). */
|
|
114
|
+
export type ExtendedRpcResult = { data: unknown } | { error: string; data?: unknown };
|
|
115
|
+
|
|
116
|
+
/** Structural LoginAssist port (host/http must not import `@zhin.js/core`). */
|
|
117
|
+
export interface ConsoleLoginAssistPort {
|
|
118
|
+
listPending(): readonly {
|
|
119
|
+
id: string;
|
|
120
|
+
adapter: string;
|
|
121
|
+
endpointKey: string;
|
|
122
|
+
endpointIdentity?: LoginEndpointIdentity;
|
|
123
|
+
type: string;
|
|
124
|
+
payload: Record<string, unknown>;
|
|
125
|
+
createdAt: number;
|
|
126
|
+
expiresAt?: number;
|
|
127
|
+
flowId?: string;
|
|
128
|
+
challengeId?: string;
|
|
129
|
+
}[];
|
|
130
|
+
submit(id: string, value: string | Record<string, unknown>, endpointIdentity?: LoginEndpointIdentity): boolean;
|
|
131
|
+
cancel(id: string, reason?: string, endpointIdentity?: LoginEndpointIdentity): boolean;
|
|
132
|
+
/** ADR 0058 */
|
|
133
|
+
listFlows?(): readonly ConsoleLoginFlow[];
|
|
134
|
+
getFlow?(adapter: string, endpointKey: string): ConsoleLoginFlow | undefined;
|
|
135
|
+
getFlowById?(id: string): ConsoleLoginFlow | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* Event-safe copy of a flow (core `withoutLoginMedia`): no QR image, sensitive blocks redacted,
|
|
138
|
+
* verification links dropped. Returned to readers without `endpoint.operate` on the flow's
|
|
139
|
+
* Endpoint; without it the host strips every challenge's media and blocks.
|
|
140
|
+
*/
|
|
141
|
+
eventSafeFlow?(flow: ConsoleLoginFlow): ConsoleLoginFlow;
|
|
142
|
+
submitAndWait?(
|
|
143
|
+
id: string,
|
|
144
|
+
value: string | Record<string, unknown>,
|
|
145
|
+
options?: { endpointIdentity?: LoginEndpointIdentity; timeoutMs?: number },
|
|
146
|
+
): Promise<{ accepted: boolean; outcome?: ConsoleLoginOutcome; pending?: boolean; flow?: ConsoleLoginFlow }>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface ConsoleEndpointLoginAccess {
|
|
150
|
+
readonly declaration?: ConsoleAdapterLoginDeclaration;
|
|
151
|
+
readonly control?: {
|
|
152
|
+
relogin?(request: { signal?: AbortSignal }): Promise<void>;
|
|
153
|
+
refreshChallenge?(request: { signal?: AbortSignal }): Promise<void>;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
102
156
|
|
|
103
157
|
const CRON_NOT_WIRED =
|
|
104
158
|
'持久化调度未接线:当前 Plugin Runtime ScheduleHost 仅支持插件注册的内存任务(list),' +
|
|
@@ -111,6 +165,11 @@ export interface ScheduleJobRow {
|
|
|
111
165
|
id: string;
|
|
112
166
|
cron: string;
|
|
113
167
|
description?: string;
|
|
168
|
+
/** Host display metadata (`ScheduleJobDisplay` in `@zhin.js/plugin-runtime`). */
|
|
169
|
+
title?: string;
|
|
170
|
+
titleI18n?: Readonly<Partial<Record<string, string>>>;
|
|
171
|
+
kind?: 'plugin-internal' | 'user-task';
|
|
172
|
+
lastRun?: { startedAt: number; finishedAt: number; status: 'succeeded' | 'failed'; error?: string };
|
|
114
173
|
}
|
|
115
174
|
|
|
116
175
|
export interface InboxSelection {
|
|
@@ -170,3 +229,11 @@ export interface EndpointManagementPort {
|
|
|
170
229
|
setGroupAdmin?(request: EndpointAdminCommand): Promise<void>;
|
|
171
230
|
deleteFriend?(request: EndpointUserCommand): Promise<void>;
|
|
172
231
|
}
|
|
232
|
+
|
|
233
|
+
/** `target` of a `schedule:list` persistent row (#1250). */
|
|
234
|
+
export interface ConsoleScheduleTarget {
|
|
235
|
+
readonly resolved: boolean;
|
|
236
|
+
readonly display?: string;
|
|
237
|
+
/** `missing_target` | `incomplete_target` | `endpoint_not_found` */
|
|
238
|
+
readonly reason?: string;
|
|
239
|
+
}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
actOnRequest, listInbox, listInboxMessages, listPendingRequests, listRecentInboxMessages,
|
|
5
5
|
mapNoticeRow, mapRequestRow, markInboxConsumed, TABLE_NOTICE, TABLE_REQUEST,
|
|
6
6
|
} from './inbox-rpc.js';
|
|
7
|
-
import { cancelLogin, listLoginPending, submitLogin } from './login-rpc.js';
|
|
7
|
+
import { cancelLogin, controlLogin, getLoginFlow, listLoginFlows, listLoginPending, submitLogin } from './login-rpc.js';
|
|
8
8
|
import {
|
|
9
9
|
deleteFriend, groupWriteOp, listChannels, listFriends, listGroupMembers, listGroups,
|
|
10
10
|
} from './endpoint-rpc.js';
|
|
@@ -21,7 +21,7 @@ export async function dispatchExtendedConsoleRpc(
|
|
|
21
21
|
switch (type) {
|
|
22
22
|
case 'schedule:list':
|
|
23
23
|
case 'cron:list':
|
|
24
|
-
return listSchedule(ctx);
|
|
24
|
+
return listSchedule(ctx, d);
|
|
25
25
|
|
|
26
26
|
case 'cron:validate':
|
|
27
27
|
return validateCron(d);
|
|
@@ -49,11 +49,18 @@ export async function dispatchExtendedConsoleRpc(
|
|
|
49
49
|
return actOnRequest(type, d, ctx);
|
|
50
50
|
|
|
51
51
|
case 'login.list':
|
|
52
|
-
return listLoginPending(ctx);
|
|
52
|
+
return listLoginPending(d, ctx);
|
|
53
53
|
case 'login.submit':
|
|
54
54
|
return submitLogin(d, ctx);
|
|
55
55
|
case 'login.cancel':
|
|
56
56
|
return cancelLogin(d, ctx);
|
|
57
|
+
case 'login.flow.list':
|
|
58
|
+
return listLoginFlows(d, ctx);
|
|
59
|
+
case 'login.flow.get':
|
|
60
|
+
return getLoginFlow(d, ctx);
|
|
61
|
+
case 'login.relogin':
|
|
62
|
+
case 'login.refresh':
|
|
63
|
+
return controlLogin(type, d, ctx);
|
|
57
64
|
|
|
58
65
|
case 'request.consumed':
|
|
59
66
|
return markInboxConsumed(d, ctx, TABLE_REQUEST);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ConsoleRpcExtendedCtx, EndpointManagementPort, ExtendedRpcResult } from './contracts.js';
|
|
2
|
+
import type { EndpointManagementCapability } from '@zhin.js/console-protocol';
|
|
2
3
|
import { boolField, numField, strField, withLiveEndpoint } from './rpc-values.js';
|
|
3
4
|
|
|
4
5
|
export async function listFriends(
|
|
@@ -12,7 +13,7 @@ export async function listFriends(
|
|
|
12
13
|
const friends = [...await management.listFriends()];
|
|
13
14
|
return { data: { friends, count: friends.length } };
|
|
14
15
|
}
|
|
15
|
-
return
|
|
16
|
+
return unsupportedList('friends', 'listFriends', endpoint.adapter, '好友列表');
|
|
16
17
|
});
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -27,7 +28,7 @@ export async function listGroups(
|
|
|
27
28
|
const groups = [...await management.listGroups()];
|
|
28
29
|
return { data: { groups, count: groups.length } };
|
|
29
30
|
}
|
|
30
|
-
return
|
|
31
|
+
return unsupportedList('groups', 'listGroups', endpoint.adapter, '群列表');
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -42,7 +43,7 @@ export async function listChannels(
|
|
|
42
43
|
const channels = [...await management.listChannels()];
|
|
43
44
|
return { data: { channels, count: channels.length } };
|
|
44
45
|
}
|
|
45
|
-
return
|
|
46
|
+
return unsupportedList('channels', 'listChannels', endpoint.adapter, '频道列表');
|
|
46
47
|
});
|
|
47
48
|
}
|
|
48
49
|
|
|
@@ -59,13 +60,36 @@ export async function listGroupMembers(
|
|
|
59
60
|
return withLiveEndpoint(ctx, adapter, endpointKey, async (management) => {
|
|
60
61
|
const method = management.listGroupMembers;
|
|
61
62
|
if (!method) {
|
|
62
|
-
return
|
|
63
|
+
return unsupportedList('members', 'listGroupMembers', adapter, '群成员列表');
|
|
63
64
|
}
|
|
64
65
|
const members = [...await method.call(management, { groupId })];
|
|
65
66
|
return { data: { members, count: members.length } };
|
|
66
67
|
});
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
/**
|
|
71
|
+
* A read the Adapter does not declare is not a failure (#1250): reply with an empty list plus
|
|
72
|
+
* `status: 'unsupported'` (`ConsoleEndpointCapabilityUnsupported`) instead of `{ error }`, so the
|
|
73
|
+
* Console can render "该平台不提供" rather than a red read error. Older clients still see an empty
|
|
74
|
+
* list with `count: 0`.
|
|
75
|
+
*/
|
|
76
|
+
function unsupportedList(
|
|
77
|
+
key: 'friends' | 'groups' | 'channels' | 'members',
|
|
78
|
+
capability: EndpointManagementCapability,
|
|
79
|
+
adapter: string,
|
|
80
|
+
label: string,
|
|
81
|
+
): ExtendedRpcResult {
|
|
82
|
+
return {
|
|
83
|
+
data: {
|
|
84
|
+
[key]: [],
|
|
85
|
+
count: 0,
|
|
86
|
+
status: 'unsupported',
|
|
87
|
+
capability,
|
|
88
|
+
message: `当前适配器(${adapter})不支持${label}`,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
69
93
|
// ---------------------------------------------------------------- 群管/好友写操作
|
|
70
94
|
|
|
71
95
|
interface GroupWriteSpec {
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export type {
|
|
2
2
|
ConsoleRpcExtendedCtx,
|
|
3
3
|
ConsoleScheduleEngine,
|
|
4
|
+
ConsoleScheduleTarget,
|
|
4
5
|
ConsoleWorkroomKnowledgeControlPort,
|
|
5
6
|
ConsoleWorkroomProfileControlPort,
|
|
6
7
|
ExtendedRpcResult,
|
|
8
|
+
ConsoleLoginAssistPort,
|
|
9
|
+
ConsoleEndpointLoginAccess,
|
|
7
10
|
} from './contracts.js';
|
|
11
|
+
export { loginAuditDetail, LOGIN_SUBMIT_WAIT_MS } from './login-rpc.js';
|
|
8
12
|
export { dispatchExtendedConsoleRpc } from './dispatcher.js';
|