@huanlin/dsh-plugin-copilot 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/README.md +95 -0
- package/cordis.patch.yml +17 -0
- package/lib/client.js +788 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +553 -0
- package/lib/invariant.js +23 -0
- package/lib/types/client/CopilotAuthCard.d.ts +16 -0
- package/lib/types/client/bindSnapshotSelector.d.ts +7 -0
- package/lib/types/client/controller.d.ts +98 -0
- package/lib/types/client/index.d.ts +24 -0
- package/lib/types/client/locales.d.ts +13 -0
- package/lib/types/gateway.d.ts +102 -0
- package/lib/types/index.d.ts +53 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/status.d.ts +82 -0
- package/lib/types/tools.d.ts +15 -0
- package/package.json +126 -0
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/invariant.ts
|
|
2
|
+
const PACKAGE_NAME = "@huanlin/dsh-plugin-copilot";
|
|
3
|
+
/** Cordis companion plugin name. */
|
|
4
|
+
const name = "dsh-plugin-copilot-invariant";
|
|
5
|
+
/** Service required before the companion can reserve package ownership. */
|
|
6
|
+
const inject = ["invariants"];
|
|
7
|
+
/**
|
|
8
|
+
* No runtime invariant: the gateway route, the settings-namespace
|
|
9
|
+
* registration, and the `copilot_status` tool registration are cordis
|
|
10
|
+
* effect contributions auto-disposed with the plugin fiber. The gateway's
|
|
11
|
+
* in-flight login slot is intentionally non-durable — a dropped page just
|
|
12
|
+
* re-polls, and a half-finished device flow holds no authority.
|
|
13
|
+
*/
|
|
14
|
+
const install = () => {};
|
|
15
|
+
/**
|
|
16
|
+
* Register this package's invariant companion.
|
|
17
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
18
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
19
|
+
*/
|
|
20
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
+
import type { CopilotCardState } from './controller.ts';
|
|
3
|
+
import type { CopilotAuthController } from './controller.ts';
|
|
4
|
+
/** Inject face: the shared controller. */
|
|
5
|
+
export interface CopilotCardInjected {
|
|
6
|
+
readonly controller: CopilotAuthController;
|
|
7
|
+
readonly useCard: <S>(select: (state: CopilotCardState) => S) => S;
|
|
8
|
+
}
|
|
9
|
+
/** Full props: locale seat + inject. */
|
|
10
|
+
export type CopilotCardProps = PropsLocale<'dsh-plugin-copilot'> & CopilotCardInjected;
|
|
11
|
+
/**
|
|
12
|
+
* Render the Copilot onboarding card.
|
|
13
|
+
* @param props - locale + controller inject.
|
|
14
|
+
* @returns a `<li>` card element.
|
|
15
|
+
*/
|
|
16
|
+
export declare function CopilotAuthCard({ t, controller, useCard }: CopilotCardProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HostObservable, SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
+
/**
|
|
3
|
+
* Bind a React selector hook to a {@link HostObservable} snapshot source.
|
|
4
|
+
* @param source - the observable snapshot store.
|
|
5
|
+
* @returns a `useSelector(sel, eq?)` hook.
|
|
6
|
+
*/
|
|
7
|
+
export declare function bindSnapshotSelector<T>(source: HostObservable<T>): SnapshotSelectorHook<T>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `CopilotAuthController` — client-side state machine for the Copilot card.
|
|
3
|
+
*
|
|
4
|
+
* Drives the host's `/copilot/api` gateway: one `status` load, a login flow
|
|
5
|
+
* that starts the attempt and then polls the sequenced `events` stream
|
|
6
|
+
* (device-code notices, progress, prompts to answer) until the attempt
|
|
7
|
+
* settles, and logout / autofill actions. The store is the single render
|
|
8
|
+
* source; the card is a pure projection of it.
|
|
9
|
+
*
|
|
10
|
+
* @module @huanlin/dsh-plugin-copilot/client/controller
|
|
11
|
+
*/
|
|
12
|
+
import { type SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
13
|
+
import type { CopilotStatus } from '../status.ts';
|
|
14
|
+
/** One sequenced gateway event as the client consumes it. */
|
|
15
|
+
export interface CopilotClientEvent {
|
|
16
|
+
seq: number;
|
|
17
|
+
kind: 'notice' | 'prompt';
|
|
18
|
+
message?: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
code?: string;
|
|
21
|
+
prompt?: {
|
|
22
|
+
kind: 'text' | 'secret' | 'select';
|
|
23
|
+
message: string;
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
options?: readonly {
|
|
26
|
+
id: string;
|
|
27
|
+
label: string;
|
|
28
|
+
}[];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** The card's render state. */
|
|
32
|
+
export interface CopilotCardState {
|
|
33
|
+
/** Whether the first status read answered. */
|
|
34
|
+
loaded: boolean;
|
|
35
|
+
/** The joined host status. */
|
|
36
|
+
status: CopilotStatus;
|
|
37
|
+
/** Login lifecycle: idle / running / terminal outcome. */
|
|
38
|
+
login: {
|
|
39
|
+
kind: 'idle';
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'running';
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'success';
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'error';
|
|
46
|
+
message: string;
|
|
47
|
+
} | {
|
|
48
|
+
kind: 'cancelled';
|
|
49
|
+
};
|
|
50
|
+
/** Notices and the latest unanswered prompt, in arrival order. */
|
|
51
|
+
events: readonly CopilotClientEvent[];
|
|
52
|
+
/** The last device code shown (for the copy button). */
|
|
53
|
+
deviceCode: string | undefined;
|
|
54
|
+
/** The verification URL of the latest device-code notice. */
|
|
55
|
+
verificationUrl: string | undefined;
|
|
56
|
+
/** The prompt awaiting an answer, when one is open. */
|
|
57
|
+
openPrompt: CopilotClientEvent | undefined;
|
|
58
|
+
/** Copy button feedback. */
|
|
59
|
+
copied: boolean;
|
|
60
|
+
/** Action in flight (disables buttons). */
|
|
61
|
+
busy: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Controller managing the Copilot card lifecycle. Constructed once in the
|
|
65
|
+
* client `apply()`; polls only while a login attempt is running or the card
|
|
66
|
+
* holds an unanswered prompt.
|
|
67
|
+
*/
|
|
68
|
+
export declare class CopilotAuthController {
|
|
69
|
+
readonly store: SnapshotStore<CopilotCardState>;
|
|
70
|
+
private cursor;
|
|
71
|
+
private pollTimer;
|
|
72
|
+
private disposed;
|
|
73
|
+
constructor();
|
|
74
|
+
/** Load the joined status from the host. */
|
|
75
|
+
load(): Promise<void>;
|
|
76
|
+
/** Start a login attempt, then poll events until settlement. */
|
|
77
|
+
login(): Promise<void>;
|
|
78
|
+
/** Withdraw the running attempt. */
|
|
79
|
+
cancel(): Promise<void>;
|
|
80
|
+
/** Answer the open prompt. */
|
|
81
|
+
answer(answer: string, declined: boolean): Promise<void>;
|
|
82
|
+
/** Delete the stored credential record. */
|
|
83
|
+
logout(): Promise<void>;
|
|
84
|
+
/** Write the provider profile (idempotent on the host side). */
|
|
85
|
+
autofill(): Promise<void>;
|
|
86
|
+
/** Copy-button feedback. */
|
|
87
|
+
markCopied(): void;
|
|
88
|
+
/** Stop polling and further actions; called on fiber disposal. */
|
|
89
|
+
dispose(): void;
|
|
90
|
+
/** Fold one settlement into the card state and refresh the join. */
|
|
91
|
+
private finishLogin;
|
|
92
|
+
/** Poll the event stream once; reschedules while the attempt runs. */
|
|
93
|
+
private pollOnce;
|
|
94
|
+
/** Fold one sequenced event into the card state. */
|
|
95
|
+
private applyEvent;
|
|
96
|
+
/** Schedule the next poll, collapsing overlapping timers. */
|
|
97
|
+
private schedulePoll;
|
|
98
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-plugin-copilot — browser half.
|
|
3
|
+
*
|
|
4
|
+
* One registration: a `settings.plugin.item` card (key `dsh-plugin-copilot`)
|
|
5
|
+
* in the Plugins settings page, rendering the Copilot onboarding state
|
|
6
|
+
* machine (sign-in device-flow panel, route activation, sign-out) through
|
|
7
|
+
* the host's `/copilot/api` gateway.
|
|
8
|
+
*
|
|
9
|
+
* @module @huanlin/dsh-plugin-copilot/client
|
|
10
|
+
*/
|
|
11
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
12
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
13
|
+
interface LocaleNamespaceMap {
|
|
14
|
+
/** The Copilot onboarding card labels. */
|
|
15
|
+
'dsh-plugin-copilot': import('./locales.ts').CopilotKey;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Required services: the slot ledger and the locale dictionaries. */
|
|
19
|
+
export declare const inject: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Client plugin body: register the locale dictionaries and the settings card.
|
|
22
|
+
* @param ctx - client root context.
|
|
23
|
+
*/
|
|
24
|
+
export declare function apply(ctx: ClientContext): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locale dictionaries for the `dsh-plugin-copilot` card namespace.
|
|
3
|
+
*
|
|
4
|
+
* @module @huanlin/dsh-plugin-copilot/client/locales
|
|
5
|
+
*/
|
|
6
|
+
/** The locale keys the Copilot card reads. */
|
|
7
|
+
export type CopilotKey = 'card.title' | 'card.intro' | 'card.unsupported' | 'card.signedIn' | 'card.signedOut' | 'card.routeActive' | 'card.routeDormant' | 'card.models' | 'action.signIn' | 'action.signOut' | 'action.cancel' | 'action.retry' | 'action.activate' | 'action.syncModels' | 'action.openUrl' | 'action.copyCode' | 'action.copied' | 'action.decline' | 'action.submit' | 'state.pending' | 'state.pendingPrompt' | 'state.working' | 'state.success' | 'state.error' | 'notice.deviceCode' | 'notice.polling' | 'prompt.placeholder' | 'prompt.answer';
|
|
8
|
+
/** The locale namespace name; matches the `locale: NS` passed at slot register. */
|
|
9
|
+
export declare const NS = "dsh-plugin-copilot";
|
|
10
|
+
/** English dictionary. */
|
|
11
|
+
export declare const en: Record<CopilotKey, string>;
|
|
12
|
+
/** Chinese dictionary. */
|
|
13
|
+
export declare const zh: Record<CopilotKey, string>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side HTTP gateway exposing the Copilot onboarding face to the browser
|
|
3
|
+
* through a self-hosted `/copilot/api` route (same-origin only).
|
|
4
|
+
*
|
|
5
|
+
* Wire shape (POST, JSON envelope `{ ok, value | error }` like the
|
|
6
|
+
* sidebar-brand-text precedent):
|
|
7
|
+
* status → { flowAvailable, loggedIn, profileActivated, inFlight, models }
|
|
8
|
+
* login → long-polls one device-flow attempt; intermediate notices are
|
|
9
|
+
* polled via `events` (see below) — the request resolves only when
|
|
10
|
+
* the attempt settles (authorized / cancelled / error)
|
|
11
|
+
* cancel → withdraw the running attempt
|
|
12
|
+
* events → { since } → notices emitted after sequence `since`
|
|
13
|
+
* logout → delete the Copilot credential record
|
|
14
|
+
* autofill → idempotently write `llm-pi-ai.providers.github-copilot = {}`
|
|
15
|
+
*
|
|
16
|
+
* `login` cannot ride a single request/response cleanly because the device
|
|
17
|
+
* flow takes minutes, so the gateway keeps a bounded notice ring the client
|
|
18
|
+
* polls; the login request itself resolves at settlement.
|
|
19
|
+
*
|
|
20
|
+
* @module @huanlin/dsh-plugin-copilot/gateway
|
|
21
|
+
*/
|
|
22
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
23
|
+
import type { AuthorizationEntry, AuthorizationInteraction, AuthorizationPrompt } from '@deepseek-ai/dsh-authorization';
|
|
24
|
+
import type { CredentialKey } from '@deepseek-ai/dsh-credentials';
|
|
25
|
+
/** One notice as the browser consumes it. */
|
|
26
|
+
export interface CopilotNoticeEvent {
|
|
27
|
+
/** Monotonic sequence; the client polls with the last one it saw. */
|
|
28
|
+
seq: number;
|
|
29
|
+
/** Notice text. */
|
|
30
|
+
message: string;
|
|
31
|
+
/** Page the human must open, when the notice carries one. */
|
|
32
|
+
url?: string;
|
|
33
|
+
/** Code the human must enter there, when the notice carries one. */
|
|
34
|
+
code?: string;
|
|
35
|
+
}
|
|
36
|
+
/** One queued prompt question the browser must answer. */
|
|
37
|
+
export interface CopilotPromptEvent {
|
|
38
|
+
/** Monotonic sequence in the same stream as notices. */
|
|
39
|
+
seq: number;
|
|
40
|
+
/** Prompt shape: text / secret / select (see the authorization seam). */
|
|
41
|
+
prompt: AuthorizationPrompt;
|
|
42
|
+
}
|
|
43
|
+
/** Login call status: settlement or in-progress. */
|
|
44
|
+
export type LoginOutcome = {
|
|
45
|
+
status: 'authorized';
|
|
46
|
+
} | {
|
|
47
|
+
status: 'cancelled';
|
|
48
|
+
} | {
|
|
49
|
+
status: 'error';
|
|
50
|
+
message: string;
|
|
51
|
+
};
|
|
52
|
+
/** Host capabilities the gateway drives. */
|
|
53
|
+
export interface GatewayDeps {
|
|
54
|
+
/** The live flow registry listing. */
|
|
55
|
+
listFlows(): readonly AuthorizationEntry[];
|
|
56
|
+
/** Begin one authorization attempt. */
|
|
57
|
+
begin(request: {
|
|
58
|
+
key: CredentialKey;
|
|
59
|
+
interaction: AuthorizationInteraction;
|
|
60
|
+
signal?: AbortSignal;
|
|
61
|
+
}): Promise<{
|
|
62
|
+
status: 'authorized' | 'cancelled';
|
|
63
|
+
}>;
|
|
64
|
+
/** Withdraw the running attempt for a key. */
|
|
65
|
+
cancel(key: CredentialKey): void;
|
|
66
|
+
/** Record presence facts; undefined without a credential store. */
|
|
67
|
+
describeRecord(key: CredentialKey): Promise<{
|
|
68
|
+
configured: boolean;
|
|
69
|
+
kind?: string;
|
|
70
|
+
} | undefined>;
|
|
71
|
+
/** Delete the stored record; undefined without a credential store. */
|
|
72
|
+
deleteRecord(key: CredentialKey): Promise<void>;
|
|
73
|
+
/** The resolved `llm-pi-ai` section, or undefined when unregistered. */
|
|
74
|
+
settingsSection(): Record<string, unknown> | undefined;
|
|
75
|
+
/** The stored grant's usable model ids, or `undefined` when unknown. */
|
|
76
|
+
models(): Promise<readonly string[] | undefined>;
|
|
77
|
+
/**
|
|
78
|
+
* The installed pi-ai catalog models for the Copilot route, or `undefined`
|
|
79
|
+
* when the llm service is unavailable. The narrow step needs the catalog's
|
|
80
|
+
* ids: a profile `models` entry naming an id the catalog does not describe
|
|
81
|
+
* would refuse the whole route at write time, because the shipped Copilot
|
|
82
|
+
* models do not share one wire protocol to default it to.
|
|
83
|
+
*/
|
|
84
|
+
catalogModels(): Promise<readonly {
|
|
85
|
+
id: string;
|
|
86
|
+
}[] | undefined>;
|
|
87
|
+
/** Merge a patch into the `llm-pi-ai` user settings section. */
|
|
88
|
+
updateSettings(patch: Record<string, unknown>): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* The configured GitHub Enterprise domain, auto-answered for the Copilot
|
|
91
|
+
* flow's enterprise question; blank or undefined serves github.com.
|
|
92
|
+
*/
|
|
93
|
+
enterpriseDomain?: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Register the `/copilot/api` route.
|
|
97
|
+
*
|
|
98
|
+
* @param ctx - host context carrying `webServer`.
|
|
99
|
+
* @param deps - the host capabilities the route drives.
|
|
100
|
+
* @returns disposer removing the route.
|
|
101
|
+
*/
|
|
102
|
+
export declare function registerCopilotGateway(ctx: Context, deps: GatewayDeps): () => void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@huanlin/dsh-plugin-copilot` — Copilot onboarding layer.
|
|
3
|
+
*
|
|
4
|
+
* This plugin no longer registers a `github-copilot` provider or adapter:
|
|
5
|
+
* dsh 0.1.2-alpha.1's `dsh-llm-pi-ai` ships the pi-ai builtin catalog whose
|
|
6
|
+
* Copilot provider already does everything the 0.1.x adapter did (OAuth
|
|
7
|
+
* device-flow login, request headers, model catalog, three wire protocols),
|
|
8
|
+
* and declaring the same provider twice fails the whole profile boot with
|
|
9
|
+
* `DUPLICATE_DIRECTORY`. What the harness lacks is a way to *reach* that
|
|
10
|
+
* built-in login from the WebUI — that is this plugin's whole job now:
|
|
11
|
+
*
|
|
12
|
+
* - host half (this module): a `/copilot/api` HTTP gateway that proxies
|
|
13
|
+
* `ctx.authorization.begin()` onto the pi-ai Copilot flow, an idempotent
|
|
14
|
+
* settings autofill that writes `llm-pi-ai.providers.github-copilot = {}`
|
|
15
|
+
* (flipping the route from dormant to active), and a read-only
|
|
16
|
+
* `copilot_status` tool;
|
|
17
|
+
* - browser half (`src/client/`): a `settings.plugin.item` card in the
|
|
18
|
+
* Plugins settings page rendering the device-flow panel.
|
|
19
|
+
*
|
|
20
|
+
* @module @huanlin/dsh-plugin-copilot
|
|
21
|
+
*/
|
|
22
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
23
|
+
import { type SettingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
24
|
+
import z from 'schemastery';
|
|
25
|
+
export { registerCopilotGateway } from './gateway.ts';
|
|
26
|
+
export { registerCopilotTools } from './tools.ts';
|
|
27
|
+
export { COPILOT_PROVIDER, COPILOT_RECORD_KEY, COPILOT_SCOPE, COPILOT_SETTINGS_NS, findCopilotFlow, grantModelIds, joinStatus, recordAddress, } from './status.ts';
|
|
28
|
+
export type { CopilotStatus, StatusSources } from './status.ts';
|
|
29
|
+
export declare const name = "dsh-plugin-copilot";
|
|
30
|
+
export declare const inject: string[];
|
|
31
|
+
/**
|
|
32
|
+
* The plugin's own settings namespace: the card owns no configurable fields,
|
|
33
|
+
* but the flow's enterprise question is answered from here.
|
|
34
|
+
*/
|
|
35
|
+
export declare const CARD_NAMESPACE: SettingsNamespace;
|
|
36
|
+
/** Plugin config. */
|
|
37
|
+
export interface Config {
|
|
38
|
+
/**
|
|
39
|
+
* GitHub Enterprise domain (e.g. `company.ghe.com`) the gateway answers the
|
|
40
|
+
* Copilot flow's enterprise question with; blank serves github.com, which
|
|
41
|
+
* is why the question never reaches the card by default.
|
|
42
|
+
*/
|
|
43
|
+
enterpriseDomain: string;
|
|
44
|
+
}
|
|
45
|
+
export declare const Config: z<Config>;
|
|
46
|
+
/**
|
|
47
|
+
* Plugin body: register the card namespace, the HTTP gateway, and the
|
|
48
|
+
* status tool. Every host read goes through optional services (`ctx.get`)
|
|
49
|
+
* so a composition without the authorization or credentials seam still
|
|
50
|
+
* boots — the card then reports the missing pieces instead of failing load.
|
|
51
|
+
* @param ctx - host plugin context.
|
|
52
|
+
*/
|
|
53
|
+
export declare function apply(ctx: Context, config?: Config): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@huanlin/dsh-plugin-copilot`.
|
|
3
|
+
*
|
|
4
|
+
* @module @huanlin/dsh-plugin-copilot/invariant
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
export declare const name = "dsh-plugin-copilot-invariant";
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
export declare const inject: string[];
|
|
11
|
+
/**
|
|
12
|
+
* Register this package's invariant companion.
|
|
13
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
14
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
15
|
+
*/
|
|
16
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copilot onboarding state shared by the host gateway and the `copilot_status`
|
|
3
|
+
* tool: which authorization flow serves the `llm-pi-ai` Copilot record, whether
|
|
4
|
+
* that record is stored, and whether the `llm-pi-ai` settings namespace carries
|
|
5
|
+
* a `github-copilot` provider profile (the fact that activates the route).
|
|
6
|
+
*
|
|
7
|
+
* @module @huanlin/dsh-plugin-copilot/status
|
|
8
|
+
*/
|
|
9
|
+
import type { AuthorizationEntry } from '@deepseek-ai/dsh-authorization';
|
|
10
|
+
import type { CredentialKey, CredentialRecord } from '@deepseek-ai/dsh-credentials';
|
|
11
|
+
/** The provider route and record id this plugin bootstraps. */
|
|
12
|
+
export declare const COPILOT_PROVIDER = "github-copilot";
|
|
13
|
+
/** Scope owning the Copilot credential record (dsh-llm-pi-ai's plugin name). */
|
|
14
|
+
export declare const COPILOT_SCOPE = "llm-pi-ai";
|
|
15
|
+
/** Settings namespace whose `providers` dict carries the Copilot profile. */
|
|
16
|
+
export declare const COPILOT_SETTINGS_NS = "llm-pi-ai";
|
|
17
|
+
/**
|
|
18
|
+
* The credential record a pi-ai Copilot login writes.
|
|
19
|
+
*
|
|
20
|
+
* Spelled from the two public constants rather than imported from
|
|
21
|
+
* `@deepseek-ai/dsh-llm-pi-ai`: a third-party plugin must not take a
|
|
22
|
+
* runtime dependency on an internal plugin package, and the record address
|
|
23
|
+
* is exactly `credentialKey('llm-pi-ai', 'github-copilot')`.
|
|
24
|
+
*/
|
|
25
|
+
export declare const COPILOT_RECORD_KEY: CredentialKey;
|
|
26
|
+
/** The joined record address, as the authorization flow registry reports it. */
|
|
27
|
+
export declare function recordAddress(key: CredentialKey): string;
|
|
28
|
+
/**
|
|
29
|
+
* Find the pi-ai Copilot authorization flow without hand-composing its key:
|
|
30
|
+
* the flow registry is the authority on which keys exist, so the join is by
|
|
31
|
+
* scope + id segments of each registered flow's key.
|
|
32
|
+
* @param entries - every registered authorization flow.
|
|
33
|
+
* @returns the entry whose record is the Copilot one, or undefined.
|
|
34
|
+
*/
|
|
35
|
+
export declare function findCopilotFlow(entries: readonly AuthorizationEntry[]): AuthorizationEntry | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Pull the model ids a stored pi-ai OAuth grant reports as usable by this
|
|
38
|
+
* account. pi-ai writes `availableModelIds` at login and rewrites it on every
|
|
39
|
+
* token refresh; anything else — an api-key record, an absent store, an
|
|
40
|
+
* unexpected payload — reads as unknown rather than empty, so a surface can
|
|
41
|
+
* stay silent instead of claiming the account has no models.
|
|
42
|
+
* @param record - the credential record as stored, or undefined.
|
|
43
|
+
* @returns the usable model ids, or undefined when unknown.
|
|
44
|
+
*/
|
|
45
|
+
export declare function grantModelIds(record: CredentialRecord | undefined): readonly string[] | undefined;
|
|
46
|
+
/** What the settings card and the status tool render. */
|
|
47
|
+
export interface CopilotStatus {
|
|
48
|
+
/** Whether the pi-ai Copilot authorization flow is registered. */
|
|
49
|
+
flowAvailable: boolean;
|
|
50
|
+
/** Whether a credential record is stored for the Copilot key. */
|
|
51
|
+
loggedIn: boolean;
|
|
52
|
+
/** Whether the `llm-pi-ai` settings section declares a `github-copilot` profile. */
|
|
53
|
+
profileActivated: boolean;
|
|
54
|
+
/** Whether an authorization attempt for the Copilot key is running right now. */
|
|
55
|
+
inFlight: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Model ids the stored grant reports as usable by this account, or undefined
|
|
58
|
+
* when not signed in / unknown. pi-ai already filters its provider catalog by
|
|
59
|
+
* this list; surfacing it is feedback, not enforcement.
|
|
60
|
+
*/
|
|
61
|
+
models: readonly string[] | undefined;
|
|
62
|
+
}
|
|
63
|
+
/** Host-side reads backing one status join. */
|
|
64
|
+
export interface StatusSources {
|
|
65
|
+
/** Live flow registry listing. */
|
|
66
|
+
listFlows(): readonly AuthorizationEntry[];
|
|
67
|
+
/** Presence/kind facts for the Copilot record, or `undefined` without a credential store. */
|
|
68
|
+
describeRecord(key: CredentialKey): Promise<{
|
|
69
|
+
configured: boolean;
|
|
70
|
+
kind?: string;
|
|
71
|
+
} | undefined>;
|
|
72
|
+
/** The resolved `llm-pi-ai` section value, or `undefined` when unregistered. */
|
|
73
|
+
settingsSection(): Record<string, unknown> | undefined;
|
|
74
|
+
/** The stored grant's usable model ids, or `undefined` when unknown. */
|
|
75
|
+
models(): Promise<readonly string[] | undefined>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Join the facts the card shows.
|
|
79
|
+
* @param sources - the live host reads.
|
|
80
|
+
* @returns the card status; `loggedIn` is false when no credential store is mounted.
|
|
81
|
+
*/
|
|
82
|
+
export declare function joinStatus(sources: StatusSources): Promise<CopilotStatus>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing Copilot status tool. Login interaction belongs to the WebUI
|
|
3
|
+
* card; this tool only reports the onboarding state so an agent can answer
|
|
4
|
+
* "am I signed in to Copilot?" without touching credentials.
|
|
5
|
+
*
|
|
6
|
+
* @module @huanlin/dsh-plugin-copilot/tools
|
|
7
|
+
*/
|
|
8
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
9
|
+
import type { StatusSources } from './status.ts';
|
|
10
|
+
/**
|
|
11
|
+
* Register the status tool.
|
|
12
|
+
* @param ctx - host plugin context carrying `ctx.tools`.
|
|
13
|
+
* @param sources - the live host reads the status join uses.
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerCopilotTools(ctx: Context, sources: StatusSources): void;
|
package/package.json
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@huanlin/dsh-plugin-copilot",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"keywords": [
|
|
8
|
+
"dsh-plugin"
|
|
9
|
+
],
|
|
10
|
+
"description": "Copilot 引导层插件:WebUI 设置页一键发起 GitHub Copilot 授权(复用 dsh-llm-pi-ai 内置 device-flow),并自动写入 provider profile 激活模型路由。 | Copilot onboarding plugin: one-click GitHub authorization from the WebUI settings card (reusing dsh-llm-pi-ai's builtin device flow) plus automatic provider-profile autofill that activates the model route.",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"main": "./lib/index.js",
|
|
14
|
+
"types": "./lib/types/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./lib/types/index.d.ts",
|
|
18
|
+
"import": "./lib/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./invariant": {
|
|
21
|
+
"types": "./lib/types/invariant.d.ts",
|
|
22
|
+
"import": "./lib/invariant.js"
|
|
23
|
+
},
|
|
24
|
+
"./client": {
|
|
25
|
+
"types": "./lib/types/client/index.d.ts",
|
|
26
|
+
"default": "./lib/client.js"
|
|
27
|
+
},
|
|
28
|
+
"./src/*": "./src/*",
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"lib/",
|
|
33
|
+
"cordis.patch.yml"
|
|
34
|
+
],
|
|
35
|
+
"dsh": {
|
|
36
|
+
"bundle": {
|
|
37
|
+
"patch": "./cordis.patch.yml"
|
|
38
|
+
},
|
|
39
|
+
"client": {
|
|
40
|
+
"inject": [
|
|
41
|
+
"@deepseek-ai/dsh-client-locale",
|
|
42
|
+
"@deepseek-ai/dsh-client-store",
|
|
43
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
44
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins",
|
|
45
|
+
"@deepseek-ai/dsh-client-ui-slots"
|
|
46
|
+
],
|
|
47
|
+
"platform": "web"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"build": "tsdown -c tsdown.config.ts && tsc -p tsconfig.json",
|
|
54
|
+
"bundle:client": "tsdown -c tsdown.config.ts"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
58
|
+
"@deepseek-ai/dsh-authorization": "^0.1.2-alpha.1",
|
|
59
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1",
|
|
60
|
+
"@deepseek-ai/dsh-client-store": "^0.1.2-alpha.1",
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.2-alpha.1",
|
|
63
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.1",
|
|
64
|
+
"@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1",
|
|
65
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1",
|
|
66
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.1",
|
|
67
|
+
"@deepseek-ai/dsh-tools": "^0.1.2-alpha.1",
|
|
68
|
+
"react": "^18.2.0",
|
|
69
|
+
"schemastery": "^3.18.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"@deepseek-ai/cordis": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@deepseek-ai/dsh-authorization": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"@deepseek-ai/dsh-client-store": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"@deepseek-ai/dsh-client-ui-renderer": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
87
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": {
|
|
88
|
+
"optional": true
|
|
89
|
+
},
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-slots": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
93
|
+
"@deepseek-ai/dsh-credentials": {
|
|
94
|
+
"optional": true
|
|
95
|
+
},
|
|
96
|
+
"@deepseek-ai/dsh-invariants": {
|
|
97
|
+
"optional": true
|
|
98
|
+
},
|
|
99
|
+
"@deepseek-ai/dsh-settings": {
|
|
100
|
+
"optional": true
|
|
101
|
+
},
|
|
102
|
+
"@deepseek-ai/dsh-tools": {
|
|
103
|
+
"optional": true
|
|
104
|
+
},
|
|
105
|
+
"react": {
|
|
106
|
+
"optional": true
|
|
107
|
+
},
|
|
108
|
+
"schemastery": {
|
|
109
|
+
"optional": true
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"devDependencies": {
|
|
113
|
+
"@types/node": "^22.20.1",
|
|
114
|
+
"@types/react": "~18.3.31",
|
|
115
|
+
"jsdom": "^30.0.1",
|
|
116
|
+
"react": "^18.2.0",
|
|
117
|
+
"react-dom": "^18.2.0",
|
|
118
|
+
"schemastery": "^3.18.0",
|
|
119
|
+
"tsdown": "^0.15.0",
|
|
120
|
+
"typescript": "^5.9.2",
|
|
121
|
+
"vitest": "^3.2.7"
|
|
122
|
+
},
|
|
123
|
+
"engines": {
|
|
124
|
+
"node": ">=22.0.0"
|
|
125
|
+
}
|
|
126
|
+
}
|