dsh-connect 0.8.0 → 0.9.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.
Files changed (94) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +134 -17
  3. package/README.zh.md +110 -17
  4. package/client/client.js +510 -80
  5. package/client/client.js.map +4 -4
  6. package/client/locale.mjs +227 -0
  7. package/client/panel-state.mjs +54 -0
  8. package/client/settings-client.mjs +272 -60
  9. package/docs/images/settings-advanced-en.png +0 -0
  10. package/docs/images/settings-advanced-zh.png +0 -0
  11. package/docs/images/settings-defaults-en.png +0 -0
  12. package/docs/images/settings-defaults-zh.png +0 -0
  13. package/docs/images/settings-overview-en.png +0 -0
  14. package/docs/images/settings-overview-zh.png +0 -0
  15. package/lib/binding.d.ts.map +1 -1
  16. package/lib/binding.js +2 -2
  17. package/lib/binding.js.map +1 -1
  18. package/lib/channels/dingtalk/message.d.ts.map +1 -1
  19. package/lib/channels/dingtalk/message.js +7 -0
  20. package/lib/channels/dingtalk/message.js.map +1 -1
  21. package/lib/channels/feishu/adapter.d.ts +6 -12
  22. package/lib/channels/feishu/adapter.d.ts.map +1 -1
  23. package/lib/channels/feishu/adapter.js +2 -17
  24. package/lib/channels/feishu/adapter.js.map +1 -1
  25. package/lib/channels/feishu/i18n.d.ts +2 -0
  26. package/lib/channels/feishu/i18n.d.ts.map +1 -1
  27. package/lib/channels/feishu/i18n.js +2 -0
  28. package/lib/channels/feishu/i18n.js.map +1 -1
  29. package/lib/channels/feishu/index.d.ts +23 -2
  30. package/lib/channels/feishu/index.d.ts.map +1 -1
  31. package/lib/channels/feishu/index.js +32 -6
  32. package/lib/channels/feishu/index.js.map +1 -1
  33. package/lib/channels/telegram/adapter.d.ts.map +1 -1
  34. package/lib/channels/telegram/adapter.js +7 -1
  35. package/lib/channels/telegram/adapter.js.map +1 -1
  36. package/lib/chat-key.d.ts +29 -0
  37. package/lib/chat-key.d.ts.map +1 -0
  38. package/lib/chat-key.js +38 -0
  39. package/lib/chat-key.js.map +1 -0
  40. package/lib/index.d.ts +15 -5
  41. package/lib/index.d.ts.map +1 -1
  42. package/lib/index.js +201 -31
  43. package/lib/index.js.map +1 -1
  44. package/lib/runner.d.ts +54 -0
  45. package/lib/runner.d.ts.map +1 -1
  46. package/lib/runner.js +158 -23
  47. package/lib/runner.js.map +1 -1
  48. package/lib/scheduler.d.ts.map +1 -1
  49. package/lib/scheduler.js +2 -2
  50. package/lib/scheduler.js.map +1 -1
  51. package/lib/service.d.ts +16 -0
  52. package/lib/service.d.ts.map +1 -1
  53. package/lib/service.js +46 -2
  54. package/lib/service.js.map +1 -1
  55. package/lib/settings/channel-runtime.d.ts +75 -0
  56. package/lib/settings/channel-runtime.d.ts.map +1 -0
  57. package/lib/settings/channel-runtime.js +165 -0
  58. package/lib/settings/channel-runtime.js.map +1 -0
  59. package/lib/settings/channels.d.ts +11 -0
  60. package/lib/settings/channels.d.ts.map +1 -1
  61. package/lib/settings/channels.js +31 -0
  62. package/lib/settings/channels.js.map +1 -1
  63. package/lib/settings/credential-store.d.ts +52 -6
  64. package/lib/settings/credential-store.d.ts.map +1 -1
  65. package/lib/settings/credential-store.js +92 -18
  66. package/lib/settings/credential-store.js.map +1 -1
  67. package/lib/settings/namespace.d.ts +175 -0
  68. package/lib/settings/namespace.d.ts.map +1 -0
  69. package/lib/settings/namespace.js +200 -0
  70. package/lib/settings/namespace.js.map +1 -0
  71. package/lib/settings/secret-disclosure.d.ts +50 -0
  72. package/lib/settings/secret-disclosure.d.ts.map +1 -0
  73. package/lib/settings/secret-disclosure.js +132 -0
  74. package/lib/settings/secret-disclosure.js.map +1 -0
  75. package/lib/settings/settings-model.d.ts +17 -0
  76. package/lib/settings/settings-model.d.ts.map +1 -1
  77. package/lib/settings/settings-model.js +8 -0
  78. package/lib/settings/settings-model.js.map +1 -1
  79. package/lib/settings/settings-rpc.d.ts +71 -3
  80. package/lib/settings/settings-rpc.d.ts.map +1 -1
  81. package/lib/settings/settings-rpc.js +167 -24
  82. package/lib/settings/settings-rpc.js.map +1 -1
  83. package/lib/settings/settings-service.d.ts +9 -1
  84. package/lib/settings/settings-service.d.ts.map +1 -1
  85. package/lib/settings/settings-service.js +74 -6
  86. package/lib/settings/settings-service.js.map +1 -1
  87. package/lib/state-dir.d.ts +28 -0
  88. package/lib/state-dir.d.ts.map +1 -0
  89. package/lib/state-dir.js +33 -0
  90. package/lib/state-dir.js.map +1 -0
  91. package/lib/stream.d.ts.map +1 -1
  92. package/lib/stream.js +3 -2
  93. package/lib/stream.js.map +1 -1
  94. package/package.json +11 -9
@@ -1 +1 @@
1
- {"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../../src/settings/credential-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../../src/settings/credential-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * The `dsh-connect` user-settings namespace.
3
+ *
4
+ * DSH ships a first-party user-settings seam (`ctx.settings`, backed by
5
+ * `$DSH_HOME/settings.yaml` via `@deepseek-ai/dsh-settings-file`, hot-reloaded,
6
+ * atomic, file-locked, comment-preserving). Registering a namespace is how a
7
+ * plugin makes its configuration user-editable *and effective*: the official
8
+ * Plugins page renders the fields from the schema, writes go through the
9
+ * provider, and `scope.watch()` tells the plugin to re-read.
10
+ *
11
+ * Two properties of that seam shape this module:
12
+ *
13
+ * - **`installSection` is the consumer entry point.** It registers the plugin's
14
+ * own composition entry as the schema's *base* layer while the settings
15
+ * service is present, and falls back to that entry if the service detaches.
16
+ * Bare `register` would make `$DSH_HOME/settings.yaml` the only source and
17
+ * silently drop a YAML-configured profile.
18
+ * - **The schema is applied to `mergeLayers(base, userSection)` at registration
19
+ * time**, so a stored section that fails validation throws out of
20
+ * `installSection`. Every install here is therefore guarded: a hand-edited
21
+ * `settings.yaml` with a typo must degrade to the plugin config, not abort
22
+ * the plugin's `apply`.
23
+ *
24
+ * **Secrets are deliberately not part of this schema.** They live in the DSH
25
+ * credential store (`ctx.credentials`), which is where the onboarding flow and
26
+ * the `FEISHU_*`-style env layering already put them; `settings.yaml` is a
27
+ * plain document users are invited to paste into bug reports. `sectionOf()`
28
+ * projects secret keys *out* of the base for the same reason — schemastery
29
+ * preserves undeclared keys verbatim, so anything left in the base rides out
30
+ * through `describe()` and the settings RPC. The same projection guards the
31
+ * write path (`LiveConnectSection.write`), which is the mirror hazard: an
32
+ * undeclared key in a *submitted* section would be preserved into the document.
33
+ *
34
+ * `installSection` returns nothing, so the write handle it hands back is built
35
+ * here: `setSource` already wires a thunk onto `scope.get()`, which reads the
36
+ * registration's resolved value at call time — live in both directions.
37
+ *
38
+ * @module dsh-connect/settings/namespace
39
+ */
40
+ import z from "@deepseek-ai/schemastery";
41
+ import { type ChannelName, type ChannelsConfig, type LoggerLike } from "./channels.js";
42
+ /** The settings namespace this plugin owns. */
43
+ export declare const CONNECT_SETTINGS_NS = "dsh-connect";
44
+ /** Channel-agnostic defaults section of the namespace. */
45
+ export type ConnectSectionDefaults = Record<string, unknown> & {
46
+ language?: string;
47
+ notifyLevel?: string;
48
+ };
49
+ /**
50
+ * The resolved namespace value. Every field is optional: a user who only ever
51
+ * edits `channels` leaves the rest absent, and each adapter applies its own
52
+ * default for anything missing.
53
+ */
54
+ export interface ConnectSection {
55
+ channels?: ChannelName[];
56
+ channelDefaults?: ConnectSectionDefaults;
57
+ feishu?: Record<string, unknown>;
58
+ telegram?: Record<string, unknown>;
59
+ dingtalk?: Record<string, unknown>;
60
+ web?: Record<string, unknown>;
61
+ }
62
+ /**
63
+ * The namespace schema, derived from the same field tables the settings pane
64
+ * renders. Deriving rather than restating them is the point: a field added to
65
+ * `CHANNEL_CONFIG_FIELDS` becomes user-editable and hot-appliable in one edit,
66
+ * and a field that the pane exposes can never be silently unrepresentable in
67
+ * `settings.yaml`.
68
+ *
69
+ * `required(false)` on every field means an absent key stays absent — it does
70
+ * *not* materialize a default. That matters for `dmMode`, whose first option is
71
+ * the permissive one: a schema-provided default would silently open DMs for
72
+ * every user who never touched the field.
73
+ */
74
+ export declare const ConnectSectionSchema: z<Schemastery.ObjectS<{
75
+ channels: z<ChannelName[], ChannelName[]>;
76
+ channelDefaults: z<any, any>;
77
+ feishu: z<any, any>;
78
+ telegram: z<any, any>;
79
+ dingtalk: z<any, any>;
80
+ web: z<any, any>;
81
+ }>, Schemastery.ObjectT<{
82
+ channels: z<ChannelName[], ChannelName[]>;
83
+ channelDefaults: z<any, any>;
84
+ feishu: z<any, any>;
85
+ telegram: z<any, any>;
86
+ dingtalk: z<any, any>;
87
+ web: z<any, any>;
88
+ }>>;
89
+ /**
90
+ * Project a channel config onto the namespace's schema: only declared,
91
+ * non-secret keys, with `channels` always present.
92
+ *
93
+ * `channels` is emitted unconditionally because an absent array resolves to
94
+ * `[]` (schemastery's array default), which would read as "activate nothing".
95
+ *
96
+ * Secret keys are dropped here, not by the schema: `redactSecrets` only strips
97
+ * nodes declared with `role("secret")`, and we declare none — so the projection
98
+ * is what keeps an `appSecret` from a legacy profile out of the settings
99
+ * document and off the RPC.
100
+ *
101
+ * The argument is typed `unknown` because this doubles as the *write* guard:
102
+ * a section submitted through the settings RPC is untrusted JSON, and it must
103
+ * go through the same projection before it can reach the document.
104
+ */
105
+ export declare function sectionOf(config: unknown): ConnectSection;
106
+ /**
107
+ * The slice of the settings service this module calls. Declared structurally so
108
+ * the plugin keeps no dependency on `@deepseek-ai/dsh-settings` — the service is
109
+ * a `dsh-base` row, present whenever dsh-connect loads, and a missing method
110
+ * degrades to "no settings support" rather than a crash.
111
+ */
112
+ export interface SettingsProviderLike {
113
+ installSection?(owner: unknown, ns: string, schema: unknown, entry: unknown, hooks: {
114
+ setSource: (source: () => unknown) => void;
115
+ onChange: () => void;
116
+ }): void;
117
+ /** Resolved value of a registered namespace, `undefined` while unregistered. */
118
+ get?(ns: string): unknown;
119
+ /**
120
+ * Replace a registered namespace's *user* section wholesale (validated by the
121
+ * provider, persisted by whichever file provider is wired up). Resolves after
122
+ * the new value is committed, so a read straight after it is fresh.
123
+ *
124
+ * Replace rather than merge, because the settings pane always submits its
125
+ * complete visible config: a merge would make a cleared field un-clearable
126
+ * (the stale value stays in the user layer and reappears on the next read).
127
+ * The declared-key set is identical for both writers of this namespace (this
128
+ * plugin's pane and the host's own Plugins page, which renders the same
129
+ * schema), so a replace cannot drop anything either surface can produce.
130
+ */
131
+ replace?(ns: string, section: unknown, expectedRevision?: number): Promise<unknown>;
132
+ }
133
+ /** A live handle onto the installed namespace: read the effective section, write a new one. */
134
+ export interface LiveConnectSection {
135
+ /** The section in force right now (base layered under the user's section). */
136
+ read(): ConnectSection;
137
+ /**
138
+ * Replace the user's section with the declared-key projection of `config`.
139
+ * Rejects if the provider's schema refuses the result — the caller is
140
+ * expected to surface that rather than swallow it.
141
+ */
142
+ write(config: unknown): Promise<void>;
143
+ }
144
+ /** Outcome of wiring the namespace. */
145
+ export interface InstallConnectSectionResult {
146
+ /** True when this fiber owns the namespace and receives live updates. */
147
+ live: boolean;
148
+ /** The section in force at install time (resolved, or the entry fallback). */
149
+ section: ConnectSection;
150
+ /**
151
+ * Read/write onto the registration. Present only when this fiber owns a live
152
+ * namespace *and* the provider exposes a write method; otherwise the caller
153
+ * keeps its own fallback (the plugin config, or the legacy state file).
154
+ */
155
+ handle?: LiveConnectSection;
156
+ }
157
+ export interface InstallConnectSectionOptions<Ctx extends LoggerLike> {
158
+ /** Context whose unload releases the registration. */
159
+ owner: Ctx;
160
+ settings: SettingsProviderLike | undefined;
161
+ /** The plugin's own config: the schema's `base` layer and the fallback. */
162
+ entry: ChannelsConfig | null | undefined;
163
+ /** Called with the in-force section now and on every change. */
164
+ onChange: (section: ConnectSection) => void;
165
+ }
166
+ /**
167
+ * Register `dsh-connect` and report the section in force.
168
+ *
169
+ * Never throws: a missing settings service, an already-claimed namespace, or a
170
+ * `settings.yaml` section that fails validation all fall back to the plugin's
171
+ * own config with a warning. A user's typo in a settings document must not be
172
+ * able to stop the bridge from starting.
173
+ */
174
+ export declare function installConnectSection<Ctx extends LoggerLike>(options: InstallConnectSectionOptions<Ctx>): InstallConnectSectionResult;
175
+ //# sourceMappingURL=namespace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"namespace.d.ts","sourceRoot":"","sources":["../../src/settings/namespace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAC;AACzC,OAAO,EAAY,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAOjG,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AAEjD,0DAA0D;AAC1D,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3G;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAyBD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;GAO/B,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,CAoBzD;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc,CAAC,CACb,KAAK,EAAE,OAAO,EACd,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,OAAO,EACd,KAAK,EAAE;QAAE,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,GAC1E,IAAI,CAAC;IACR,gFAAgF;IAChF,GAAG,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACrF;AAED,+FAA+F;AAC/F,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,IAAI,IAAI,cAAc,CAAC;IACvB;;;;OAIG;IACH,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,uCAAuC;AACvC,MAAM,WAAW,2BAA2B;IAC1C,yEAAyE;IACzE,IAAI,EAAE,OAAO,CAAC;IACd,8EAA8E;IAC9E,OAAO,EAAE,cAAc,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,4BAA4B,CAAC,GAAG,SAAS,UAAU;IAClE,sDAAsD;IACtD,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC3C,2EAA2E;IAC3E,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,gEAAgE;IAChE,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;CAC7C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,SAAS,UAAU,EAC1D,OAAO,EAAE,4BAA4B,CAAC,GAAG,CAAC,GACzC,2BAA2B,CAmE7B"}
@@ -0,0 +1,200 @@
1
+ /**
2
+ * The `dsh-connect` user-settings namespace.
3
+ *
4
+ * DSH ships a first-party user-settings seam (`ctx.settings`, backed by
5
+ * `$DSH_HOME/settings.yaml` via `@deepseek-ai/dsh-settings-file`, hot-reloaded,
6
+ * atomic, file-locked, comment-preserving). Registering a namespace is how a
7
+ * plugin makes its configuration user-editable *and effective*: the official
8
+ * Plugins page renders the fields from the schema, writes go through the
9
+ * provider, and `scope.watch()` tells the plugin to re-read.
10
+ *
11
+ * Two properties of that seam shape this module:
12
+ *
13
+ * - **`installSection` is the consumer entry point.** It registers the plugin's
14
+ * own composition entry as the schema's *base* layer while the settings
15
+ * service is present, and falls back to that entry if the service detaches.
16
+ * Bare `register` would make `$DSH_HOME/settings.yaml` the only source and
17
+ * silently drop a YAML-configured profile.
18
+ * - **The schema is applied to `mergeLayers(base, userSection)` at registration
19
+ * time**, so a stored section that fails validation throws out of
20
+ * `installSection`. Every install here is therefore guarded: a hand-edited
21
+ * `settings.yaml` with a typo must degrade to the plugin config, not abort
22
+ * the plugin's `apply`.
23
+ *
24
+ * **Secrets are deliberately not part of this schema.** They live in the DSH
25
+ * credential store (`ctx.credentials`), which is where the onboarding flow and
26
+ * the `FEISHU_*`-style env layering already put them; `settings.yaml` is a
27
+ * plain document users are invited to paste into bug reports. `sectionOf()`
28
+ * projects secret keys *out* of the base for the same reason — schemastery
29
+ * preserves undeclared keys verbatim, so anything left in the base rides out
30
+ * through `describe()` and the settings RPC. The same projection guards the
31
+ * write path (`LiveConnectSection.write`), which is the mirror hazard: an
32
+ * undeclared key in a *submitted* section would be preserved into the document.
33
+ *
34
+ * `installSection` returns nothing, so the write handle it hands back is built
35
+ * here: `setSource` already wires a thunk onto `scope.get()`, which reads the
36
+ * registration's resolved value at call time — live in both directions.
37
+ *
38
+ * @module dsh-connect/settings/namespace
39
+ */
40
+ import z from "@deepseek-ai/schemastery";
41
+ import { CHANNELS } from "./channels.js";
42
+ import { CHANNEL_CONFIG_FIELDS, CHANNEL_DEFAULT_FIELDS, } from "./settings-model.js";
43
+ /** The settings namespace this plugin owns. */
44
+ export const CONNECT_SETTINGS_NS = "dsh-connect";
45
+ /** schemastery schema for one non-secret config field. */
46
+ function fieldSchema(field) {
47
+ switch (field.kind) {
48
+ case "number":
49
+ return z.number().required(false);
50
+ case "boolean":
51
+ return z.boolean().required(false);
52
+ case "select":
53
+ return field.options !== undefined && field.options.length > 0
54
+ ? z.union(field.options.map((option) => z.const(option))).required(false)
55
+ : z.string().required(false);
56
+ default:
57
+ return z.string().required(false);
58
+ }
59
+ }
60
+ /** Build an object schema from a field table (unknown keys pass through). */
61
+ function objectSchema(fields) {
62
+ return z.object(Object.fromEntries(fields.map((field) => [field.key, fieldSchema(field)])));
63
+ }
64
+ /**
65
+ * The namespace schema, derived from the same field tables the settings pane
66
+ * renders. Deriving rather than restating them is the point: a field added to
67
+ * `CHANNEL_CONFIG_FIELDS` becomes user-editable and hot-appliable in one edit,
68
+ * and a field that the pane exposes can never be silently unrepresentable in
69
+ * `settings.yaml`.
70
+ *
71
+ * `required(false)` on every field means an absent key stays absent — it does
72
+ * *not* materialize a default. That matters for `dmMode`, whose first option is
73
+ * the permissive one: a schema-provided default would silently open DMs for
74
+ * every user who never touched the field.
75
+ */
76
+ export const ConnectSectionSchema = z.object({
77
+ channels: z.array(z.union(CHANNELS.map((name) => z.const(name)))).required(false),
78
+ channelDefaults: objectSchema(CHANNEL_DEFAULT_FIELDS).required(false),
79
+ feishu: objectSchema(CHANNEL_CONFIG_FIELDS.feishu).required(false),
80
+ telegram: objectSchema(CHANNEL_CONFIG_FIELDS.telegram).required(false),
81
+ dingtalk: objectSchema(CHANNEL_CONFIG_FIELDS.dingtalk).required(false),
82
+ web: objectSchema(CHANNEL_CONFIG_FIELDS.web).required(false),
83
+ });
84
+ /**
85
+ * Project a channel config onto the namespace's schema: only declared,
86
+ * non-secret keys, with `channels` always present.
87
+ *
88
+ * `channels` is emitted unconditionally because an absent array resolves to
89
+ * `[]` (schemastery's array default), which would read as "activate nothing".
90
+ *
91
+ * Secret keys are dropped here, not by the schema: `redactSecrets` only strips
92
+ * nodes declared with `role("secret")`, and we declare none — so the projection
93
+ * is what keeps an `appSecret` from a legacy profile out of the settings
94
+ * document and off the RPC.
95
+ *
96
+ * The argument is typed `unknown` because this doubles as the *write* guard:
97
+ * a section submitted through the settings RPC is untrusted JSON, and it must
98
+ * go through the same projection before it can reach the document.
99
+ */
100
+ export function sectionOf(config) {
101
+ const source = (config !== null && typeof config === "object" ? config : {});
102
+ const section = { channels: (source.channels ?? CHANNELS) };
103
+ const defaults = {};
104
+ const rawDefaults = (source.channelDefaults ?? {});
105
+ for (const field of CHANNEL_DEFAULT_FIELDS) {
106
+ if (rawDefaults[field.key] !== undefined)
107
+ defaults[field.key] = rawDefaults[field.key];
108
+ }
109
+ if (Object.keys(defaults).length > 0)
110
+ section.channelDefaults = defaults;
111
+ for (const name of CHANNELS) {
112
+ const raw = (source[name] ?? {});
113
+ const projected = {};
114
+ for (const field of CHANNEL_CONFIG_FIELDS[name]) {
115
+ if (raw[field.key] !== undefined)
116
+ projected[field.key] = raw[field.key];
117
+ }
118
+ if (Object.keys(projected).length > 0)
119
+ section[name] = projected;
120
+ }
121
+ return section;
122
+ }
123
+ /**
124
+ * Register `dsh-connect` and report the section in force.
125
+ *
126
+ * Never throws: a missing settings service, an already-claimed namespace, or a
127
+ * `settings.yaml` section that fails validation all fall back to the plugin's
128
+ * own config with a warning. A user's typo in a settings document must not be
129
+ * able to stop the bridge from starting.
130
+ */
131
+ export function installConnectSection(options) {
132
+ const { settings, entry, onChange } = options;
133
+ const entrySection = sectionOf(entry);
134
+ const warn = (message) => {
135
+ options.owner.logger?.warn?.(`connect: ${message}`);
136
+ };
137
+ if (settings?.installSection === undefined) {
138
+ // No settings service: the plugin config is the only source, as it was
139
+ // before this namespace existed.
140
+ onChange(entrySection);
141
+ return { live: false, section: entrySection };
142
+ }
143
+ let source = () => entrySection;
144
+ try {
145
+ settings.installSection(options.owner, CONNECT_SETTINGS_NS, ConnectSectionSchema, entrySection, {
146
+ setSource: (fn) => {
147
+ source = () => fn();
148
+ },
149
+ onChange: () => onChange(source()),
150
+ });
151
+ }
152
+ catch (error) {
153
+ // Two reachable causes, both handled the same way:
154
+ // - a stored section that fails the schema (a hand-edited settings.yaml);
155
+ // - the namespace is already registered by an earlier fiber. `register`
156
+ // is an effect on the *settings service*, which outlives a plugin
157
+ // reload, so a live config reload lands here.
158
+ // `get()` returns the previously resolved value in the second case, which
159
+ // keeps the user's saved settings effective — only the live watch is gone
160
+ // until the process restarts.
161
+ const resolved = readRegistered(settings);
162
+ if (resolved !== undefined) {
163
+ warn(`settings namespace "${CONNECT_SETTINGS_NS}" is already registered ` +
164
+ `(the plugin reloaded without a host restart); using the saved section — ` +
165
+ `restart dsh to re-enable live settings updates`);
166
+ onChange(resolved);
167
+ return { live: false, section: resolved };
168
+ }
169
+ warn(`settings namespace "${CONNECT_SETTINGS_NS}" unavailable (${String(error)}); ` +
170
+ `falling back to the plugin config`);
171
+ onChange(entrySection);
172
+ return { live: false, section: entrySection };
173
+ }
174
+ // `source` is reassigned (not rebound) by `setSource`, so a thunk closing
175
+ // over the variable reads whatever the provider's `scope.get()` returns now.
176
+ const read = () => source();
177
+ const replace = settings.replace?.bind(settings);
178
+ return {
179
+ live: true,
180
+ section: source(),
181
+ ...(replace === undefined
182
+ ? {}
183
+ : {
184
+ handle: {
185
+ read,
186
+ write: async (config) => {
187
+ await replace(CONNECT_SETTINGS_NS, sectionOf(config));
188
+ },
189
+ },
190
+ }),
191
+ };
192
+ }
193
+ /** Read a namespace an earlier fiber registered, if any. */
194
+ function readRegistered(settings) {
195
+ const value = settings.get?.(CONNECT_SETTINGS_NS);
196
+ if (value === null || typeof value !== "object")
197
+ return undefined;
198
+ return value;
199
+ }
200
+ //# sourceMappingURL=namespace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../src/settings/namespace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * How much of a stored secret the settings pane is allowed to show.
3
+ *
4
+ * The user has to be able to *confirm* what they configured — a pane that only
5
+ * ever says 「已配置」 cannot tell a correct appId from a typo — but a browser
6
+ * tab (or a screenshot of one) must never hold a usable credential. The two are
7
+ * reconciled here: the host masks the value **before it crosses the wire**, so
8
+ * what the pane receives is already unusable, and `maskSecret` is the only
9
+ * place that decides how much survives.
10
+ *
11
+ * Deliberately pure and dependency-free: the host (`settings-service`) masks
12
+ * with it and the client bundle imports the same compiled module for the input
13
+ * `type`, so the two can never drift into disagreeing about which keys are
14
+ * confidential.
15
+ *
16
+ * @module dsh-connect/settings/secret-disclosure
17
+ */
18
+ /**
19
+ * - `full` — shown verbatim. For identifiers, not authenticators.
20
+ * - `mask` — head and tail survive, the middle does not.
21
+ * - `url` — a URL whose *query* carries the token, so only that part is masked.
22
+ */
23
+ export type Disclosure = "full" | "mask" | "url";
24
+ /**
25
+ * Per-config-key policy, keyed by the channel config key (`CREDENTIAL`-style
26
+ * keys are unique across channels: no channel has two fields that share a name
27
+ * but want different treatment).
28
+ *
29
+ * `appId` / `clientId` are `full` on purpose. They are **identifiers**: they
30
+ * appear in the URL or body of every outbound API call, are readable in the
31
+ * vendor's own console, and grant nothing without the paired secret. Masking
32
+ * them would cost the user the one thing this pane is for — checking that the
33
+ * id they pasted is the one they meant — and buy no secrecy at all.
34
+ */
35
+ export declare const SECRET_DISCLOSURE: Record<string, Disclosure>;
36
+ /** Policy for a config key; unknown keys are treated as confidential. */
37
+ export declare function disclosureOf(configKey: string): Disclosure;
38
+ /** True when the pane should render this key as a password input. */
39
+ export declare function isMaskedSecret(configKey: string): boolean;
40
+ /**
41
+ * The display preview for one stored secret. Never returns the input: for
42
+ * `mask` and `url` the returned string is lossy by construction, so a caller
43
+ * that echoes it to a browser cannot leak a usable credential.
44
+ *
45
+ * Surrounding whitespace is trimmed first — a pasted value commonly carries a
46
+ * trailing newline, and the mask should describe the stored value, not the
47
+ * paste.
48
+ */
49
+ export declare function maskSecret(configKey: string, value: string): string;
50
+ //# sourceMappingURL=secret-disclosure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-disclosure.d.ts","sourceRoot":"","sources":["../../src/settings/secret-disclosure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAEjD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAQvD,CAAC;AAkBH,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAE1D;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEzD;AAqDD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAWnE"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * How much of a stored secret the settings pane is allowed to show.
3
+ *
4
+ * The user has to be able to *confirm* what they configured — a pane that only
5
+ * ever says 「已配置」 cannot tell a correct appId from a typo — but a browser
6
+ * tab (or a screenshot of one) must never hold a usable credential. The two are
7
+ * reconciled here: the host masks the value **before it crosses the wire**, so
8
+ * what the pane receives is already unusable, and `maskSecret` is the only
9
+ * place that decides how much survives.
10
+ *
11
+ * Deliberately pure and dependency-free: the host (`settings-service`) masks
12
+ * with it and the client bundle imports the same compiled module for the input
13
+ * `type`, so the two can never drift into disagreeing about which keys are
14
+ * confidential.
15
+ *
16
+ * @module dsh-connect/settings/secret-disclosure
17
+ */
18
+ /**
19
+ * Per-config-key policy, keyed by the channel config key (`CREDENTIAL`-style
20
+ * keys are unique across channels: no channel has two fields that share a name
21
+ * but want different treatment).
22
+ *
23
+ * `appId` / `clientId` are `full` on purpose. They are **identifiers**: they
24
+ * appear in the URL or body of every outbound API call, are readable in the
25
+ * vendor's own console, and grant nothing without the paired secret. Masking
26
+ * them would cost the user the one thing this pane is for — checking that the
27
+ * id they pasted is the one they meant — and buy no secrecy at all.
28
+ */
29
+ export const SECRET_DISCLOSURE = Object.freeze({
30
+ appId: "full",
31
+ clientId: "full",
32
+ appSecret: "mask",
33
+ clientSecret: "mask",
34
+ botToken: "mask",
35
+ secret: "mask",
36
+ webhookUrl: "url",
37
+ });
38
+ /** Query parameters whose *value* is a credential and must not be echoed. */
39
+ const SECRET_QUERY_PARAM = /(secret|token|key|sign)/i;
40
+ /** Characters kept visible on each end of a masked value. */
41
+ const EDGE = 4;
42
+ /**
43
+ * Below this length there is no middle to hide — showing 4+4 of a 10-character
44
+ * value would print it in full — so short values collapse to a fixed-width
45
+ * mask, which also avoids leaking the length.
46
+ */
47
+ const MIN_MASKABLE = 12;
48
+ /** The width of the all-bullets mask used for values too short to split. */
49
+ const BULLETS = "••••••";
50
+ /** Policy for a config key; unknown keys are treated as confidential. */
51
+ export function disclosureOf(configKey) {
52
+ return SECRET_DISCLOSURE[configKey] ?? "mask";
53
+ }
54
+ /** True when the pane should render this key as a password input. */
55
+ export function isMaskedSecret(configKey) {
56
+ return disclosureOf(configKey) !== "full";
57
+ }
58
+ /** `head…tail`, or the bullets mask when the value is too short to split. */
59
+ function maskMiddle(value) {
60
+ if (value.length < MIN_MASKABLE)
61
+ return BULLETS;
62
+ return value.slice(0, EDGE) + "…" + value.slice(-EDGE);
63
+ }
64
+ /** Escape a query-parameter name for use inside a `RegExp`. */
65
+ function escapeForRegExp(text) {
66
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
67
+ }
68
+ /**
69
+ * Mask the credential-bearing query parameters of a URL, keeping the origin,
70
+ * path and the innocent parameters readable.
71
+ *
72
+ * Masking such a URL end-to-end would defeat the purpose: DingTalk's webhook is
73
+ * `https://oapi.dingtalk.com/robot/send?access_token=…`, so `https…bcde` tells
74
+ * the user nothing about whether they pasted the right robot. Falls back to the
75
+ * plain mask when the value isn't a parseable URL (a bare token, a typo, an
76
+ * empty-ish string).
77
+ *
78
+ * The masking is a surgical replace on the original text rather than a
79
+ * re-serialize through `URLSearchParams`, which would percent-encode the `…`
80
+ * into `%E2%80%A6` and render the preview unreadable.
81
+ */
82
+ function maskUrl(value) {
83
+ let url;
84
+ try {
85
+ url = new URL(value);
86
+ }
87
+ catch {
88
+ return maskMiddle(value);
89
+ }
90
+ let masked = value;
91
+ for (const key of new Set(url.searchParams.keys())) {
92
+ if (!SECRET_QUERY_PARAM.test(key))
93
+ continue;
94
+ const pattern = new RegExp(`([?&]${escapeForRegExp(key)}=)([^&#]*)`, "g");
95
+ masked = masked.replace(pattern, (whole, prefix, held) => {
96
+ // Tolerate a percent-encoded token: decode to measure it, mask the
97
+ // decoded form, and splice the readable result back into the raw text.
98
+ let decoded = held;
99
+ try {
100
+ decoded = decodeURIComponent(held);
101
+ }
102
+ catch {
103
+ /* not encoded — use as-is */
104
+ }
105
+ return decoded.length === 0 ? whole : prefix + maskMiddle(decoded);
106
+ });
107
+ }
108
+ return masked;
109
+ }
110
+ /**
111
+ * The display preview for one stored secret. Never returns the input: for
112
+ * `mask` and `url` the returned string is lossy by construction, so a caller
113
+ * that echoes it to a browser cannot leak a usable credential.
114
+ *
115
+ * Surrounding whitespace is trimmed first — a pasted value commonly carries a
116
+ * trailing newline, and the mask should describe the stored value, not the
117
+ * paste.
118
+ */
119
+ export function maskSecret(configKey, value) {
120
+ const trimmed = value.trim();
121
+ if (trimmed.length === 0)
122
+ return "";
123
+ switch (disclosureOf(configKey)) {
124
+ case "full":
125
+ return trimmed;
126
+ case "url":
127
+ return maskUrl(trimmed);
128
+ default:
129
+ return maskMiddle(trimmed);
130
+ }
131
+ }
132
+ //# sourceMappingURL=secret-disclosure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-disclosure.js","sourceRoot":"","sources":["../../src/settings/secret-disclosure.ts"],"names":[],"mappings":"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"}
@@ -92,8 +92,25 @@ export interface SettingsForm {
92
92
  channels: ChannelName[];
93
93
  channelDefaults: Record<string, unknown>;
94
94
  channelConfigs: Record<string, Record<string, unknown>>;
95
+ /** Secret inputs the user is *typing*: written on save, never seeded by a read. */
95
96
  secrets: Record<string, Record<string, string>>;
97
+ /**
98
+ * Whether a value is already stored for a secret key, for the placeholder.
99
+ * Separate from `secrets` because the two have opposite lifetimes: presence
100
+ * comes from the snapshot and the typed values start empty and are cleared
101
+ * after a save — a read must never be able to populate an input.
102
+ */
103
+ secretPresence: Record<string, Record<string, boolean>>;
104
+ /**
105
+ * Host-masked previews of the stored secrets, so the user can confirm what
106
+ * they configured. Rendered as read-only text and **never** fed into
107
+ * `secrets`: a masked value is not a credential, and treating one as a value
108
+ * to save would overwrite a working secret with its own mask.
109
+ */
110
+ secretPreviews: Record<string, Record<string, string>>;
96
111
  settingsStatePath?: string;
112
+ /** True when the section lives in the settings namespace: a save is immediate and durable. */
113
+ live: boolean;
97
114
  }
98
115
  /** Build the initial form from a snapshot. */
99
116
  export declare function snapshotToForm(snapshot: SettingsSnapshot): SettingsForm;
@@ -1 +1 @@
1
- {"version":3,"file":"settings-model.d.ts","sourceRoot":"","sources":["../../src/settings/settings-model.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,EAE7B,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsB8B,CAAC;AAEjE,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,EAAE,SAAS,WAAW,EAGxD,CAAC;AAEF,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAclF;AAED,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,YAAY,CAYvE;AASD,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAU3E;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAAE,CAM9G"}
1
+ {"version":3,"file":"settings-model.d.ts","sourceRoot":"","sources":["../../src/settings/settings-model.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,EAE7B,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsB8B,CAAC;AAEjE,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,EAAE,SAAS,WAAW,EAGxD,CAAC;AAEF,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAclF;AAED,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8FAA8F;IAC9F,IAAI,EAAE,OAAO,CAAC;CACf;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,YAAY,CAoBvE;AASD,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAU3E;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAAE,CAM9G"}