@sparkelf/dsh-knowledge-base 0.0.0-stage → 0.1.1

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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/SKILL.md +147 -0
  3. package/cordis.patch.yml +5 -0
  4. package/i18n/zh.json +10 -0
  5. package/lib/client.js +18308 -0
  6. package/lib/client.js.map +1 -0
  7. package/lib/index.js +6005 -0
  8. package/lib/types/api.d.ts +620 -0
  9. package/lib/types/client/KnowledgeAudit.d.ts +39 -0
  10. package/lib/types/client/KnowledgeContextMenu.d.ts +121 -0
  11. package/lib/types/client/KnowledgeCreateDialog.d.ts +134 -0
  12. package/lib/types/client/KnowledgeFileTree.d.ts +117 -0
  13. package/lib/types/client/KnowledgeHome.d.ts +102 -0
  14. package/lib/types/client/KnowledgeIndexing.d.ts +60 -0
  15. package/lib/types/client/KnowledgeListing.d.ts +174 -0
  16. package/lib/types/client/KnowledgeLoading.d.ts +41 -0
  17. package/lib/types/client/KnowledgeMenuIcon.d.ts +27 -0
  18. package/lib/types/client/KnowledgePanel.d.ts +58 -0
  19. package/lib/types/client/KnowledgePermissionOverview.d.ts +147 -0
  20. package/lib/types/client/KnowledgePermissions.d.ts +47 -0
  21. package/lib/types/client/KnowledgeProgressPanel.d.ts +53 -0
  22. package/lib/types/client/KnowledgeReferencePicker.d.ts +81 -0
  23. package/lib/types/client/KnowledgeSearch.d.ts +83 -0
  24. package/lib/types/client/KnowledgeSectionHeader.d.ts +71 -0
  25. package/lib/types/client/KnowledgeSelectionBar.d.ts +45 -0
  26. package/lib/types/client/KnowledgeSettings.d.ts +96 -0
  27. package/lib/types/client/KnowledgeSettingsSection.d.ts +21 -0
  28. package/lib/types/client/KnowledgeShareDialog.d.ts +70 -0
  29. package/lib/types/client/KnowledgeTemplates.d.ts +43 -0
  30. package/lib/types/client/KnowledgeTrash.d.ts +62 -0
  31. package/lib/types/client/MenuGlyphs.d.ts +37 -0
  32. package/lib/types/client/auditPaging.d.ts +40 -0
  33. package/lib/types/client/departmentFolders.d.ts +57 -0
  34. package/lib/types/client/deployTarget.d.ts +34 -0
  35. package/lib/types/client/index.d.ts +40 -0
  36. package/lib/types/client/insertReadiness.d.ts +60 -0
  37. package/lib/types/client/knowledgeMenuCopy.d.ts +23 -0
  38. package/lib/types/client/locales.d.ts +505 -0
  39. package/lib/types/client/openInSidebar.d.ts +62 -0
  40. package/lib/types/client/ownerLabel.d.ts +39 -0
  41. package/lib/types/client/paging.d.ts +69 -0
  42. package/lib/types/client/permissionRows.d.ts +265 -0
  43. package/lib/types/client/progressRules.d.ts +54 -0
  44. package/lib/types/client/referenceTargets.d.ts +60 -0
  45. package/lib/types/client/roleSummary.d.ts +47 -0
  46. package/lib/types/client/saveAudit.d.ts +87 -0
  47. package/lib/types/client/shareRelations.d.ts +81 -0
  48. package/lib/types/client/useFavorites.d.ts +49 -0
  49. package/lib/types/client/useFileDrop.d.ts +171 -0
  50. package/lib/types/client/useFileDropHook.d.ts +64 -0
  51. package/lib/types/client/usePinnedPaths.d.ts +39 -0
  52. package/lib/types/client/useTreeState.d.ts +46 -0
  53. package/lib/types/client/useViewMode.d.ts +39 -0
  54. package/lib/types/deploy.d.ts +209 -0
  55. package/lib/types/extract.d.ts +144 -0
  56. package/lib/types/extractionQueue.d.ts +154 -0
  57. package/lib/types/index.d.ts +899 -0
  58. package/lib/types/permissions.d.ts +237 -0
  59. package/lib/types/protocol.d.ts +229 -0
  60. package/lib/types/remote.d.ts +105 -0
  61. package/lib/types/trash.d.ts +142 -0
  62. package/lib/types/viewMetadata.d.ts +20 -0
  63. package/lib/types/views.d.ts +923 -0
  64. package/package.json +109 -4
  65. package/skills/kb-organize/SKILL.md +365 -0
  66. package/skills/kb-organize/i18n/zh.json +10 -0
  67. package/README.md +0 -3
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Five knowledge-base permissions and the roles that bundle them.
3
+ *
4
+ * A knowledge base needs one vocabulary everyone can agree on before it can have a
5
+ * permission screen. DataOps already owns the enforcement — this module does NOT
6
+ * re-decide who may read a path. What it adds is the product's own vocabulary on
7
+ * top of that enforcement:
8
+ *
9
+ * - **Five actions**, not three. An editor who may add a document is not
10
+ * automatically allowed to delete one, so `create` and `delete` stay separate.
11
+ * `all` exists because "give this person everything on this folder" is the
12
+ * common case, and spelling it as four grants invites three of them being
13
+ * forgotten.
14
+ * - **Two built-in roles** that are scoped here rather than in DataOps, because
15
+ * "super administrator" means super-administrator-of-the-knowledge-base, not of
16
+ * the platform. Treating it as a global DataOps role would hand out far more
17
+ * than the label promises.
18
+ *
19
+ * ## The one rule that keeps this honest
20
+ *
21
+ * {@link permissionToDataOpsActions} only ever expresses a requirement, and
22
+ * {@link dataOpsActionsToPermissions} only ever NARROWS one. A permission the
23
+ * product shows must be one DataOps can enforce; when the two disagree the
24
+ * narrower answer wins, because showing a permission that will be refused is
25
+ * worse than not showing it.
26
+ *
27
+ * @module @sparkelf/dsh-knowledge-base/permissions
28
+ */
29
+ /**
30
+ * One action a caller can hold on a knowledge path.
31
+ *
32
+ * `all` is deliberately last and deliberately not a fifth privilege: it is the
33
+ * union of the four before it. {@link expandActions} is the only correct way to
34
+ * read it.
35
+ */
36
+ export declare const KNOWLEDGE_ACTIONS: readonly ["read", "create", "update", "delete", "all"];
37
+ /** One knowledge action. */
38
+ export type KnowledgeAction = typeof KNOWLEDGE_ACTIONS[number];
39
+ /** The four concrete actions `all` stands for, in the order a UI should show them. */
40
+ export declare const CONCRETE_ACTIONS: readonly ["read", "create", "update", "delete"];
41
+ /** One concrete action, never `all`. */
42
+ export type ConcreteKnowledgeAction = typeof CONCRETE_ACTIONS[number];
43
+ /** Display name per action, for the permission screen. */
44
+ export declare const ACTION_LABELS: Readonly<Record<KnowledgeAction, {
45
+ zh: string;
46
+ en: string;
47
+ }>>;
48
+ /**
49
+ * Whether a value names a knowledge action.
50
+ *
51
+ * @param value - candidate, typically read from a request body.
52
+ * @returns true when it is one of {@link KNOWLEDGE_ACTIONS}.
53
+ */
54
+ export declare function isKnowledgeAction(value: unknown): value is KnowledgeAction;
55
+ /**
56
+ * Expand any action set into the concrete actions it grants.
57
+ *
58
+ * `all` becomes the four concrete actions; anything else passes through. The
59
+ * result is de-duplicated and ordered by {@link CONCRETE_ACTIONS}, so two callers
60
+ * holding the same privileges always produce the same list and a comparison does
61
+ * not depend on input order.
62
+ *
63
+ * @param actions - whatever a grant or a request holds.
64
+ * @returns the concrete actions, ordered and unique.
65
+ */
66
+ export declare function expandActions(actions: readonly KnowledgeAction[]): ConcreteKnowledgeAction[];
67
+ /**
68
+ * Collapse concrete actions back into the shortest describing set.
69
+ *
70
+ * The inverse of {@link expandActions}: four concrete actions answer `['all']`,
71
+ * because that is what the grant table should store and what the UI should show.
72
+ * A partial set stays explicit so nothing looks broader than it is.
73
+ *
74
+ * @param actions - any actions, including `all`.
75
+ * @returns `['all']` when everything is held, else the concrete subset.
76
+ */
77
+ export declare function collapseActions(actions: readonly KnowledgeAction[]): KnowledgeAction[];
78
+ /**
79
+ * Whether a held set covers every action another set requires.
80
+ *
81
+ * Used by the permission screen to answer "does this grant already give them
82
+ * that", and by the role editor to refuse a downgrade that was meant to be a
83
+ * no-op.
84
+ *
85
+ * @param held - actions the caller currently has.
86
+ * @param required - actions the operation needs.
87
+ * @returns true when every required action is held.
88
+ */
89
+ export declare function coversActions(held: readonly KnowledgeAction[], required: readonly KnowledgeAction[]): boolean;
90
+ /**
91
+ * Translate a product permission into the DataOps actions that would satisfy it.
92
+ *
93
+ * A grant request is a REQUIREMENT, not a decision: these actions are what the
94
+ * caller asks DataOps to record, and DataOps still decides whether it may.
95
+ *
96
+ * @param action - one knowledge action, `all` included.
97
+ * @returns the DataOps actions to record, or an empty list for an unknown action.
98
+ */
99
+ export declare function permissionToDataOpsActions(action: KnowledgeAction): string[];
100
+ /**
101
+ * Translate the DataOps actions a caller actually holds into product permissions.
102
+ *
103
+ * This NARROWS: only actions listed in {@link DATA_OPS_ACTION_MAP} contribute, so a
104
+ * `comment` grant shows nothing rather than something the plugin cannot enforce.
105
+ *
106
+ * @param dataOpsActions - actions DataOps reported as allowed.
107
+ * @returns the product permissions those actions justify.
108
+ */
109
+ export declare function dataOpsActionsToPermissions(dataOpsActions: readonly string[]): KnowledgeAction[];
110
+ /**
111
+ * Identifier of the built-in role that may do anything anywhere.
112
+ *
113
+ * The prefix keeps it from colliding with a DataOps role code: this is a role OF
114
+ * THE KNOWLEDGE BASE, and a DataOps role that happens to be called `admin` must
115
+ * not silently acquire its powers.
116
+ */
117
+ export declare const SUPER_ADMIN_ROLE = "knowledge-super-admin";
118
+ /** Identifier of the built-in role that administers one department's documents. */
119
+ export declare const DEPARTMENT_ADMIN_ROLE = "knowledge-dept-admin";
120
+ /** One built-in role. */
121
+ export interface BuiltInRole {
122
+ /** Stable id, stored as the grant's `principalId`. */
123
+ id: string;
124
+ /** Display name. */
125
+ name: {
126
+ zh: string;
127
+ en: string;
128
+ };
129
+ /** What the role can do, for the role list. */
130
+ description: {
131
+ zh: string;
132
+ en: string;
133
+ };
134
+ /** Actions it confers on a path it covers. */
135
+ actions: readonly KnowledgeAction[];
136
+ /** Whether the role's coverage depends on the caller's own department. */
137
+ departmentScoped: boolean;
138
+ /**
139
+ * Whether it may be removed.
140
+ *
141
+ * Always false for the built-ins: a knowledge base with no super administrator
142
+ * has nobody able to restore one, which is a state no screen should be able to
143
+ * reach.
144
+ */
145
+ removable: false;
146
+ }
147
+ /** The roles every knowledge base starts with. */
148
+ export declare const BUILT_IN_ROLES: readonly BuiltInRole[];
149
+ /**
150
+ * Look a built-in role up by id.
151
+ *
152
+ * @param id - role id, typically a grant's `principalId`.
153
+ * @returns the role, or undefined when the id names no built-in.
154
+ */
155
+ export declare function builtInRoleOf(id: string): BuiltInRole | undefined;
156
+ /**
157
+ * The path prefix a department-scoped role covers.
158
+ *
159
+ * With an organization path the prefix is the caller's OWN department folder inside
160
+ * the nested layout, `department/<ancestor>/.../<own>/`. A department administrator
161
+ * manages their own department's documents, not an ancestor's: a parent folder holds
162
+ * sibling departments they have no business in.
163
+ *
164
+ * The nested prefix ends with the caller's own code and starts at the organization
165
+ * root, because the path is root-first. That is why this returns a whole prefix
166
+ * rather than a bare code: the caller's code alone appears at several depths.
167
+ *
168
+ * Without a usable path (an older backend) the legacy single-level
169
+ * `department/<code>/` is returned, so an un-upgraded deployment keeps exactly the
170
+ * permissions it had.
171
+ *
172
+ * @param departmentCode - the caller's own department code.
173
+ * @param departmentPathCodes - the caller's organization path, root first.
174
+ * @returns The prefix, or undefined when there is no department to scope to.
175
+ */
176
+ export declare function departmentScopeOf(departmentCode: string | undefined, departmentPathCodes?: readonly string[]): string | undefined;
177
+ /**
178
+ * Whether one built-in role covers one path for one caller.
179
+ *
180
+ * The prefix is compared as a PREFIX of the finished path, so a department
181
+ * administrator's own folder covers everything inside it but never a peer's. The
182
+ * nested prefix carries the caller's whole chain, so `department/A/B/` covers
183
+ * `department/A/B/2026/计划.md` and never `department/A/C/...`: sharing the ancestor
184
+ * `A` is not enough to reach a sibling.
185
+ *
186
+ * @param roleId - the built-in role's id.
187
+ * @param relPath - path relative to the knowledge root.
188
+ * @param departmentCode - the caller's department, when they have one.
189
+ * @param departmentPathCodes - the caller's organization path, root first.
190
+ * @returns true when the role confers its actions on that path for that caller.
191
+ */
192
+ export declare function builtInRoleCoversPath(roleId: string, relPath: string, departmentCode: string | undefined, departmentPathCodes?: readonly string[]): boolean;
193
+ /**
194
+ * Every action a role confers on a path, for a given caller.
195
+ *
196
+ * @param roleId - role id.
197
+ * @param relPath - path relative to the knowledge root.
198
+ * @param departmentCode - the caller's department, when they have one.
199
+ * @param departmentPathCodes - the caller's organization path, root first.
200
+ * @returns the concrete actions, empty when the role does not cover the path.
201
+ */
202
+ export declare function builtInRoleActionsOn(roleId: string, relPath: string, departmentCode: string | undefined, departmentPathCodes?: readonly string[]): ConcreteKnowledgeAction[];
203
+ /** The result of asking whether a role may do something to a path. */
204
+ export interface RoleDecision {
205
+ /** Whether the role covers the path at all. */
206
+ covered: boolean;
207
+ /** Actions granted on it. */
208
+ actions: ConcreteKnowledgeAction[];
209
+ /** One sentence explaining a refusal, for the UI to show verbatim. */
210
+ reason?: string;
211
+ }
212
+ /**
213
+ * Explain whether a role grants an action on a path.
214
+ *
215
+ * The explanation is part of the answer rather than something a screen composes
216
+ * afterwards: a refusal that says only "denied" cannot be acted on, and the two
217
+ * reasons here are very different problems — the wrong department is a
218
+ * permission request, an unknown role is a stale screen.
219
+ *
220
+ * @param roleId - role id.
221
+ * @param relPath - path relative to the knowledge root.
222
+ * @param action - the action being attempted.
223
+ * @param departmentCode - the caller's department, when they have one.
224
+ * @param departmentPathCodes - the caller's organization path, root first.
225
+ * @returns the decision, with a reason whenever it is negative.
226
+ */
227
+ export declare function decideRoleAction(roleId: string, relPath: string, action: KnowledgeAction, departmentCode: string | undefined, departmentPathCodes?: readonly string[]): RoleDecision;
228
+ /**
229
+ * Whether a role id names a built-in role rather than one an administrator made.
230
+ *
231
+ * The permission screen needs this to decide whether to offer a delete button,
232
+ * and a grant does not record the difference: only the id does.
233
+ *
234
+ * @param roleId - role id from a grant.
235
+ * @returns true when it is one of {@link BUILT_IN_ROLES}.
236
+ */
237
+ export declare function isBuiltInRole(roleId: string): boolean;
@@ -0,0 +1,229 @@
1
+ /**
2
+ * The knowledge-base protocol contract.
3
+ *
4
+ * This module is the Client's authoritative declaration of what the
5
+ * knowledge-base capability needs from its backend. A DataOps deployment
6
+ * implements these endpoints; this package never talks to the NAS directly and
7
+ * never decides authorization itself.
8
+ *
9
+ * Design boundary (see the design textbook, chapter 9):
10
+ * - Transport is HTTPS with the DataOps session credential.
11
+ * - Retrieval is NOT part of this protocol. Content search runs over the
12
+ * mounted copy with the official `grep` and `read` tools, because the mount
13
+ * makes the remote tree a local path. Only configuration, authorization
14
+ * changes, and index generation cross this boundary.
15
+ * - File transfer is NOT part of this protocol. Files are read and written
16
+ * through the mount point.
17
+ *
18
+ * @module @sparkelf/dsh-knowledge-base/protocol
19
+ */
20
+ /** How this capability identifies its remote backend to the user. */
21
+ export interface KnowledgeBackend {
22
+ /** Base URL of the DataOps knowledge API, without a trailing slash. */
23
+ baseUrl: string;
24
+ /** Credential reference holding the DataOps session token. */
25
+ credentialRef: string;
26
+ }
27
+ /**
28
+ * Resolved mount layout, as reported by the backend.
29
+ *
30
+ * The backend owns these values because it also owns the authorization model:
31
+ * the view root a user sees is derived from their grants, not from client
32
+ * configuration.
33
+ */
34
+ export interface KnowledgeConfig {
35
+ /** Human-facing identifier of the storage host, for display only. */
36
+ host: string;
37
+ /**
38
+ * Absolute path of the authoritative file tree on the storage host.
39
+ * Never mounted into a container.
40
+ */
41
+ actualRoot: string;
42
+ /**
43
+ * Absolute path of the calling user's view tree on the storage host.
44
+ * This is the directory a container mounts.
45
+ */
46
+ viewRoot: string;
47
+ /** Absolute path where the view tree is mounted inside the container. */
48
+ mountPoint: string;
49
+ /** NFS export host, when the deployment uses the NFS transport. */
50
+ nfsServer?: string;
51
+ /** Read-only summaries the deployment wants shown beside the connection. */
52
+ notes?: string[];
53
+ }
54
+ /**
55
+ * One access grant.
56
+ *
57
+ * The fields mirror the DataOps permission store, whose resource type and id are
58
+ * free-form strings, so a knowledge path needs no schema change: the file is
59
+ * addressed as the resource id under the knowledge resource type.
60
+ */
61
+ export interface KnowledgeGrant {
62
+ /** Stable identifier of this grant. */
63
+ id: string;
64
+ /** Path relative to {@link KnowledgeConfig.actualRoot}. */
65
+ relPath: string;
66
+ /** Whether this entry authorizes a file or a whole directory. */
67
+ kind: 'file' | 'directory';
68
+ /** Principal kind the grant names. */
69
+ principalType: KnowledgeGrantPrincipalType;
70
+ /** Principal id within its kind: a user id, role code, department code, or group id. */
71
+ principalId: string;
72
+ /** Permission the grant confers. */
73
+ action: string;
74
+ /** Display name of whoever granted it. */
75
+ grantedBy: string;
76
+ /** ISO-8601 timestamp of the grant. */
77
+ grantedAt: string;
78
+ }
79
+ /** Principal kinds the DataOps permission store accepts. */
80
+ export type KnowledgeGrantPrincipalType = 'user' | 'role' | 'department' | 'group';
81
+ /** Progress of one index job. */
82
+ export interface KnowledgeIndexJob {
83
+ /** Job identifier used for polling. */
84
+ jobId: string;
85
+ /** Terminal or in-flight state. */
86
+ state: 'queued' | 'running' | 'succeeded' | 'failed';
87
+ /** Total files selected for this run. */
88
+ total: number;
89
+ /** Files processed so far. */
90
+ done: number;
91
+ /** Relative paths that failed, with their reasons. */
92
+ failures: Array<{
93
+ relPath: string;
94
+ reason: string;
95
+ }>;
96
+ }
97
+ /**
98
+ * Aggregated view and index state.
99
+ *
100
+ * Both halves are derived from the mounted tree rather than reported by a
101
+ * backend: the count walks the mount, and indexing is the tree itself because
102
+ * retrieval reads it with grep and read. A null count means the mount is absent,
103
+ * which is an expected state before the storage host is attached.
104
+ */
105
+ export type { KnowledgeSshView } from './api.ts';
106
+ export interface KnowledgeStatus {
107
+ /** Files visible under the mount point, or null when nothing is mounted there. */
108
+ visibleFiles: number | null;
109
+ /** ISO-8601 timestamp of the last successful view synchronization. */
110
+ lastSyncAt: string | null;
111
+ /** The in-flight index job, when one is running. */
112
+ indexing: {
113
+ running: boolean;
114
+ jobId?: string;
115
+ progress?: number;
116
+ };
117
+ }
118
+ /**
119
+ * One grant to create.
120
+ *
121
+ * A path is addressed as the resource id under the knowledge resource type, and
122
+ * the principal travels as a (kind, id) pair because the permission store keys
123
+ * grants that way. The plugin therefore never builds a policy of its own: it
124
+ * names the principal and lets DataOps decide whether that principal exists and
125
+ * what the caller may do.
126
+ */
127
+ export interface KnowledgeGrantRequest {
128
+ /** Path relative to {@link KnowledgeConfig.actualRoot}. */
129
+ relPath: string;
130
+ /** Principal kind the grant names. */
131
+ principalType: KnowledgeGrantPrincipalType;
132
+ /** Principal id within its kind. */
133
+ principalId: string;
134
+ /** Permission to confer, defaulting to read. */
135
+ action?: string;
136
+ }
137
+ /** One grant to remove. The fields identify the row; the rest is untouched. */
138
+ export interface KnowledgeRevokeRequest {
139
+ /** Path relative to {@link KnowledgeConfig.actualRoot}. */
140
+ relPath: string;
141
+ /** Principal kind the grant names. */
142
+ principalType: KnowledgeGrantPrincipalType;
143
+ /** Principal id within its kind. */
144
+ principalId: string;
145
+ /** Permission the grant confers; identifies the row together with the above. */
146
+ action: string;
147
+ }
148
+ /** Request to index one path, or the whole tree when `relPath` is omitted. */
149
+ export interface KnowledgeIndexRequest {
150
+ /** Path relative to {@link KnowledgeConfig.actualRoot}. */
151
+ relPath?: string;
152
+ /** Rebuild even when the source seems unchanged. */
153
+ force?: boolean;
154
+ }
155
+ /**
156
+ * Endpoint paths this protocol requires a backend to implement.
157
+ *
158
+ * Kept as data rather than free strings so Client and backend can share one
159
+ * list and drift is caught by the type checker and by tests.
160
+ *
161
+ * Every path here was matched against a controller in `dataops/backend` rather
162
+ * than assumed: the global prefix is `api`, and the permission routes belong to
163
+ * `system/resource-permissions`. An earlier revision of this list invented an
164
+ * `/api/kb/*` family that no controller ever served, so every call answered 404
165
+ * while the plugin reported itself merely unconfigured. Config, status, and
166
+ * indexing have no backend counterpart at all and are derived on the Host
167
+ * instead; only the grants routes are remote.
168
+ */
169
+ export declare const ENDPOINTS: {
170
+ /**
171
+ * Knowledge file grants. The permission store keys a grant by resource type
172
+ * and id, and a knowledge path is addressed as the id under
173
+ * {@link KB_RESOURCE_TYPE}; the kind travels in the request body.
174
+ */
175
+ readonly grants: "/api/system/resource-permissions/grants";
176
+ /**
177
+ * Principal directories, so the permission surface can offer the subjects that
178
+ * actually exist instead of asking an operator to transcribe a UUID.
179
+ *
180
+ * The permission store matches a user by primary key and everything else by
181
+ * `code`, so these are the only places a valid identifier can come from. Each
182
+ * one is a controller in `dataops/backend` and answers `{ items: [...] }`
183
+ * except departments, which nests its rows under `departments`.
184
+ */
185
+ readonly users: "/api/system/users";
186
+ readonly roles: "/api/system/roles";
187
+ readonly departments: "/api/system/departments";
188
+ readonly groups: "/api/system/groups";
189
+ /**
190
+ * Resolve one path's permissions for the CALLING user.
191
+ *
192
+ * This controller is `@SelfScoped`, so the answer is always about the caller and
193
+ * no other identity can be asked about. That is what lets a per-user view be
194
+ * built without this plugin evaluating a grant itself: it asks, per path,
195
+ * whether the caller may read it, and links what the answer allows.
196
+ */
197
+ readonly resolve: "/api/system/resource-permissions/resolve";
198
+ /** The authenticated caller, whose id keys their view directory. */
199
+ readonly me: "/api/auth/me";
200
+ /**
201
+ * Templates a reader can start from, which are DataOps wiki pages carrying the
202
+ * `report_template` category (`resource-wiki-core.controller.ts`, `@Get('pages/templates')`).
203
+ *
204
+ * The plugin keeps no template store: the pages a team already wrote ARE the
205
+ * templates, and duplicating them here would create a second copy to keep in
206
+ * step. The endpoint answers an empty list today because no page carries that
207
+ * category yet.
208
+ */
209
+ readonly templates: "/api/resources/wiki/pages/templates";
210
+ };
211
+ /**
212
+ * Resource type the knowledge base records its paths under.
213
+ *
214
+ * The DataOps permission store treats the type as a free-form string, so the
215
+ * knowledge tree needs no schema change to be grantable. The value is the one
216
+ * `dataops/backend/src/system/resource-permissions/resource-permissions.types.ts`
217
+ * exports for this purpose.
218
+ */
219
+ export declare const KB_RESOURCE_TYPE = "knowledge-base-file";
220
+ /**
221
+ * Whether a backend serves remote knowledge state.
222
+ *
223
+ * Config, status, and indexing are derived on the Host from the mounted tree and
224
+ * the local index, so they need no remote counterpart. Only the grant routes
225
+ * reach DataOps, and this constant names the one path they use.
226
+ */
227
+ export declare const REMOTE_GRANTS_PATH: "/api/system/resource-permissions/grants";
228
+ /** Join a base URL and an endpoint path without doubling or dropping the slash. */
229
+ export declare function joinUrl(baseUrl: string, path: string): string;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Deployment-runner seam for the knowledge base.
3
+ *
4
+ * Deploying MinerU or an NFS export means running commands on a remote host.
5
+ * This plugin does not implement SSH: the workspace already ships
6
+ * `@sparkelf/dsh-ssh-manager`, which owns connection handling, host-key
7
+ * verification, credential storage, and command execution. Reusing it keeps one
8
+ * authoritative credential store and one host inventory, per the managed
9
+ * DataOps authorization rule in AGENTS.md.
10
+ *
11
+ * The seam is intentionally narrow: callers pass a host id and a command, and
12
+ * receive stdout/stderr/exit code. Swapping the transport (for example to an
13
+ * in-process call) requires no change to callers.
14
+ *
15
+ * @module @sparkelf/dsh-knowledge-base/remote
16
+ */
17
+ /** Result of one remote command. */
18
+ export interface RemoteCommandResult {
19
+ /** Process exit status, or null when the command died from a signal. */
20
+ exitCode: number | null;
21
+ /** Captured standard output. */
22
+ stdout: string;
23
+ /** Captured standard error. */
24
+ stderr: string;
25
+ /** True when the transport truncated the output. */
26
+ truncated: boolean;
27
+ }
28
+ /** One SSH host as the manager reports it. Secrets are never included. */
29
+ export interface RemoteHost {
30
+ id: string;
31
+ name: string;
32
+ hostname: string;
33
+ username: string;
34
+ port: number;
35
+ /** Host-key fingerprint the manager will verify against. */
36
+ knownHostFingerprint: string | null;
37
+ /** Whether a usable credential is configured for this host. */
38
+ credentialConfigured: boolean;
39
+ }
40
+ /** Transport the deployment steps run over. */
41
+ export interface RemoteRunner {
42
+ /** List the hosts available for deployment. */
43
+ listHosts(): Promise<RemoteHost[]>;
44
+ /**
45
+ * Run one command on a host.
46
+ * @param hostId - id from {@link RemoteHost}.
47
+ * @param command - shell command executed by the remote login shell.
48
+ * @param timeoutMs - abort the command after this long.
49
+ */
50
+ execute(hostId: string, command: string, timeoutMs?: number): Promise<RemoteCommandResult>;
51
+ }
52
+ /** Why a remote call failed. */
53
+ export declare class RemoteError extends Error {
54
+ readonly code: 'manager-unavailable' | 'unknown-host' | 'transport';
55
+ constructor(message: string, code: RemoteError['code'], options?: ErrorOptions);
56
+ }
57
+ /** Options accepted by {@link sshManagerRunner}. */
58
+ export interface SshManagerRunnerOptions {
59
+ /**
60
+ * Origin the ssh-manager API answers on, for example `http://127.0.0.1:3080`.
61
+ * Required on the Host: `fetch` in Node rejects a relative URL outright, so a
62
+ * path-only base cannot work there even though the browser would accept it.
63
+ */
64
+ origin: string;
65
+ /** Request timeout for inventory reads. */
66
+ timeoutMs?: number;
67
+ }
68
+ /**
69
+ * The listener facts {@link originFrom} reads.
70
+ *
71
+ * `protocol` is optional because the seam added it after the peer range this
72
+ * package declares against; a deployment without it speaks plain HTTP, which is
73
+ * also the default here.
74
+ */
75
+ export interface WebServerFacts {
76
+ /** Configured bind address. */
77
+ host: string;
78
+ /** Live listening port. */
79
+ port: number;
80
+ /** `http:` or `https:`, when the seam reports it. */
81
+ protocol?: 'http:' | 'https:';
82
+ }
83
+ /**
84
+ * Build the origin this process's own HTTP server answers on.
85
+ *
86
+ * A Host-side plugin cannot call its sibling's route with a relative path: Node's
87
+ * `fetch` throws `Failed to parse URL` before any request is made, which is why
88
+ * the runner needs this. `ctx.webServer` exposes the listener's real host, port,
89
+ * and protocol, so the value follows configuration rather than a guessed port.
90
+ *
91
+ * @param webServer - the live listener facts.
92
+ * @returns an origin suitable for {@link SshManagerRunnerOptions.origin}.
93
+ */
94
+ export declare function originFrom(webServer: WebServerFacts): string;
95
+ /**
96
+ * Build a runner that reaches SSH hosts through the ssh-manager HTTP API.
97
+ *
98
+ * The manager already exposes `hosts.list`-equivalent state and
99
+ * `hosts.execute`; this adapter only adapts shapes, so a change to the
100
+ * manager's own auth or approval rules takes effect here without a code change.
101
+ *
102
+ * @param options - optional base URL and timeout overrides.
103
+ * @returns a runner bound to the manager API.
104
+ */
105
+ export declare function sshManagerRunner(options: SshManagerRunnerOptions): RemoteRunner;