@mangoc/dsh-sub2api-sync 0.0.0-stage → 0.1.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 +155 -2
- package/README.zh.md +124 -0
- package/cordis.patch.yml +10 -0
- package/data/models-dev.snapshot.json +1 -0
- package/lib/client.cjs +2416 -0
- package/lib/client.d.ts +49 -0
- package/lib/client.js +2425 -0
- package/lib/config.d.ts +89 -0
- package/lib/engine.d.ts +129 -0
- package/lib/enrich.d.ts +104 -0
- package/lib/icon.d.ts +50 -0
- package/lib/index.d.ts +31 -0
- package/lib/index.js +2836 -0
- package/lib/oauth.d.ts +128 -0
- package/lib/panel.d.ts +203 -0
- package/lib/protocols.d.ts +119 -0
- package/lib/routes.d.ts +263 -0
- package/lib/types.d.ts +130 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +124 -3
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plugin's own settings section (`sub2api-sync` namespace): types and the
|
|
3
|
+
* schemastery schema the host projects into the settings page.
|
|
4
|
+
*
|
|
5
|
+
* rev.4 shape (no legacy compatibility — the project has not shipped):
|
|
6
|
+
* - `selectedKeys` lists the relay keys checked for sync (default `[]` — 全不选);
|
|
7
|
+
* - one provider route per selected key (`sub2api-<10-hex>` slug over
|
|
8
|
+
* instance + key id — no sequential ids in route or credential names),
|
|
9
|
+
* written by the card and by the engine's keyId persistence;
|
|
10
|
+
* - `manualGroups` (hand-declared Mode B groups) is retired — groups come
|
|
11
|
+
* from the panel, and platform is read directly from the panel DTO;
|
|
12
|
+
* - `providerNameTemplate` renders the provider displayName (C5: must keep
|
|
13
|
+
* the `{keyName}` or `{siteName}` placeholder);
|
|
14
|
+
* - `allowRemoteActions` is the cross-device escape hatch (§11 of the
|
|
15
|
+
* client-UI design note).
|
|
16
|
+
*
|
|
17
|
+
* Since dsh 0.1.7 the `Config` export is auto-projected — there is no
|
|
18
|
+
* `installSection` registration any more. Every top-level field is marked
|
|
19
|
+
* `volatile`, which is what makes the entry appear as a settings form at
|
|
20
|
+
* all: the host keeps each volatile field as a live cell, so a form write
|
|
21
|
+
* updates the running plugin in place (no remount) and the entry's resolved
|
|
22
|
+
* config is the single source of truth.
|
|
23
|
+
*
|
|
24
|
+
* @module sub2api-sync/config
|
|
25
|
+
*/
|
|
26
|
+
import z from '@deepseek-ai/schemastery';
|
|
27
|
+
import type { ModelTemplate } from './protocols.ts';
|
|
28
|
+
./protocols.jsApiProtocol } from './types.ts';
|
|
29
|
+
/** ./types.jskey-source options; `keyId` absent = 自动(组名建/复用). */
|
|
30
|
+
export interface Sub2apiSyncConfig {
|
|
31
|
+
/** sub2api instance address, with or without a trailing `/v1`. */
|
|
32
|
+
baseURL?: string;
|
|
33
|
+
/** Panel account email (Mode A, password/2FA channel). */
|
|
34
|
+
email?: string;
|
|
35
|
+
/** Provider displayName template; `{keyName}` and `{siteName}` placeholders. Default `{siteName}({keyName})`. */
|
|
36
|
+
providerNameTemplate?: string;
|
|
37
|
+
/** Relay keys selected for sync (panel key ids); default `[]` — 全不选. */
|
|
38
|
+
selectedKeys?: number[];
|
|
39
|
+
/** Manual model lists when `/v1/models` is unavailable for a group. */
|
|
40
|
+
modelListOverrides?: Record<string, string[]>;
|
|
41
|
+
/** Per-group wire-protocol override over the panel platform mapping. */
|
|
42
|
+
protocolOverrides?: Record<string, ApiProtocol>;
|
|
43
|
+
/** Registered route → panel key id, written by the engine so the card's
|
|
44
|
+
* per-route remove button works without a panel round trip (slugs hide
|
|
45
|
+
* the numeric id by design). */
|
|
46
|
+
routeKeyIds?: Record<string, number>;
|
|
47
|
+
/** Per-model template inherited by every synced model entry. */
|
|
48
|
+
compatTemplate?: ModelTemplate;
|
|
49
|
+
/** models.dev metadata enrichment toggle (reserved, C4). */
|
|
50
|
+
enrichModelMeta?: boolean;
|
|
51
|
+
/** Optional quota (USD) for keys the plugin creates. */
|
|
52
|
+
keyQuota?: number;
|
|
53
|
+
/** Optional expiry in days for keys the plugin creates. */
|
|
54
|
+
keyExpiresInDays?: number;
|
|
55
|
+
/** Whether removal also revokes the remote key (needs the stored keyId). */
|
|
56
|
+
deleteOrphanKeys?: boolean;
|
|
57
|
+
/** Scheduled resync interval; absent = off. */
|
|
58
|
+
syncIntervalMs?: number;
|
|
59
|
+
/** Escape hatch: allow action routes from private-network peers (§11). Default false. */
|
|
60
|
+
allowRemoteActions?: boolean;
|
|
61
|
+
}
|
|
62
|
+
export declare const Config: z<Sub2apiSyncConfig, any>;
|
|
63
|
+
/** The default provider-name template — owned by `types.ts` (browser-safe), re-exported here. */
|
|
64
|
+
export { DEFAULT_PROVIDER_NAME_TEMPLATE } from './types.ts';
|
|
65
|
+
/**
|
|
66
|
+
* One resolved volatile field:./types.jsreference whose `get()` yields the
|
|
67
|
+
* current immutable snapshot. Structurally `cosmokit`'s `Volatile<T>`; kept
|
|
68
|
+
* local so this package need not depend on it.
|
|
69
|
+
*/
|
|
70
|
+
export interface LiveField<T> {
|
|
71
|
+
get(): T;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The entry config `apply` receives: every schema field resolves to a live
|
|
75
|
+
* cell the host swaps in place on each settings-form write. Keep the
|
|
76
|
+
* reference (re-reading cells per call), never cache a snapshot across a
|
|
77
|
+
* write boundary.
|
|
78
|
+
*/
|
|
79
|
+
export type ResolvedSub2apiSyncConfig = {
|
|
80
|
+
[K in keyof Sub2apiSyncConfig]: LiveField<Sub2apiSyncConfig[K]>;
|
|
81
|
+
};
|
|
82
|
+
/** Fold the live entry config into one plain snapshot for a sync run. */
|
|
83
|
+
export declare function readConfig(config: ResolvedSub2apiSyncConfig): Sub2apiSyncConfig;
|
|
84
|
+
/**
|
|
85
|
+
* Cross-field constraints the schema cannot express. Shape-only on purpose:
|
|
86
|
+
* reachability needs network I/O and belongs to the sync path, which fails
|
|
87
|
+
* loudly with the instance named.
|
|
88
|
+
*/
|
|
89
|
+
export declare function validateConfig(config: Sub2apiSyncConfig): void;
|
package/lib/engine.d.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sync engine: diff the panel group state against the plugin's entries
|
|
3
|
+
* in the settings user layer and apply a minimal write plan to
|
|
4
|
+
* `llm-pi-ai.providers` through `settings.mutate`.
|
|
5
|
+
*
|
|
6
|
+
* rev.4 model: the panel is the single source of groups (id/name/platform),
|
|
7
|
+
* reached through the credential-backed {@link PanelGateway}. Key sources
|
|
8
|
+
* per group (three states):
|
|
9
|
+
* 1. `groups[gid].keyId` set — reuse that exact panel key (persisted by the
|
|
10
|
+
* card's "选已有 key" or by a previous auto-creation);
|
|
11
|
+
* 2. `keyId` unset but a credential exists — pasted key, used verbatim (its
|
|
12
|
+
* panel name is matched best-effort for the display name);
|
|
13
|
+
* 3. neither — auto: reuse the group-named key, else create one named after
|
|
14
|
+
* the group, then persist the keyId (rename-proof idempotency).
|
|
15
|
+
*
|
|
16
|
+
* Guarantees (per the design Agent Note, carried over):
|
|
17
|
+
* - idempotent: a second sync with unchanged inputs produces zero writes;
|
|
18
|
+
* - scoped: only `sub2api-<10-hex>` (and legacy `sub2api-<digits>`) routes
|
|
19
|
+
* are ever written or removed;
|
|
20
|
+
* - optimistic: writes carry `expectedRevision` and a `SettingsConflictError`
|
|
21
|
+
* triggers a re-read, a recompute, and a bounded retry (3 attempts);
|
|
22
|
+
* - single flight: syncs are serialized on one queue;
|
|
23
|
+
* - loud: every per-group failure lands in the report, whole-sync failures
|
|
24
|
+
* throw with context, and nothing writes a half state.
|
|
25
|
+
*
|
|
26
|
+
* @module sub2api-sync/engine
|
|
27
|
+
*/
|
|
28
|
+
import type { PanelGateway, PanelKey } from './panel.ts';./panel.jsloadModelIndex } from './enrich.ts'./enrich.jspe { Sub2apiSyncConfig } from './config.ts'./config.jstype CredentialsLike, type SettingsLike } from './types.ts';./types.jsettings namespace owned by dsh-llm-pi-ai. */
|
|
29
|
+
export declare const PI_AI_NS = "llm-pi-ai";
|
|
30
|
+
/** The settings namespace owned by this plugin. */
|
|
31
|
+
export declare const OWN_NS = "sub2api-sync";
|
|
32
|
+
/** Minimal structural view of the settings service the engine needs. */
|
|
33
|
+
export type { SettingsLike, SettingsPathOp } from './types.ts';./types.jsterface SyncEngineOptions {
|
|
34
|
+
settings: SettingsLike;
|
|
35
|
+
credentials: CredentialsLike;
|
|
36
|
+
/** Credential-backed panel gateway (login tokens, single-flight refresh). */
|
|
37
|
+
gateway: PanelGateway;
|
|
38
|
+
fetchImpl?: typeof fetch;
|
|
39
|
+
/** Model-metadata index loader (C4); overridable in tests. */
|
|
40
|
+
loadModelIndex?: typeof loadModelIndex;
|
|
41
|
+
/**
|
|
42
|
+
* The instance's configured site name for the `{siteName}` template
|
|
43
|
+
* placeholder (cached probe; absent/unknown falls back to "sub2api").
|
|
44
|
+
*/
|
|
45
|
+
siteName?: () => Promise<string | undefined>;
|
|
46
|
+
}
|
|
47
|
+
export type SyncAction = 'created' | 'updated' | 'unchanged' | 'failed';
|
|
48
|
+
export interface SyncEntryOutcome {
|
|
49
|
+
groupId: number;
|
|
50
|
+
route: string;
|
|
51
|
+
/** The panel key id this entry synced (absent for stale/invalid ids). */
|
|
52
|
+
keyId?: number;
|
|
53
|
+
displayName?: string;
|
|
54
|
+
action: SyncAction;
|
|
55
|
+
models?: number;
|
|
56
|
+
error?: string;
|
|
57
|
+
/** Set when the selected id does not exist in the panel (stale selection). */
|
|
58
|
+
invalid?: true;
|
|
59
|
+
}
|
|
60
|
+
export interface SyncReport {
|
|
61
|
+
entries: SyncEntryOutcome[];
|
|
62
|
+
removed: string[];
|
|
63
|
+
/** Number of `set`/`unset` ops the accepted mutate carried. */
|
|
64
|
+
writes: number;
|
|
65
|
+
/** How many mutate attempts were used (1 = no conflict). */
|
|
66
|
+
attempts: number;
|
|
67
|
+
warnings: string[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* In-memory holder for the last accepted sync report (the status
|
|
71
|
+
* projection's `lastReport` / `lastReportAt`). The host clears it wherever
|
|
72
|
+
* the sync state is purged — logout, definitive auth loss, instance
|
|
73
|
+
* switch — because a report describing routes that no longer exist must
|
|
74
|
+
* not keep presenting itself as the current state.
|
|
75
|
+
*/
|
|
76
|
+
export interface SyncReportStore {
|
|
77
|
+
record(report: SyncReport): void;
|
|
78
|
+
clear(): void;
|
|
79
|
+
report(): SyncReport | undefined;
|
|
80
|
+
at(): number | undefined;
|
|
81
|
+
}
|
|
82
|
+
export declare function createSyncReportStore(): SyncReportStore;
|
|
83
|
+
/** Whole-sync failure: bad config, not signed in, llm-pi-ai unavailable, or retries exhausted. */
|
|
84
|
+
export declare class Sub2apiSyncError extends Error {
|
|
85
|
+
constructor(message: string);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve the selected relay keys against the panel's key rows. A selected id
|
|
89
|
+
* the panel no longer serves becomes an `invalid` entry (reported, not fatal).
|
|
90
|
+
*/
|
|
91
|
+
export declare function resolveTargets(config: Sub2apiSyncConfig, keyRows: readonly PanelKey[]): Array<{
|
|
92
|
+
key: PanelKey;
|
|
93
|
+
} | {
|
|
94
|
+
invalid: number;
|
|
95
|
+
error: string;
|
|
96
|
+
}>;
|
|
97
|
+
export declare class Sub2apiSyncEngine {
|
|
98
|
+
private readonly settings;
|
|
99
|
+
private readonly credentials;
|
|
100
|
+
private readonly gateway;
|
|
101
|
+
private readonly fetchImpl?;
|
|
102
|
+
private readonly modelIndexLoader;
|
|
103
|
+
private readonly siteName?;
|
|
104
|
+
private queue;
|
|
105
|
+
private running;
|
|
106
|
+
constructor(options: SyncEngineOptions);
|
|
107
|
+
/** Whether a sync is currently executing (UI busy marker). */
|
|
108
|
+
isRunning(): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Run one sync. At most one runs at a time — later callers queue behind
|
|
111
|
+
* the previous one (the settings-card button, the tool, and the timer all
|
|
112
|
+
* share this queue).
|
|
113
|
+
*/
|
|
114
|
+
sync(config: Sub2apiSyncConfig, keyIds?: number[]): Promise<SyncReport>;
|
|
115
|
+
private runSync;
|
|
116
|
+
private syncNow;
|
|
117
|
+
/** Protocol + models + naming for one selected key; the plaintext comes
|
|
118
|
+
* from the panel row when present, else from the stored credential. */
|
|
119
|
+
private buildKeyProfile;
|
|
120
|
+
/**
|
|
121
|
+
* Model source precedence: a non-empty `modelListOverrides` entry is the
|
|
122
|
+
* user's explicit selection from the card picker and wins outright (no
|
|
123
|
+
* fetch — the entry contains exactly the chosen ids). Otherwise discover
|
|
124
|
+
* via `/v1/models`; discovery failure falls back to the overrides loudly.
|
|
125
|
+
*/
|
|
126
|
+
private discoverModels;
|
|
127
|
+
private readCurrentProviders;
|
|
128
|
+
private revisionOf;
|
|
129
|
+
}
|
package/lib/enrich.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-metadata enrichment (C4): fills per-model facts the sub2api gateway
|
|
3
|
+
* never reports — context window, max output tokens, image input, and the
|
|
4
|
+
* reasoning-effort levels (`reasoning_options` → pi-ai `reasoningEfforts`) —
|
|
5
|
+
* from the models.dev catalog (https://models.dev/api.json).
|
|
6
|
+
*
|
|
7
|
+
* Source chain, in order:
|
|
8
|
+
* 1. a fresh catalog fetch (only when `enrichModelMeta` is on), cached
|
|
9
|
+
* in-process for 24h;
|
|
10
|
+
* 2. the bundled snapshot `data/models-dev.snapshot.json` (generated by
|
|
11
|
+
* `scripts/fetch-models-dev.mjs`, shipped with the package) — the
|
|
12
|
+
* deterministic offline fallback;
|
|
13
|
+
* 3. nothing — the caller keeps its route-level defaults.
|
|
14
|
+
*
|
|
15
|
+
* A failed fetch degrades to the bundled snapshot (and is cached as such for
|
|
16
|
+
* the TTL, so an unreachable models.dev is not hammered on every sync).
|
|
17
|
+
* Enrichment never fails a sync.
|
|
18
|
+
*
|
|
19
|
+
* @module sub2api-sync/enrich
|
|
20
|
+
*/
|
|
21
|
+
/** Per-model capacity facts the enrichment can contribute. */
|
|
22
|
+
export interface ModelMeta {
|
|
23
|
+
/** Display name for selectors (models.dev `name`), e.g. "Claude Opus 4.5". */
|
|
24
|
+
name?: string;
|
|
25
|
+
contextWindow?: number;
|
|
26
|
+
maxTokens?: number;
|
|
27
|
+
imageInput?: boolean;
|
|
28
|
+
reasoningModel?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* pi-ai `reasoningEfforts` map derived from the models.dev `reasoning_options`
|
|
31
|
+
* effort values: keys are pi-ai thinking levels, values the wire spelling
|
|
32
|
+
* (same-name passthrough; `none` maps to `off: null` — dispatch sends nothing).
|
|
33
|
+
* Absent for toggle/budget-only reasoning, whose wire shape is not an effort
|
|
34
|
+
* string and cannot travel through `reasoningEfforts`.
|
|
35
|
+
*/
|
|
36
|
+
reasoningEfforts?: Record<string, string | null>;
|
|
37
|
+
}
|
|
38
|
+
/** Model id (lowercase) → metadata. */
|
|
39
|
+
export type ModelMetaIndex = Map<string, ModelMeta>;
|
|
40
|
+
/**
|
|
41
|
+
* pi-ai's `THINKING_LEVELS` vocabulary (dsh-llm-pi-ai: off/minimal/low/medium/
|
|
42
|
+
* high/xhigh/max) extended with models.dev's `none` spelling for `off`. models.dev
|
|
43
|
+
* effort values outside this set cannot travel through `reasoningEfforts` and are
|
|
44
|
+
* dropped. Canonical order doubles as the output order. Exported for the
|
|
45
|
+
* snapshot generator script (`scripts/fetch-models-dev.mjs`), which mirrors
|
|
46
|
+
* {@link parseCatalog}'s efforts transform.
|
|
47
|
+
*/
|
|
48
|
+
export declare const EFFORT_VOCABULARY: readonly ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
49
|
+
/**
|
|
50
|
+
* models.dev provider keys that publish the upstream's own effort surface —
|
|
51
|
+
* vendor consoles and vendor-governed cloud hosting. Aggregator entries invent
|
|
52
|
+
* richer vocabularies than the upstream accepts (claude with `xhigh`/`max`,
|
|
53
|
+
* glm-5.2 with seven levels), and a synced group forwards to the vendor
|
|
54
|
+
* upstream, so when any first-party entry exists for an id it wins outright and
|
|
55
|
+
* aggregator effort data is ignored; without one, aggregator sources intersect
|
|
56
|
+
* (a level every source claims — never more than any one of them offers).
|
|
57
|
+
* Exported for the snapshot generator script (single source of truth with
|
|
58
|
+
* {@link parseCatalog}).
|
|
59
|
+
*/
|
|
60
|
+
export declare const FIRST_PARTY_PROVIDERS: Set<string>;
|
|
61
|
+
/**
|
|
62
|
+
* Normalize one models.dev `name` into a display name: strip the trailing
|
|
63
|
+
* " (latest)" marker (a models.dev family-latest convention, not part of the
|
|
64
|
+
* model's name) and reject empty results or names that merely repeat the id —
|
|
65
|
+
* a display name identical to the id carries no information and the UI keeps
|
|
66
|
+
* its own placeholder in that case. Exported for the snapshot generator
|
|
67
|
+
* script (single source of truth with {@link parseCatalog}).
|
|
68
|
+
*/
|
|
69
|
+
export declare function displayNameOf(raw: unknown, id?: string): string | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Parse the bundled tuple snapshot:
|
|
72
|
+
* `{ generatedAt, source, models: { [id]: [context, output, image, reasoning, efforts?, name?] } }`
|
|
73
|
+
* — the fifth element (optional, 4-tuples predate the efforts enrichment) is the
|
|
74
|
+
* normalized effort-level array the generator script resolved, the sixth
|
|
75
|
+
* (optional) the display name.
|
|
76
|
+
*/
|
|
77
|
+
export declare function parseSnapshot(raw: string): ModelMetaIndex;
|
|
78
|
+
/**
|
|
79
|
+
* Parse the live models.dev catalog: `{ [provider]: { models: { [id]: {
|
|
80
|
+
* name?, limit?: { context?, output? }, modalities?: { input?: string[] },
|
|
81
|
+
* attachment?: boolean, reasoning?: boolean, reasoning_options?: Array<{
|
|
82
|
+
* type: 'effort' | 'toggle' | 'budget_tokens', values?: string[] }> } } } }`.
|
|
83
|
+
* Capacity facts merge across providers with max/OR; effort levels follow the
|
|
84
|
+
* first-party preference (see {@link FIRST_PARTY_PROVIDERS}): first-party
|
|
85
|
+
* sources win outright, otherwise aggregator sources intersect. Display names
|
|
86
|
+
* also prefer first-party sources (see the tail of this function).
|
|
87
|
+
*/
|
|
88
|
+
export declare function parseCatalog(catalog: unknown): ModelMetaIndex;
|
|
89
|
+
/** The bundled offline snapshot, read once per process. */
|
|
90
|
+
export declare function bundledIndex(): ModelMetaIndex;
|
|
91
|
+
/**
|
|
92
|
+
* Resolve the index for one sync run. With `enrich` on, a fresh fetch is
|
|
93
|
+
* attempted and cached for 24h (failures degrade to the bundled snapshot and
|
|
94
|
+
* are cached for the same TTL so a dead endpoint is not retried per sync);
|
|
95
|
+
* with `enrich` off, only the bundled snapshot is used — deterministic and
|
|
96
|
+
* offline.
|
|
97
|
+
*/
|
|
98
|
+
export declare function loadModelIndex(options: {
|
|
99
|
+
enrich: boolean;
|
|
100
|
+
fetchImpl?: typeof fetch;
|
|
101
|
+
now?: () => number;
|
|
102
|
+
}): Promise<ModelMetaIndex>;
|
|
103
|
+
/** Test/reset hook: forget the in-process caches. */
|
|
104
|
+
export declare function resetCaches(): void;
|
package/lib/icon.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instance icon + site-name discovery and the icon byte proxy (host half only).
|
|
3
|
+
*
|
|
4
|
+
* The settings card shows the instance's own icon as the identity mark and
|
|
5
|
+
* its configured site name in the header once signed in. One probe of the
|
|
6
|
+
* instance root HTML serves both: parse `<link rel="icon">` (sub2api
|
|
7
|
+
* declares `/logo.svg` there) and `<title>` (sub2api's backend rewrites it
|
|
8
|
+
* to `{site_name} - AI API Gateway`, so the title carries the operator's
|
|
9
|
+
* configured site name). Undeclared icons fall back to the `/favicon.ico`
|
|
10
|
+
* convention. The bytes are proxied through a same-origin plugin route so
|
|
11
|
+
* the webview never loads cross-origin imagery (no CORS/mixed-content
|
|
12
|
+
* caveats). A per-instance cache (10 min TTL) keeps repeat polls off the
|
|
13
|
+
* instance's HTML.
|
|
14
|
+
*
|
|
15
|
+
* @module sub2api-sync/icon
|
|
16
|
+
*/
|
|
17
|
+
/** Icon bytes handed to the /icon route (content-type is upstream's or guessed). */
|
|
18
|
+
export interface IconPayload {
|
|
19
|
+
contentType: string;
|
|
20
|
+
body: Buffer;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Extract the first usable icon href from an HTML document. Only the
|
|
24
|
+
* `<link>` shape is honored (the standard mechanism); `mask-icon` is skipped
|
|
25
|
+
* (Safari pinned-tab monochrome glyphs make poor avatars), everything else
|
|
26
|
+
* whose `rel` contains the `icon` word qualifies. Minimal entity decoding
|
|
27
|
+
* for the one entity HTML attribute values practically carry.
|
|
28
|
+
*/
|
|
29
|
+
export declare function pickIconHref(html: string): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Extract the site name from an HTML `<title>`: decode the handful of
|
|
32
|
+
* entities title text practically carries, trim, and strip sub2api's fixed
|
|
33
|
+
* ` - AI API Gateway` suffix (case-insensitive) to surface the operator's
|
|
34
|
+
* configured site name. Absent or empty titles yield undefined.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseSiteTitle(html: string): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Build the instance probe the route deps expose: `icon` proxies the icon
|
|
39
|
+
* bytes for the /icon route, `siteName` serves the parsed title for the
|
|
40
|
+
* /site route. One factory per plugin instance — the meta cache lives in
|
|
41
|
+
* the closure, so the icon and site-name consumers share one HTML read per
|
|
42
|
+
* TTL window. Errors never throw — an unavailable answer resolves to
|
|
43
|
+
* `undefined` and the client keeps its fallbacks.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createInstanceProbe(options?: {
|
|
46
|
+
fetchImpl?: typeof fetch;
|
|
47
|
+
}): {
|
|
48
|
+
icon: (instance: string) => Promise<IconPayload | undefined>;
|
|
49
|
+
siteName: (instance: string) => Promise<string | undefined>;
|
|
50
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sub2api sync plugin — host half.
|
|
3
|
+
*
|
|
4
|
+
* rev.4: the panel is the single source of groups. Login channels are
|
|
5
|
+
* password(+2FA) / OAuth (OAuth routes deferred until the sub2api
|
|
6
|
+
* side lands); configuration reads/writes ride the platform settings seam
|
|
7
|
+
* (`configForms` in the browser), so the host routes carry only actions,
|
|
8
|
+
* secrets, and state projection.
|
|
9
|
+
*
|
|
10
|
+
* The settings section needs no registration since dsh 0.1.7: the `Config`
|
|
11
|
+
* export is auto-projected into the settings page, and every field arrives
|
|
12
|
+
* here as a live volatile cell (`ResolvedSub2apiSyncConfig`), so the entry's
|
|
13
|
+
* resolved config is the authoritative source — the loader swaps cell values
|
|
14
|
+
* in place on each form write and announces them via `loader/volatile-update`.
|
|
15
|
+
*
|
|
16
|
+
* @module sub2api-sync
|
|
17
|
+
*/
|
|
18
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
19
|
+
import { type ResolvedSub2apiSyncConfig } from './co./config.jseclare module '@deepseek-ai/cordis' {
|
|
20
|
+
interface Events {
|
|
21
|
+
/**
|
|
22
|
+
* Mirrors `@deepseek-ai/cordis-plugin-loader`'s declaration (not a
|
|
23
|
+
* dependency here): the loader announces an in-place volatile config
|
|
24
|
+
* commit for this plugin's entry with the changed field paths.
|
|
25
|
+
*/
|
|
26
|
+
'loader/volatile-update'(paths: readonly (readonly string[])[]): void;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export { Config, validateConfig, DEFAULT_PROVIDER_NAME_TEMPLATE } from './co./config.jsxport { Sub2apiSyncEngine, Sub2apiSyncError, OWN_NS, PI_AI_NS } from './en./engine.jsxport { PanelClient, PanelGateway, PanelError } from './pa./panel.jsxport { buildProviderProfile, baseURLForProtocol, protocolForPlatform, guessPlatformFromModels, renderProviderName, SUB2API_RETRY_POLICY } from './pr./protocols.jsxport { API_PROTOCOLS, PLATFORMS, instanceKey, isOwnedRoute, routeForKey, routeKeyRef, legacyKeyRef, legacyKeyIdOfRoute, panelTokenRef, panelRefreshRef, oauthTokenRef, oauthRefreshRef, panelPasswordRef, parseGroupId, } from './ty./types.jsxport { ROUTES, trustedRequest, callbackLangOf } from './ro./routes.jsxport { OAUTH_CALLBACK_PATH, OAUTH_CLIENT_ID, OAuthAttemptStore, OAuthError, buildAuthorizeUrl, callbackPageHtml, createPkcePair, discoverOAuthEndpoints, exchangeAuthorizationCode, loopbackCallbackOrigin, oauthScopesFor, refreshAccessToken, revokeToken, type CallbackLang, } from './oa./oauth.jsxport declare const name: string;
|
|
30
|
+
export declare const inject: string[];
|
|
31
|
+
export declare function apply(ctx: Context, config: ResolvedSub2apiSyncConfig): void;
|