@krischoichoi/channel-control 0.6.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 +67 -0
- package/lib/access/conversation-directory-store.d.ts +73 -0
- package/lib/access/conversation-directory-store.d.ts.map +1 -0
- package/lib/access/conversation-directory-store.js +66 -0
- package/lib/access/conversation-directory-store.js.map +1 -0
- package/lib/access/conversation-directory.d.ts +51 -0
- package/lib/access/conversation-directory.d.ts.map +1 -0
- package/lib/access/conversation-directory.js +118 -0
- package/lib/access/conversation-directory.js.map +1 -0
- package/lib/access/manager.d.ts +47 -0
- package/lib/access/manager.d.ts.map +1 -0
- package/lib/access/manager.js +124 -0
- package/lib/access/manager.js.map +1 -0
- package/lib/access/materialize.d.ts +33 -0
- package/lib/access/materialize.d.ts.map +1 -0
- package/lib/access/materialize.js +67 -0
- package/lib/access/materialize.js.map +1 -0
- package/lib/access/owner-claim.d.ts +91 -0
- package/lib/access/owner-claim.d.ts.map +1 -0
- package/lib/access/owner-claim.js +200 -0
- package/lib/access/owner-claim.js.map +1 -0
- package/lib/access/policy-store.d.ts +55 -0
- package/lib/access/policy-store.d.ts.map +1 -0
- package/lib/access/policy-store.js +88 -0
- package/lib/access/policy-store.js.map +1 -0
- package/lib/access/validation.d.ts +29 -0
- package/lib/access/validation.d.ts.map +1 -0
- package/lib/access/validation.js +89 -0
- package/lib/access/validation.js.map +1 -0
- package/lib/auth/sanitizer.d.ts +35 -0
- package/lib/auth/sanitizer.d.ts.map +1 -0
- package/lib/auth/sanitizer.js +52 -0
- package/lib/auth/sanitizer.js.map +1 -0
- package/lib/auth/session-manager.d.ts +38 -0
- package/lib/auth/session-manager.d.ts.map +1 -0
- package/lib/auth/session-manager.js +219 -0
- package/lib/auth/session-manager.js.map +1 -0
- package/lib/config.d.ts +20 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +15 -0
- package/lib/config.js.map +1 -0
- package/lib/credentials/manager.d.ts +44 -0
- package/lib/credentials/manager.d.ts.map +1 -0
- package/lib/credentials/manager.js +24 -0
- package/lib/credentials/manager.js.map +1 -0
- package/lib/definitions/registry.d.ts +24 -0
- package/lib/definitions/registry.d.ts.map +1 -0
- package/lib/definitions/registry.js +59 -0
- package/lib/definitions/registry.js.map +1 -0
- package/lib/errors.d.ts +20 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +45 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +31 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +31 -0
- package/lib/index.js.map +1 -0
- package/lib/plugin.d.ts +20 -0
- package/lib/plugin.d.ts.map +1 -0
- package/lib/plugin.js +81 -0
- package/lib/plugin.js.map +1 -0
- package/lib/runtime/manager.d.ts +73 -0
- package/lib/runtime/manager.d.ts.map +1 -0
- package/lib/runtime/manager.js +277 -0
- package/lib/runtime/manager.js.map +1 -0
- package/lib/runtime/mount-handle.d.ts +11 -0
- package/lib/runtime/mount-handle.d.ts.map +1 -0
- package/lib/runtime/mount-handle.js +2 -0
- package/lib/runtime/mount-handle.js.map +1 -0
- package/lib/service.d.ts +180 -0
- package/lib/service.d.ts.map +1 -0
- package/lib/service.js +432 -0
- package/lib/service.js.map +1 -0
- package/lib/types.d.ts +370 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/lib/update-check.d.ts +128 -0
- package/lib/update-check.d.ts.map +1 -0
- package/lib/update-check.js +380 -0
- package/lib/update-check.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access Policy validation — the entry constrains what a policy may express
|
|
3
|
+
* against a channel's declared access descriptor.
|
|
4
|
+
*
|
|
5
|
+
* Validation is two-layered:
|
|
6
|
+
* 1. The shared `channelAccessPolicySchema` (version pinning, canonical id
|
|
7
|
+
* trimming, strict unknown-key rejection, type checks).
|
|
8
|
+
* 2. Descriptor-relative rules (owner-only needs an ownerId, mentions/groups/
|
|
9
|
+
* DM capability gating).
|
|
10
|
+
*
|
|
11
|
+
* Normalization performed on a validated policy: trim canonical ids and remove
|
|
12
|
+
* EXACT duplicates in every `allowFrom` list. Deliberately NO lowercase, no
|
|
13
|
+
* username resolution, no fuzzy matching — ids are opaque.
|
|
14
|
+
*/
|
|
15
|
+
import { channelAccessPolicySchema, } from '@krischoichoi/channel-core';
|
|
16
|
+
import { materializeAccessPolicy } from './materialize.js';
|
|
17
|
+
/** Remove exact duplicated strings while preserving first-seen order. */
|
|
18
|
+
function dedupeExact(values) {
|
|
19
|
+
const seen = new Set();
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const value of values) {
|
|
22
|
+
if (seen.has(value))
|
|
23
|
+
continue;
|
|
24
|
+
seen.add(value);
|
|
25
|
+
out.push(value);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Validate a candidate policy against a channel descriptor and, when valid,
|
|
31
|
+
* return a normalized copy (ids trimmed by the schema, `allowFrom` de-duplicated).
|
|
32
|
+
*/
|
|
33
|
+
export function validateAccessPolicy(input, descriptor) {
|
|
34
|
+
const parsed = channelAccessPolicySchema.safeParse(input);
|
|
35
|
+
if (!parsed.success) {
|
|
36
|
+
return { ok: false, error: `invalid policy: ${parsed.error.issues[0]?.message ?? 'schema mismatch'}` };
|
|
37
|
+
}
|
|
38
|
+
// From here on we work on the canonical, schema-validated policy.
|
|
39
|
+
const policy = parsed.data;
|
|
40
|
+
if (policy.preset === 'owner-only' && (!policy.ownerId || policy.ownerId === '')) {
|
|
41
|
+
return { ok: false, error: 'owner-only preset requires an ownerId' };
|
|
42
|
+
}
|
|
43
|
+
// requireMention=true is only expressible when the channel can detect mentions.
|
|
44
|
+
const groupRules = [
|
|
45
|
+
...Object.entries(policy.groups),
|
|
46
|
+
...(policy.defaultGroupRule
|
|
47
|
+
? [['*', policy.defaultGroupRule]]
|
|
48
|
+
: []),
|
|
49
|
+
];
|
|
50
|
+
for (const [groupId, rule] of groupRules) {
|
|
51
|
+
if (rule.requireMention && descriptor.mentions !== true) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
error: `group '${groupId}' requires mention but channel does not support mentions`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// A channel without groups cannot carry any group configuration.
|
|
59
|
+
const hasGroupConfig = policy.groupPolicy !== 'disabled' || Object.keys(policy.groups).length > 0;
|
|
60
|
+
if (hasGroupConfig && descriptor.groups !== true) {
|
|
61
|
+
return { ok: false, error: 'policy configures groups but channel does not support groups' };
|
|
62
|
+
}
|
|
63
|
+
// Channel with direct messages disabled must keep DM fully disabled.
|
|
64
|
+
if (descriptor.directMessages !== true && policy.dmPolicy !== 'disabled') {
|
|
65
|
+
return { ok: false, error: 'policy enables direct messages but channel does not support them' };
|
|
66
|
+
}
|
|
67
|
+
// Channel without groups must keep group policy disabled.
|
|
68
|
+
if (descriptor.groups !== true && policy.groupPolicy !== 'disabled') {
|
|
69
|
+
return { ok: false, error: 'policy enables groups but channel does not support them' };
|
|
70
|
+
}
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Normalization (trim + exact dedupe). The schema already trims ids; here we
|
|
73
|
+
// additionally de-duplicate allowFrom lists. No lowercase / fuzzy matching.
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
const groups = {};
|
|
76
|
+
for (const [groupId, rule] of Object.entries(policy.groups)) {
|
|
77
|
+
groups[groupId] = { ...rule, allowFrom: dedupeExact(rule.allowFrom) };
|
|
78
|
+
}
|
|
79
|
+
const normalized = {
|
|
80
|
+
...policy,
|
|
81
|
+
allowFrom: dedupeExact(policy.allowFrom),
|
|
82
|
+
groups,
|
|
83
|
+
...(policy.defaultGroupRule
|
|
84
|
+
? { defaultGroupRule: { ...policy.defaultGroupRule, allowFrom: dedupeExact(policy.defaultGroupRule.allowFrom) } }
|
|
85
|
+
: {}),
|
|
86
|
+
};
|
|
87
|
+
return { ok: true, policy: materializeAccessPolicy(normalized) };
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/access/validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public-DTO sanitizer (doc §18, §59).
|
|
3
|
+
*
|
|
4
|
+
* Converts host-only auth session/status objects into browser-facing public
|
|
5
|
+
* DTOs. The sanitizer is the ONLY legitimate path to a [PublicAuthSession] /
|
|
6
|
+
* [PublicAuthStatus]: it strips every field that must never reach the browser
|
|
7
|
+
* (providerState, challenge, deviceCode, tokens, any provider payload).
|
|
8
|
+
* Security tests assert the absence of these fields after sanitization.
|
|
9
|
+
*
|
|
10
|
+
* The manager stores sessions as a [SessionSnapshot] — an [InternalAuthSession]
|
|
11
|
+
* plus the host-tracked current phase/state/qr/prompt. The sanitizer accepts
|
|
12
|
+
* that snapshot and forwards only the declared public fields.
|
|
13
|
+
*/
|
|
14
|
+
import type { AuthMethod, AuthPhase, AuthProviderSession, AuthState, InternalAuthSession, PublicAuthPrompt, PublicAuthSession, PublicAuthStatus, PublicQrPayload } from '../types.js';
|
|
15
|
+
/** Host-view snapshot the manager keeps for one session. */
|
|
16
|
+
export interface SessionSnapshot extends InternalAuthSession {
|
|
17
|
+
method: AuthMethod;
|
|
18
|
+
state: AuthState;
|
|
19
|
+
phase: AuthPhase;
|
|
20
|
+
qr?: PublicQrPayload;
|
|
21
|
+
prompt?: PublicAuthPrompt;
|
|
22
|
+
/** The full provider session returned by beginAuth (host-only). */
|
|
23
|
+
providerSession: AuthProviderSession;
|
|
24
|
+
}
|
|
25
|
+
/** Deep-copy a QR payload so mutations cannot escape the host boundary. */
|
|
26
|
+
export declare function toPublicQr(qr: PublicQrPayload | undefined): PublicQrPayload | undefined;
|
|
27
|
+
export declare function toPublicPrompt(prompt: PublicAuthPrompt | undefined): PublicAuthPrompt | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Field-by-field copy into the public session shape. Nothing beyond the
|
|
30
|
+
* declared public fields is forwarded, so a host mutation cannot leak.
|
|
31
|
+
*/
|
|
32
|
+
export declare function toPublicSession(session: SessionSnapshot): PublicAuthSession;
|
|
33
|
+
/** Sanitize a poll/status result, dropping any host-only fields. */
|
|
34
|
+
export declare function toPublicStatus(status: PublicAuthStatus): PublicAuthStatus;
|
|
35
|
+
//# sourceMappingURL=sanitizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizer.d.ts","sourceRoot":"","sources":["../../src/auth/sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EACV,UAAU,EACV,SAAS,EACT,mBAAmB,EACnB,SAAS,EACT,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EAChB,MAAM,aAAa,CAAC;AAErB,4DAA4D;AAC5D,MAAM,WAAW,eAAgB,SAAQ,mBAAmB;IAC1D,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,EAAE,CAAC,EAAE,eAAe,CAAC;IACrB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,mEAAmE;IACnE,eAAe,EAAE,mBAAmB,CAAC;CACtC;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,EAAE,EAAE,eAAe,GAAG,SAAS,GAAG,eAAe,GAAG,SAAS,CAGvF;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,gBAAgB,GAAG,SAAS,GACnC,gBAAgB,GAAG,SAAS,CAE9B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,iBAAiB,CAmB3E;AAED,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAUzE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Deep-copy a QR payload so mutations cannot escape the host boundary. */
|
|
2
|
+
export function toPublicQr(qr) {
|
|
3
|
+
if (!qr)
|
|
4
|
+
return undefined;
|
|
5
|
+
return qr.kind && qr.value ? { ...qr } : undefined;
|
|
6
|
+
}
|
|
7
|
+
export function toPublicPrompt(prompt) {
|
|
8
|
+
return prompt ? { ...prompt } : undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Field-by-field copy into the public session shape. Nothing beyond the
|
|
12
|
+
* declared public fields is forwarded, so a host mutation cannot leak.
|
|
13
|
+
*/
|
|
14
|
+
export function toPublicSession(session) {
|
|
15
|
+
const publicSession = {
|
|
16
|
+
id: session.id,
|
|
17
|
+
channelId: session.channelId,
|
|
18
|
+
state: session.state,
|
|
19
|
+
phase: session.phase,
|
|
20
|
+
};
|
|
21
|
+
const qr = toPublicQr(session.qr);
|
|
22
|
+
if (qr)
|
|
23
|
+
publicSession.qr = qr;
|
|
24
|
+
if (session.expiresAt !== undefined)
|
|
25
|
+
publicSession.expiresAt = session.expiresAt;
|
|
26
|
+
const prompt = toPublicPrompt(session.prompt);
|
|
27
|
+
if (prompt)
|
|
28
|
+
publicSession.prompt = prompt;
|
|
29
|
+
// The provider poll interval is safe to expose (doc §15): it is neither a
|
|
30
|
+
// secret nor a credential — it just lets the browser space client polls at
|
|
31
|
+
// the provider's declared throttle. Only forwarded when actually declared.
|
|
32
|
+
if (typeof session.pollingIntervalMs === 'number' && session.pollingIntervalMs > 0) {
|
|
33
|
+
publicSession.pollingIntervalMs = session.pollingIntervalMs;
|
|
34
|
+
}
|
|
35
|
+
return publicSession;
|
|
36
|
+
}
|
|
37
|
+
/** Sanitize a poll/status result, dropping any host-only fields. */
|
|
38
|
+
export function toPublicStatus(status) {
|
|
39
|
+
const result = {
|
|
40
|
+
state: status.state,
|
|
41
|
+
phase: status.phase,
|
|
42
|
+
};
|
|
43
|
+
if (status.expiresAt !== undefined)
|
|
44
|
+
result.expiresAt = status.expiresAt;
|
|
45
|
+
if (status.detail !== undefined)
|
|
46
|
+
result.detail = status.detail;
|
|
47
|
+
const prompt = toPublicPrompt(status.prompt);
|
|
48
|
+
if (prompt)
|
|
49
|
+
result.prompt = prompt;
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=sanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizer.js","sourceRoot":"","sources":["../../src/auth/sanitizer.ts"],"names":[],"mappings":"AAoCA,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,EAA+B;IACxD,IAAI,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IAC1B,OAAO,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,MAAoC;IAEpC,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAwB;IACtD,MAAM,aAAa,GAAsB;QACvC,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC;IACF,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClC,IAAI,EAAE;QAAE,aAAa,CAAC,EAAE,GAAG,EAAE,CAAC;IAC9B,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS;QAAE,aAAa,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACjF,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,MAAM;QAAE,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC;IAC1C,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,IAAI,OAAO,OAAO,CAAC,iBAAiB,KAAK,QAAQ,IAAI,OAAO,CAAC,iBAAiB,GAAG,CAAC,EAAE,CAAC;QACnF,aAAa,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAC9D,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,cAAc,CAAC,MAAwB;IACrD,MAAM,MAAM,GAAqB;QAC/B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC;IACF,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS;QAAE,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IACxE,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;QAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC/D,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,MAAM;QAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACnC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ChannelDefinitionRegistry } from '../definitions/registry.js';
|
|
2
|
+
import type { AuthBeginInput, AuthInput, PublicAuthSession, PublicAuthStatus } from '../types.js';
|
|
3
|
+
/** Interactive QR/device authorization is deliberately short-lived. */
|
|
4
|
+
export declare const MAX_AUTH_SESSION_TTL_MS: number;
|
|
5
|
+
export interface AuthSessionManagerOptions {
|
|
6
|
+
registry: ChannelDefinitionRegistry;
|
|
7
|
+
/** Injectable clock (ms since epoch) for tests. Defaults to Date.now. */
|
|
8
|
+
now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
export declare class AuthSessionManager {
|
|
11
|
+
private readonly sessions;
|
|
12
|
+
/** Tombstones for cancelled session ids so a later poll/submit rejects clearly. */
|
|
13
|
+
private readonly cancelled;
|
|
14
|
+
private readonly registry;
|
|
15
|
+
private readonly now;
|
|
16
|
+
constructor(options: AuthSessionManagerOptions);
|
|
17
|
+
create(channelId: string, input: AuthBeginInput): Promise<PublicAuthSession>;
|
|
18
|
+
poll(sessionId: string): Promise<PublicAuthStatus>;
|
|
19
|
+
submit(sessionId: string, input: AuthInput): Promise<PublicAuthStatus>;
|
|
20
|
+
cancel(sessionId: string): Promise<void>;
|
|
21
|
+
/** Abort + delete every session past its TTL; returns how many expired. */
|
|
22
|
+
expireAll(): number;
|
|
23
|
+
/** Sweep expired sessions and prune cancelled tombstones. */
|
|
24
|
+
cleanup(): number;
|
|
25
|
+
/** Internal list of live session ids (test/diagnostics). */
|
|
26
|
+
listIds(): string[];
|
|
27
|
+
get size(): number;
|
|
28
|
+
private key;
|
|
29
|
+
private findByKey;
|
|
30
|
+
private accountKey;
|
|
31
|
+
private buildSnapshot;
|
|
32
|
+
/** Call the definition's pollAuth, update stored status, return sanitized. */
|
|
33
|
+
private pollProvider;
|
|
34
|
+
private lastKnownStatus;
|
|
35
|
+
private cancelById;
|
|
36
|
+
private expire;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/auth/session-manager.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAEvE,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EAIT,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAOrB,uEAAuE;AACvE,eAAO,MAAM,uBAAuB,QAAgB,CAAC;AAErD,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,yBAAyB,CAAC;IACpC,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAID,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,OAAO,EAAE,yBAAyB;IAOxC,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,iBAAiB,CAAC;IA6BvB,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkClD,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkBtE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9C,2EAA2E;IAC3E,SAAS,IAAI,MAAM;IAWnB,6DAA6D;IAC7D,OAAO,IAAI,MAAM;IAIjB,4DAA4D;IAC5D,OAAO,IAAI,MAAM,EAAE;IAInB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAID,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,aAAa;IA8CrB,8EAA8E;YAChE,YAAY;IAgB1B,OAAO,CAAC,eAAe;YAST,UAAU;IAWxB,OAAO,CAAC,MAAM;CAMf"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [AuthSessionManager] — host-side lifecycle for interactive authorization
|
|
3
|
+
* (doc §19–§20, §53 Task 7).
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* - one active session per (channelId, accountId, method); a new session for
|
|
7
|
+
* the same key cancels (aborts + deletes) the previous pending one
|
|
8
|
+
* - browser-safe session ids are [crypto.randomUUID()], never derived from
|
|
9
|
+
* channelId/timestamp/deviceCode
|
|
10
|
+
* - host-side POLL THROTTLE: the provider is only hit when
|
|
11
|
+
* now >= nextPollAt; between intervals the last known status is returned
|
|
12
|
+
* without touching the provider (the browser may poll every second)
|
|
13
|
+
* - terminal states (authenticated/expired/failed) short-circuit provider
|
|
14
|
+
* polls and return the cached status
|
|
15
|
+
* - TTL expiry sweeps + a lightweight injectable clock for tests
|
|
16
|
+
* - every public result is passed through the sanitizer
|
|
17
|
+
*/
|
|
18
|
+
import { randomUUID } from 'node:crypto';
|
|
19
|
+
import { ChannelDefinitionRegistry } from '../definitions/registry.js';
|
|
20
|
+
import { ControlError } from '../errors.js';
|
|
21
|
+
import { toPublicSession, toPublicStatus, } from './sanitizer.js';
|
|
22
|
+
/** Interactive QR/device authorization is deliberately short-lived. */
|
|
23
|
+
export const MAX_AUTH_SESSION_TTL_MS = 3 * 60 * 1000;
|
|
24
|
+
const TERMINAL_STATES = ['authenticated', 'expired', 'failed'];
|
|
25
|
+
export class AuthSessionManager {
|
|
26
|
+
sessions = new Map();
|
|
27
|
+
/** Tombstones for cancelled session ids so a later poll/submit rejects clearly. */
|
|
28
|
+
cancelled = new Set();
|
|
29
|
+
registry;
|
|
30
|
+
now;
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.registry = options.registry;
|
|
33
|
+
this.now = options.now ?? (() => Date.now());
|
|
34
|
+
}
|
|
35
|
+
// --- create ---------------------------------------------------------------
|
|
36
|
+
async create(channelId, input) {
|
|
37
|
+
const definition = this.registry.require(channelId);
|
|
38
|
+
if (!definition.beginAuth) {
|
|
39
|
+
throw new ControlError('AUTH_NOT_SUPPORTED', `channel '${channelId}' does not support interactive authorization`);
|
|
40
|
+
}
|
|
41
|
+
const key = this.key(channelId, input);
|
|
42
|
+
// One active session per key: cancel any existing pending session first.
|
|
43
|
+
const existing = this.findByKey(key);
|
|
44
|
+
if (existing) {
|
|
45
|
+
await this.cancelById(existing.id);
|
|
46
|
+
}
|
|
47
|
+
const providerSession = await definition.beginAuth(input);
|
|
48
|
+
const snapshot = this.buildSnapshot(channelId, input.accountId ?? '', key, providerSession);
|
|
49
|
+
this.sessions.set(snapshot.id, snapshot);
|
|
50
|
+
return toPublicSession(snapshot);
|
|
51
|
+
}
|
|
52
|
+
// --- poll -----------------------------------------------------------------
|
|
53
|
+
async poll(sessionId) {
|
|
54
|
+
if (this.cancelled.has(sessionId)) {
|
|
55
|
+
throw new ControlError('AUTH_SESSION_CANCELLED');
|
|
56
|
+
}
|
|
57
|
+
const session = this.sessions.get(sessionId);
|
|
58
|
+
if (!session) {
|
|
59
|
+
throw new ControlError('AUTH_SESSION_NOT_FOUND');
|
|
60
|
+
}
|
|
61
|
+
// Terminal states: return the cached status without ever hitting provider.
|
|
62
|
+
if (TERMINAL_STATES.includes(session.state)) {
|
|
63
|
+
return toPublicStatus({
|
|
64
|
+
state: session.state,
|
|
65
|
+
phase: session.phase,
|
|
66
|
+
expiresAt: session.expiresAt,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// Expired by TTL: mark + delete, report 'expired'.
|
|
70
|
+
if (this.now() >= session.expiresAt) {
|
|
71
|
+
this.expire(session);
|
|
72
|
+
return toPublicStatus({ state: 'expired', phase: 'expired' });
|
|
73
|
+
}
|
|
74
|
+
// Poll throttle: within the provider interval, return the last known status.
|
|
75
|
+
if (this.now() < session.nextPollAt) {
|
|
76
|
+
return this.lastKnownStatus(session);
|
|
77
|
+
}
|
|
78
|
+
return this.pollProvider(session);
|
|
79
|
+
}
|
|
80
|
+
// --- submit -----------------------------------------------------------------
|
|
81
|
+
async submit(sessionId, input) {
|
|
82
|
+
if (this.cancelled.has(sessionId)) {
|
|
83
|
+
throw new ControlError('AUTH_SESSION_CANCELLED');
|
|
84
|
+
}
|
|
85
|
+
const session = this.sessions.get(sessionId);
|
|
86
|
+
if (!session)
|
|
87
|
+
throw new ControlError('AUTH_SESSION_NOT_FOUND');
|
|
88
|
+
const definition = this.registry.require(session.channelId);
|
|
89
|
+
if (!definition.submitAuthInput) {
|
|
90
|
+
throw new ControlError('AUTH_NOT_SUPPORTED');
|
|
91
|
+
}
|
|
92
|
+
await definition.submitAuthInput(session.providerSession, input);
|
|
93
|
+
// Force a fresh provider poll to reflect the submitted input.
|
|
94
|
+
return this.pollProvider(session);
|
|
95
|
+
}
|
|
96
|
+
// --- cancel ----------------------------------------------------------------
|
|
97
|
+
async cancel(sessionId) {
|
|
98
|
+
if (this.cancelled.has(sessionId))
|
|
99
|
+
return;
|
|
100
|
+
const session = this.sessions.get(sessionId);
|
|
101
|
+
if (!session) {
|
|
102
|
+
// Idempotent: unknown ids are treated as already gone.
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
await this.cancelById(session.id);
|
|
106
|
+
}
|
|
107
|
+
// --- expiry / cleanup --------------------------------------------------------
|
|
108
|
+
/** Abort + delete every session past its TTL; returns how many expired. */
|
|
109
|
+
expireAll() {
|
|
110
|
+
let count = 0;
|
|
111
|
+
for (const session of [...this.sessions.values()]) {
|
|
112
|
+
if (this.now() >= session.expiresAt) {
|
|
113
|
+
this.expire(session);
|
|
114
|
+
count += 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return count;
|
|
118
|
+
}
|
|
119
|
+
/** Sweep expired sessions and prune cancelled tombstones. */
|
|
120
|
+
cleanup() {
|
|
121
|
+
return this.expireAll();
|
|
122
|
+
}
|
|
123
|
+
/** Internal list of live session ids (test/diagnostics). */
|
|
124
|
+
listIds() {
|
|
125
|
+
return [...this.sessions.keys()];
|
|
126
|
+
}
|
|
127
|
+
get size() {
|
|
128
|
+
return this.sessions.size;
|
|
129
|
+
}
|
|
130
|
+
// --- internals ----------------------------------------------------------------
|
|
131
|
+
key(channelId, input) {
|
|
132
|
+
return `${channelId}:${input.accountId ?? ''}:${input.method}`;
|
|
133
|
+
}
|
|
134
|
+
findByKey(key) {
|
|
135
|
+
for (const session of this.sessions.values()) {
|
|
136
|
+
if (this.accountKey(session) === key)
|
|
137
|
+
return session;
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
accountKey(session) {
|
|
142
|
+
return `${session.channelId}:${session.accountId}:${session.method}`;
|
|
143
|
+
}
|
|
144
|
+
buildSnapshot(channelId, accountId, key, providerSession) {
|
|
145
|
+
const now = this.now();
|
|
146
|
+
const expiresAt = Math.min(providerSession.expiresAt ?? now + MAX_AUTH_SESSION_TTL_MS, now + MAX_AUTH_SESSION_TTL_MS);
|
|
147
|
+
const scopedQr = providerSession.qr
|
|
148
|
+
? { ...providerSession.qr, expiresAt: Math.min(providerSession.qr.expiresAt ?? expiresAt, expiresAt) }
|
|
149
|
+
: undefined;
|
|
150
|
+
const scopedProviderSession = {
|
|
151
|
+
...providerSession,
|
|
152
|
+
expiresAt,
|
|
153
|
+
qr: scopedQr,
|
|
154
|
+
};
|
|
155
|
+
const interval = scopedProviderSession.pollingIntervalMs;
|
|
156
|
+
const initialPhase = scopedProviderSession.qr || scopedProviderSession.prompt?.kind === 'open-browser'
|
|
157
|
+
? 'waiting-scan'
|
|
158
|
+
: 'preparing';
|
|
159
|
+
const snapshot = {
|
|
160
|
+
id: randomUUID(),
|
|
161
|
+
channelId,
|
|
162
|
+
accountId,
|
|
163
|
+
method: key.split(':')[2] ?? 'qr',
|
|
164
|
+
provider: scopedProviderSession.provider,
|
|
165
|
+
createdAt: now,
|
|
166
|
+
expiresAt,
|
|
167
|
+
pollingIntervalMs: interval,
|
|
168
|
+
nextPollAt: now + interval,
|
|
169
|
+
deviceCode: scopedProviderSession.deviceCode,
|
|
170
|
+
abortController: new AbortController(),
|
|
171
|
+
providerState: scopedProviderSession.providerState,
|
|
172
|
+
qr: scopedProviderSession.qr,
|
|
173
|
+
prompt: scopedProviderSession.prompt,
|
|
174
|
+
state: 'pending',
|
|
175
|
+
phase: initialPhase,
|
|
176
|
+
providerSession: scopedProviderSession,
|
|
177
|
+
};
|
|
178
|
+
return snapshot;
|
|
179
|
+
}
|
|
180
|
+
/** Call the definition's pollAuth, update stored status, return sanitized. */
|
|
181
|
+
async pollProvider(session) {
|
|
182
|
+
const definition = this.registry.require(session.channelId);
|
|
183
|
+
if (!definition.pollAuth) {
|
|
184
|
+
throw new ControlError('AUTH_NOT_SUPPORTED', `channel '${session.channelId}' has no pollAuth to drive authorize flow`);
|
|
185
|
+
}
|
|
186
|
+
const status = await definition.pollAuth(session.providerSession);
|
|
187
|
+
session.nextPollAt = this.now() + session.pollingIntervalMs;
|
|
188
|
+
session.state = status.state;
|
|
189
|
+
session.phase = status.phase;
|
|
190
|
+
if (status.prompt)
|
|
191
|
+
session.prompt = status.prompt;
|
|
192
|
+
return toPublicStatus({ ...status, expiresAt: session.expiresAt });
|
|
193
|
+
}
|
|
194
|
+
lastKnownStatus(session) {
|
|
195
|
+
return toPublicStatus({
|
|
196
|
+
state: session.state,
|
|
197
|
+
phase: session.phase,
|
|
198
|
+
expiresAt: session.expiresAt,
|
|
199
|
+
prompt: session.prompt,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
async cancelById(sessionId) {
|
|
203
|
+
const session = this.sessions.get(sessionId);
|
|
204
|
+
if (!session) {
|
|
205
|
+
this.cancelled.add(sessionId);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
session.abortController.abort();
|
|
209
|
+
this.sessions.delete(sessionId);
|
|
210
|
+
this.cancelled.add(sessionId);
|
|
211
|
+
}
|
|
212
|
+
expire(session) {
|
|
213
|
+
session.abortController.abort();
|
|
214
|
+
session.state = 'expired';
|
|
215
|
+
session.phase = 'expired';
|
|
216
|
+
this.sessions.delete(session.id);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../src/auth/session-manager.ts"],"names":[],"mappings":"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"}
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schemastery configuration for the channel-control plugin.
|
|
3
|
+
*
|
|
4
|
+
* All deployment-tunable parameters live here (no hardcoded deployment
|
|
5
|
+
* constants). The update check is prompt-only; disabling it only removes the
|
|
6
|
+
* hint surfaces, never any channel functionality.
|
|
7
|
+
*/
|
|
8
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
9
|
+
/** Runtime bundle update check (prompt-only; never installs anything). */
|
|
10
|
+
export interface UpdateCheckConfig {
|
|
11
|
+
/** Whether the periodic npm dist-tag check runs. */
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
/** Minimum hours between two registry checks (cache TTL). */
|
|
14
|
+
intervalHours: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Config {
|
|
17
|
+
updateCheck: UpdateCheckConfig;
|
|
18
|
+
}
|
|
19
|
+
export declare const Config: Schema<Config>;
|
|
20
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAE9C,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,6DAA6D;IAC7D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,MAAM;IACrB,WAAW,EAAE,iBAAiB,CAAC;CAChC;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CAKhC,CAAC"}
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schemastery configuration for the channel-control plugin.
|
|
3
|
+
*
|
|
4
|
+
* All deployment-tunable parameters live here (no hardcoded deployment
|
|
5
|
+
* constants). The update check is prompt-only; disabling it only removes the
|
|
6
|
+
* hint surfaces, never any channel functionality.
|
|
7
|
+
*/
|
|
8
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
9
|
+
export const Config = Schema.object({
|
|
10
|
+
updateCheck: Schema.object({
|
|
11
|
+
enabled: Schema.boolean().default(true),
|
|
12
|
+
intervalHours: Schema.natural().default(24),
|
|
13
|
+
}).default({ enabled: true, intervalHours: 24 }),
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAc9C,MAAM,CAAC,MAAM,MAAM,GAAmB,MAAM,CAAC,MAAM,CAAC;IAClD,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC;QACzB,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QACvC,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;KAC5C,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;CACjD,CAAC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [CredentialManager] — the single place channel-control touches the injected
|
|
3
|
+
* credential seam (doc §13, §31, §52).
|
|
4
|
+
*
|
|
5
|
+
* The seam is typed structurally (locally) and is never directly imported from
|
|
6
|
+
* an implementation package: the concrete seam is injected at construction,
|
|
7
|
+
* exactly like channel-core wires 'ctx.channels'. The manager is deliberately
|
|
8
|
+
* thin. [CredentialManager.set] never returns the written value — callers only
|
|
9
|
+
* learn whether the credential is configured and writable.
|
|
10
|
+
*/
|
|
11
|
+
export interface CredentialSeam {
|
|
12
|
+
resolve(ref: string): Promise<{
|
|
13
|
+
value: string;
|
|
14
|
+
source: string;
|
|
15
|
+
} | undefined>;
|
|
16
|
+
describe(ref: string): Promise<{
|
|
17
|
+
configured: boolean;
|
|
18
|
+
source?: string;
|
|
19
|
+
writable: boolean;
|
|
20
|
+
}>;
|
|
21
|
+
set(ref: string, value: string): Promise<void>;
|
|
22
|
+
unset(ref: string): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export interface CredentialDescribe {
|
|
25
|
+
configured: boolean;
|
|
26
|
+
source?: string;
|
|
27
|
+
writable: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare class CredentialManager {
|
|
30
|
+
private readonly seam;
|
|
31
|
+
constructor(seam: CredentialSeam);
|
|
32
|
+
resolve(ref: string): Promise<{
|
|
33
|
+
value: string;
|
|
34
|
+
source: string;
|
|
35
|
+
} | undefined>;
|
|
36
|
+
describe(ref: string): Promise<CredentialDescribe>;
|
|
37
|
+
/**
|
|
38
|
+
* Write a credential. Returns { configured, writable } metadata and NEVER
|
|
39
|
+
* echoes the value back.
|
|
40
|
+
*/
|
|
41
|
+
set(ref: string, value: string): Promise<CredentialDescribe>;
|
|
42
|
+
unset(ref: string): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/credentials/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAC7E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC5F,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,iBAAiB;IAChB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,cAAc;IAE3C,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAI5E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIxD;;;OAGG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAK5D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGxC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class CredentialManager {
|
|
2
|
+
seam;
|
|
3
|
+
constructor(seam) {
|
|
4
|
+
this.seam = seam;
|
|
5
|
+
}
|
|
6
|
+
async resolve(ref) {
|
|
7
|
+
return this.seam.resolve(ref);
|
|
8
|
+
}
|
|
9
|
+
async describe(ref) {
|
|
10
|
+
return this.seam.describe(ref);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Write a credential. Returns { configured, writable } metadata and NEVER
|
|
14
|
+
* echoes the value back.
|
|
15
|
+
*/
|
|
16
|
+
async set(ref, value) {
|
|
17
|
+
await this.seam.set(ref, value);
|
|
18
|
+
return this.seam.describe(ref);
|
|
19
|
+
}
|
|
20
|
+
async unset(ref) {
|
|
21
|
+
await this.seam.unset(ref);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/credentials/manager.ts"],"names":[],"mappings":"AAuBA,MAAM,OAAO,iBAAiB;IACC;IAA7B,YAA6B,IAAoB;QAApB,SAAI,GAAJ,IAAI,CAAgB;IAAG,CAAC;IAErD,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAW;QACxB,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa;QAClC,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW;QACrB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ChannelDefinition } from '../types.js';
|
|
2
|
+
export declare class ChannelDefinitionRegistry {
|
|
3
|
+
private readonly definitions;
|
|
4
|
+
private readonly listeners;
|
|
5
|
+
/**
|
|
6
|
+
* Subscribe to definition registrations. The returned disposer removes the
|
|
7
|
+
* listener. Fired synchronously after a successful register() — the control
|
|
8
|
+
* plane uses this to auto-start configured channels the moment their plugin
|
|
9
|
+
* registers the definition (doc §27), since channel plugins activate AFTER
|
|
10
|
+
* the channel-control plugin itself.
|
|
11
|
+
*/
|
|
12
|
+
onRegister(listener: (definition: ChannelDefinition) => void): () => void;
|
|
13
|
+
/** Register a definition; throws ChannelDuplicateError on duplicate id. */
|
|
14
|
+
register(definition: ChannelDefinition): void;
|
|
15
|
+
get(id: string): ChannelDefinition | undefined;
|
|
16
|
+
/** Require a definition, or raise a stable ChannelError. */
|
|
17
|
+
require(id: string): ChannelDefinition;
|
|
18
|
+
has(id: string): boolean;
|
|
19
|
+
/** List definitions in registration order. */
|
|
20
|
+
list(): ChannelDefinition[];
|
|
21
|
+
/** Remove a definition; returns whether it existed. */
|
|
22
|
+
unregister(id: string): boolean;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/definitions/registry.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAwC;IACpE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsD;IAEhF;;;;;;OAMG;IACH,UAAU,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI;IAOzE,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,iBAAiB,GAAG,IAAI;IAa7C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAI9C,4DAA4D;IAC5D,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB;IAWtC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB,8CAA8C;IAC9C,IAAI,IAAI,iBAAiB,EAAE;IAI3B,uDAAuD;IACvD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAGhC"}
|