@chance722/dsh-inbox 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -0
  3. package/README.zh.md +182 -0
  4. package/cordis.patch.yml +11 -0
  5. package/lib/cli.js +259 -0
  6. package/lib/client.js +3823 -0
  7. package/lib/index.js +3718 -0
  8. package/lib/types/cli.d.ts +49 -0
  9. package/lib/types/client/card.d.ts +52 -0
  10. package/lib/types/client/dock.d.ts +52 -0
  11. package/lib/types/client/heading.d.ts +54 -0
  12. package/lib/types/client/index.d.ts +24 -0
  13. package/lib/types/client/manual.d.ts +22 -0
  14. package/lib/types/client/scheme.d.ts +47 -0
  15. package/lib/types/host/capture.d.ts +117 -0
  16. package/lib/types/host/classify/model.d.ts +112 -0
  17. package/lib/types/host/classify/redact.d.ts +18 -0
  18. package/lib/types/host/classify/rules.d.ts +58 -0
  19. package/lib/types/host/command.d.ts +18 -0
  20. package/lib/types/host/crypto/secret-box.d.ts +60 -0
  21. package/lib/types/host/index.d.ts +21 -0
  22. package/lib/types/host/link-title.d.ts +64 -0
  23. package/lib/types/host/remote/auto-push.d.ts +37 -0
  24. package/lib/types/host/remote/merge.d.ts +80 -0
  25. package/lib/types/host/remote/pull.d.ts +60 -0
  26. package/lib/types/host/remote/push.d.ts +101 -0
  27. package/lib/types/host/remote/remove.d.ts +65 -0
  28. package/lib/types/host/remote/writer.d.ts +43 -0
  29. package/lib/types/host/rpc.d.ts +36 -0
  30. package/lib/types/host/s3/client.d.ts +216 -0
  31. package/lib/types/host/s3/probe.d.ts +28 -0
  32. package/lib/types/host/tools.d.ts +56 -0
  33. package/lib/types/host/ui/config.d.ts +54 -0
  34. package/lib/types/host/vault/lease.d.ts +54 -0
  35. package/lib/types/host/vault/query.d.ts +38 -0
  36. package/lib/types/host/vault/spec.d.ts +193 -0
  37. package/lib/types/host/vault/vault.d.ts +269 -0
  38. package/lib/types/host/webdav/client.d.ts +102 -0
  39. package/lib/types/host/webdav/config.d.ts +129 -0
  40. package/lib/types/host/webdav/probe.d.ts +22 -0
  41. package/lib/types/host/webdav/run.d.ts +34 -0
  42. package/lib/types/shared/constants.d.ts +23 -0
  43. package/lib/types/shared/panel-wire.d.ts +474 -0
  44. package/lib/types/shared/vocabulary.d.ts +39 -0
  45. package/package.json +101 -0
@@ -0,0 +1,474 @@
1
+ /**
2
+ * The wire contract between the inbox panel (browser half) and the vault (host
3
+ * half).
4
+ *
5
+ * Types and constants only: the browser bundle must not pull a schema library
6
+ * or a host package (AGENTS.md constraint 11), so runtime validation lives in
7
+ * `src/host/rpc.ts` and the shapes here stay structural.
8
+ *
9
+ * Transport: exact Fetch routes on the shared `/api` channel, registered
10
+ * through `ctx.connection.fetch.register`, which the physical carrier only
11
+ * dispatches after its Host/Origin trust fence and the signed browser cookie.
12
+ * The panel posts with plain same-origin `fetch`; every answer is an
13
+ * `InboxRpcResult`, so a refusal is an answer rather than a crash.
14
+ */
15
+ import type { Category, CategorySource, Kind } from './vocabulary.js';
16
+ /** Authenticated path prefix every inbox endpoint lives under. */
17
+ export declare const INBOX_API_PREFIX = "/api/inbox";
18
+ /** File one submission (text, image bytes, generic file bytes). */
19
+ export declare const INBOX_ENDPOINT_CAPTURE = "capture";
20
+ /** Page through records with filters. */
21
+ export declare const INBOX_ENDPOINT_LIST = "list";
22
+ /** One record in full, including its attachment metadata. */
23
+ export declare const INBOX_ENDPOINT_DETAIL = "detail";
24
+ /** Replace the editable fields of one record. */
25
+ export declare const INBOX_ENDPOINT_UPDATE = "update";
26
+ /** Soft delete (the recycle bin) and undo. */
27
+ export declare const INBOX_ENDPOINT_DELETE = "delete";
28
+ export declare const INBOX_ENDPOINT_RESTORE = "restore";
29
+ /** Empty the recycle bin for real. */
30
+ export declare const INBOX_ENDPOINT_PURGE = "purge";
31
+ /** Stream one attachment's bytes back to the panel. */
32
+ export declare const INBOX_ENDPOINT_ATTACHMENT = "attachment";
33
+ /** Read or change the WebDAV configuration (the panel's ⚙ form). */
34
+ export declare const INBOX_ENDPOINT_WEBDAV = "webdav";
35
+ /** Pull the remote folder right now, without waiting for the next start. */
36
+ export declare const INBOX_ENDPOINT_PULL = "pull";
37
+ /** Ask the remote a handful of questions and report each answer. */
38
+ export declare const INBOX_ENDPOINT_PROBE = "probe";
39
+ /** Read or write the panel's own preferences (list mode and friends). */
40
+ export declare const INBOX_ENDPOINT_UI = "ui";
41
+ /** Operate on tags across records (currently: drop one everywhere). */
42
+ export declare const INBOX_ENDPOINT_TAGS = "tags";
43
+ export declare const INBOX_ENDPOINT_SECRET = "secret";
44
+ export declare const INBOX_ENDPOINT_PUSH = "push";
45
+ /** What the panel sends to `webdav`: read the status, or save a patch. */
46
+ export interface WebdavRequest {
47
+ action: 'read' | 'save';
48
+ protocol?: RemoteProtocol;
49
+ baseUrl?: string;
50
+ directory?: string;
51
+ username?: string;
52
+ /** Empty string clears the stored password; absent leaves it alone. */
53
+ password?: string;
54
+ endpoint?: string;
55
+ bucket?: string;
56
+ region?: string;
57
+ signatureVersion?: string;
58
+ accessKeyId?: string;
59
+ /** Empty string clears the stored S3 secret; absent leaves it alone. */
60
+ accessKeySecret?: string;
61
+ /**
62
+ * Identity sent as `User-Agent` for the protocol in this same patch; empty
63
+ * string means "the plugin's own". Stored per protocol, because the identity
64
+ * belongs to the *credential* and each protocol has its own.
65
+ */
66
+ userAgent?: string;
67
+ }
68
+ /** Which remote protocol the vault pulls from. */
69
+ export declare const REMOTE_PROTOCOLS: readonly ["webdav", "s3"];
70
+ export type RemoteProtocol = (typeof REMOTE_PROTOCOLS)[number];
71
+ /**
72
+ * The non-secret half of the remote configuration.
73
+ *
74
+ * The wire still calls this endpoint `webdav` for historical reasons; it now
75
+ * carries both protocols, and the field below is what decides which.
76
+ */
77
+ export interface WebdavSettings {
78
+ /** Which client the pull uses. */
79
+ protocol: RemoteProtocol;
80
+ /** WebDAV: base URL, e.g. `https://data.cstcloud.cn/dav`. */
81
+ /** Base URL, e.g. `https://data.cstcloud.cn/dav`. */
82
+ baseUrl: string;
83
+ /** Folder under the base URL; the convention every device drops into. */
84
+ directory: string;
85
+ username: string;
86
+ /** S3: endpoint host, e.g. `https://s3.cstcloud.cn`. */
87
+ endpoint: string;
88
+ /** S3: bucket to read from. */
89
+ bucket: string;
90
+ /** S3: covered by the signature even when the server ignores it. */
91
+ region: string;
92
+ /** S3: only `v4` is implemented; the field exists so the choice is visible. */
93
+ signatureVersion: string;
94
+ /** S3: the identifier, which is not a secret. */
95
+ accessKeyId: string;
96
+ /**
97
+ * What we call ourselves in the S3 request's `User-Agent` header.
98
+ *
99
+ * Empty means "use the plugin's own identity". Some gateways (数据胶囊 among
100
+ * them) bind an access key to an application and reject every request whose
101
+ * caller does not claim to be that application, so this is a real setting
102
+ * rather than decoration.
103
+ */
104
+ userAgent: string;
105
+ /**
106
+ * The same thing for the WebDAV request.
107
+ *
108
+ * A separate value on purpose: the gate is bound to the *credential*, and a
109
+ * user may well have an S3 key bound to one application and a WebDAV account
110
+ * bound to another. Sharing one field made switching protocols silently break
111
+ * both, with an error that reads like a wrong password.
112
+ */
113
+ webdavUserAgent: string;
114
+ }
115
+ /** What the panel needs to render the form and its status. */
116
+ export interface WebdavStatus {
117
+ settings: WebdavSettings;
118
+ /** Whether the WebDAV password is stored — never the value itself. */
119
+ passwordSet: boolean;
120
+ /** Whether the S3 secret is stored — never the value itself. */
121
+ secretSet: boolean;
122
+ settingsAvailable: boolean;
123
+ credentialsAvailable: boolean;
124
+ }
125
+ /** What one pull did, for the panel and for the log. */
126
+ export interface PullResult {
127
+ status: 'ok' | 'unconfigured' | 'failed';
128
+ reason?: string;
129
+ pulled: number;
130
+ /**
131
+ * Records another device pushed that this one took in (new here, or newer
132
+ * than the local copy). Only the merge half of a pull can produce these.
133
+ */
134
+ merged?: number;
135
+ /** Attachment objects the merge had to fetch and admit locally. */
136
+ attachments?: number;
137
+ failed: number;
138
+ /** Files the server listed but we skipped as already-seen. */
139
+ skipped: number;
140
+ /** How many entries the remote listed at all: distinguishes "empty folder"
141
+ * from "everything already ingested". */
142
+ listed: number;
143
+ /** Set when the pull completed. */
144
+ lastPullAt?: string;
145
+ }
146
+ /**
147
+ * What one push did.
148
+ *
149
+ * `partial` exists because the useful answer to "did it work" is often "these
150
+ * twelve did, that one attachment could not be read" — a plain ok/failed would
151
+ * have to throw that away.
152
+ */
153
+ export interface PushResult {
154
+ status: 'ok' | 'partial' | 'unconfigured' | 'failed';
155
+ reason?: string;
156
+ /** Records written to the remote. */
157
+ pushed: number;
158
+ /** Attachment objects written alongside them. */
159
+ attachments: number;
160
+ /** Records the remote already had, untouched since the last push. */
161
+ skipped: number;
162
+ /** Records the push considered (including tombstones). */
163
+ listed: number;
164
+ lastPushAt?: string;
165
+ }
166
+ /** One row of the connection self-test. */
167
+ export interface ProbeRow {
168
+ label: string;
169
+ url: string;
170
+ status: number;
171
+ detail: string;
172
+ }
173
+ /** What the self-test's rows add up to, in one line. */
174
+ export interface ProbeVerdict {
175
+ ok: boolean;
176
+ /** The sentence itself, e.g. 「通道可用:v4 精简(只签 host + date)」. */
177
+ title: string;
178
+ /** The thing to try next when it is not usable. */
179
+ hint?: string;
180
+ }
181
+ /**
182
+ * Turn the self-test's rows into one sentence.
183
+ *
184
+ * The matrix below it exists for the day something breaks (it is what tells a
185
+ * gateway's four same-looking refusals apart), but nobody opening 入库设置 wants
186
+ * to read eight rows to learn whether the channel works. This says yes or no,
187
+ * names the shape that worked when there is one, and — when nothing worked —
188
+ * points at the cause the statuses actually imply.
189
+ *
190
+ * @param rows - the self-test's rows, in the order they were tried.
191
+ * @returns the verdict to show above them.
192
+ */
193
+ export declare function probeVerdict(rows: readonly ProbeRow[]): ProbeVerdict;
194
+ /** Raster formats dsh's own attachment store accepts, and we therefore pass through. */
195
+ export declare const INBOX_IMAGE_TYPES: readonly ["image/png", "image/jpeg", "image/webp", "image/gif"];
196
+ export type InboxImageType = (typeof INBOX_IMAGE_TYPES)[number];
197
+ /** One browser-submitted image: base64 bytes plus the declared media type. */
198
+ export interface WireImage {
199
+ mediaType: InboxImageType;
200
+ data: string;
201
+ name?: string;
202
+ }
203
+ /** One browser-submitted generic file, stored byte-for-byte. */
204
+ export interface WireFile {
205
+ data: string;
206
+ name?: string;
207
+ /**
208
+ * What the browser said this file is, when it said anything.
209
+ *
210
+ * Kept only for media the panel can render (`video/*`, `audio/*`): a generic
211
+ * file's bytes are stored verbatim on the file path, and the media type the
212
+ * panel later serves them with has to be one that cannot execute — an
213
+ * attacker-declared `text/html` or `image/svg+xml` served from the panel's own
214
+ * origin would be a script injection, which is why the host allowlists this
215
+ * instead of trusting it.
216
+ */
217
+ mediaType?: string;
218
+ }
219
+ /** Everything one panel submission can carry. */
220
+ export interface CaptureRequest {
221
+ text?: string;
222
+ images?: WireImage[];
223
+ files?: WireFile[];
224
+ }
225
+ /** How many records a submission stored, and how many merged into existing ones. */
226
+ export interface CaptureResult {
227
+ stored: number;
228
+ /** Records that absorbed this capture instead of a new one being written. */
229
+ merged: number;
230
+ /**
231
+ * The subset of `merged` that was sitting in the recycle bin and came back
232
+ * out — a repeat that *does* change the list, and says something different.
233
+ */
234
+ restored: number;
235
+ }
236
+ /** Which shelf a list is asking about. */
237
+ export type ListScope = 'live' | 'bin';
238
+ /** Filters and paging for one list call. */
239
+ export interface ListRequest {
240
+ scope?: ListScope;
241
+ categories?: Category[];
242
+ /** Filter to the records the user flagged for later. */
243
+ watchLater?: boolean;
244
+ kinds?: Kind[];
245
+ /** Every listed tag must be present. */
246
+ tags?: string[];
247
+ /** Case-insensitive substring over title, text, url, note and tags. */
248
+ text?: string;
249
+ limit?: number;
250
+ offset?: number;
251
+ }
252
+ /** One row of the panel's list. */
253
+ export interface EntrySummary {
254
+ id: string;
255
+ kind: Kind;
256
+ category: Category;
257
+ /** Who chose the category; absent on records written before M5. */
258
+ categorySource?: CategorySource;
259
+ /**
260
+ * The one flag the user manages himself: "I want to come back to this".
261
+ *
262
+ * Replaces the old read/unread pair, which claimed to know something the
263
+ * software cannot know and made every capture start life as "unread".
264
+ */
265
+ watchLater: boolean;
266
+ title?: string;
267
+ /**
268
+ * The headline fetched from the link's own page, when there is one.
269
+ *
270
+ * Second only to a name the user typed, and never a substitute for one: this
271
+ * is somebody else's words, so the pane keeps showing which is which.
272
+ */
273
+ linkTitle?: string;
274
+ /**
275
+ * Why no headline arrived, as a short code (`network:…`, `http:404`,
276
+ * `not-html:…`, `no-title`). The pane translates it — a link that quietly
277
+ * keeps its URL looks like a broken feature rather than a site that refused.
278
+ */
279
+ linkTitleError?: string;
280
+ /**
281
+ * The file name the record arrived with, when it has one.
282
+ *
283
+ * A picture or a file carries no text of its own, so without this its row has
284
+ * nothing to be called but 「(无标题)」 — which is what an uploaded photo used
285
+ * to show. It is a **fallback, never an override**: a name the user typed wins
286
+ * over it, and a credential record ignores it entirely (its heading is fixed;
287
+ * see `src/client/heading.ts`).
288
+ */
289
+ attachmentName?: string;
290
+ /** A short excerpt of the stored text, already trimmed by the host. */
291
+ preview?: string;
292
+ url?: string;
293
+ platform?: string;
294
+ note?: string;
295
+ tags: string[];
296
+ createdAt: string;
297
+ updatedAt: string;
298
+ /** How many attachments the record references. */
299
+ attachmentCount: number;
300
+ /**
301
+ * The first attachment the panel can render itself — the card's picture.
302
+ *
303
+ * Not just pictures any more: a video or an audio file gets a play tile in the
304
+ * same slot and opens in the panel's own lightbox. `previewMime` says which of
305
+ * the three it is, because the card has to draw it differently — and because a
306
+ * `<video>` needs to know it is one before it fetches the bytes.
307
+ */
308
+ previewId?: string;
309
+ /** That attachment's MIME type: `image/*`, `video/*` or `audio/*`. */
310
+ previewMime?: string;
311
+ /** Present while the record sits in the recycle bin. */
312
+ deletedAt?: string;
313
+ }
314
+ /** One attachment's metadata, as the detail view shows it. */
315
+ export interface AttachmentSummary {
316
+ id: string;
317
+ mime: string;
318
+ bytes: number;
319
+ filename?: string;
320
+ width?: number;
321
+ height?: number;
322
+ /** True when the panel can render a thumbnail through the attachment route. */
323
+ image: boolean;
324
+ }
325
+ /** One record in full. */
326
+ export interface EntryDetail extends EntrySummary {
327
+ text?: string;
328
+ attachments: AttachmentSummary[];
329
+ }
330
+ /** A filter value and how many records carry it. */
331
+ export interface FacetCount<T> {
332
+ value: T;
333
+ count: number;
334
+ }
335
+ /** One page of records plus the numbers the header and filter bar show. */
336
+ export interface ListResult {
337
+ entries: EntrySummary[];
338
+ /** Records matching the filters, paging ignored. */
339
+ matched: number;
340
+ /** Live records overall. */
341
+ total: number;
342
+ /** Live records flagged 待看, overall. */
343
+ watchLater: number;
344
+ /** Records sitting in the recycle bin. */
345
+ deleted: number;
346
+ /** Facets are computed over live records only. */
347
+ categories: FacetCount<Category>[];
348
+ tags: FacetCount<string>[];
349
+ }
350
+ export interface DetailRequest {
351
+ id: string;
352
+ }
353
+ export interface DetailResult {
354
+ entry: EntryDetail;
355
+ }
356
+ /** The editable fields. Omitted fields keep their stored value. */
357
+ export interface UpdateRequest {
358
+ id: string;
359
+ category?: Category;
360
+ watchLater?: boolean;
361
+ note?: string;
362
+ title?: string;
363
+ tags?: string[];
364
+ }
365
+ export interface UpdateResult {
366
+ entry: EntrySummary;
367
+ }
368
+ /** How the vault's key stands, and what the panel may do about it. */
369
+ export interface SecretStatus {
370
+ /** A master password exists. Without one, nothing can be sealed. */
371
+ configured: boolean;
372
+ /** The key is in this process's memory: credentials can be read and written. */
373
+ unlocked: boolean;
374
+ /**
375
+ * How many credentials the last unlock moved out of plain text. Zero most of
376
+ * the time; non-zero exactly once, on the unlock after this feature arrived.
377
+ */
378
+ sealed?: number;
379
+ }
380
+ /**
381
+ * What the panel sends to `secret`.
382
+ *
383
+ * `password` travels over the panel's own token-fenced route and is used to
384
+ * derive a key in memory; it is never written anywhere, which is why unlocking
385
+ * is something the user does again after every restart.
386
+ */
387
+ export interface SecretRequest {
388
+ action: 'status' | 'set' | 'unlock' | 'lock';
389
+ password?: string;
390
+ }
391
+ export interface IdRequest {
392
+ id: string;
393
+ }
394
+ export interface IdResult {
395
+ entry: EntrySummary;
396
+ }
397
+ export interface PurgeResult {
398
+ removed: number;
399
+ /** Objects the same cleanup deleted from the remote, when one is configured. */
400
+ remoteRemoved?: number;
401
+ /** True when there is no remote configured — nothing to clean up there. */
402
+ remoteSkipped?: boolean;
403
+ /** What the remote deletion could not do. */
404
+ reason?: string;
405
+ }
406
+ /** Carrier-neutral failure, mirroring Connection's `ConnectionRpcFailure`. */
407
+ export interface InboxRpcFailure {
408
+ code: string;
409
+ message: string;
410
+ details: Record<string, unknown>;
411
+ }
412
+ /** Business outcome of one call: a refused request is an answer, not a crash. */
413
+ export type InboxRpcResult<T> = {
414
+ ok: true;
415
+ value: T;
416
+ } | {
417
+ ok: false;
418
+ error: InboxRpcFailure;
419
+ };
420
+ /** How many records one page holds. */
421
+ export declare const LIST_LIMIT = 50;
422
+ /**
423
+ * How many records the panel asks for at a time.
424
+ *
425
+ * Twelve, not fifty: the list is made of cards now, and a page that has to be
426
+ * scrolled twice before you see the pager is a page you cannot count.
427
+ */
428
+ export declare const PAGE_SIZE = 12;
429
+ /**
430
+ * The two list densities the panel offers.
431
+ *
432
+ * There used to be a third (`rows`, one wide card per line). It went when the
433
+ * user pointed out that the type badge it led with repeated what the metadata
434
+ * line already said, and that two densities cover both habits: `grid` for
435
+ * pictures, `compact` for volume. A stored `rows` is no longer a value this
436
+ * list accepts, so readers fall back to the default — which is `grid`.
437
+ */
438
+ export declare const UI_LIST_MODES: readonly ["grid", "compact"];
439
+ export type UiListMode = (typeof UI_LIST_MODES)[number];
440
+ /** What the panel remembers about itself. */
441
+ export interface UiPrefs {
442
+ /** How the record list is laid out. */
443
+ listMode: UiListMode;
444
+ }
445
+ /** What the panel sends to `ui`: read the preferences, or change them. */
446
+ export interface UiRequest {
447
+ action: 'read' | 'save';
448
+ listMode?: UiListMode;
449
+ }
450
+ /** What the panel sends to `tags`. */
451
+ export interface TagRequest {
452
+ action: 'remove';
453
+ /** The exact tag to strip from every record that carries it. */
454
+ tag: string;
455
+ }
456
+ /** How much stored text a list row shows before the panel truncates it. */
457
+ export declare const PREVIEW_CHARS = 140;
458
+ /** Most attachments one submission may carry; mirrors the composer's own ceiling. */
459
+ export declare const MAX_ATTACHMENTS_PER_SUBMISSION = 20;
460
+ /** Ceiling on the user's own note, so one edit cannot bloat the domain. */
461
+ export declare const MAX_NOTE_CHARS = 2000;
462
+ /** Tag limits: a filter list nobody can read is worse than no tags. */
463
+ export declare const MAX_TAGS = 20;
464
+ export declare const MAX_TAG_CHARS = 40;
465
+ /** Ceiling on one filter's free text. */
466
+ export declare const MAX_FILTER_CHARS = 200;
467
+ /**
468
+ * Ceiling on the name a user gives a record.
469
+ *
470
+ * It is a heading, not a document: the ceiling is here so a runaway paste into
471
+ * the name field cannot bloat the domain, and the card ellipsises long before
472
+ * this number is ever reached.
473
+ */
474
+ export declare const MAX_TITLE_CHARS = 300;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The vault's fixed vocabulary, shared by both halves.
3
+ *
4
+ * No zod here on purpose: the browser half must not pull a schema library into
5
+ * the client bundle (the platform's static module table has no zod). Validation
6
+ * lives in `src/host/vault/spec.ts`.
7
+ */
8
+ /** What the pasted thing physically is. */
9
+ export declare const KINDS: readonly ["text", "link", "image", "file"];
10
+ export type Kind = (typeof KINDS)[number];
11
+ /** How the vault classifies it. Seven top-level buckets, tags carry the rest. */
12
+ export declare const CATEGORIES: readonly ["idea", "article", "media", "image", "document", "secret", "other"];
13
+ export type Category = (typeof CATEGORIES)[number];
14
+ /** Where the record entered the vault. */
15
+ export declare const SOURCES: readonly ["panel", "chat", "webdav", "import"];
16
+ export type Source = (typeof SOURCES)[number];
17
+ /**
18
+ * Who decided the category. Precedence runs user > model > rule: a category the
19
+ * user typed is never overwritten, and a rule never overrides a model pass.
20
+ */
21
+ export declare const CATEGORY_SOURCES: readonly ["rule", "model", "user"];
22
+ export type CategorySource = (typeof CATEGORY_SOURCES)[number];
23
+ /** Display labels. Chinese is the primary UI language. */
24
+ export declare const CATEGORY_LABELS: Record<Category, string>;
25
+ export declare const KIND_LABELS: Record<Kind, string>;
26
+ /**
27
+ * Display labels for the three answers to "who judged this category".
28
+ *
29
+ * The first cut was 「规则 / 模型 / 你」, three bare nouns sitting after a `·` next
30
+ * to the category — they named the *thing* and never the *question*, which is
31
+ * why they read as decoration rather than as a fact about the record. Each
32
+ * label now carries the verb.
33
+ *
34
+ * `user` is 「手动判定」 and not the obvious 「自判定」: 自 at a glance reads as 自动,
35
+ * which says the exact opposite of what happened (the machine judged it).
36
+ */
37
+ export declare const CATEGORY_SOURCE_LABELS: Record<CategorySource, string>;
38
+ /** One sentence per source: what it means, and what it implies about the record. */
39
+ export declare const CATEGORY_SOURCE_HINTS: Record<CategorySource, string>;
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "@chance722/dsh-inbox",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Personal paste inbox for DeepSeek Harness (dsh): capture, classify, browse and retrieve.",
6
+ "keywords": [
7
+ "dsh",
8
+ "dsh-plugin",
9
+ "deepseek-harness",
10
+ "inbox",
11
+ "clipboard",
12
+ "vault"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/Chance722/dsh-inbox.git"
17
+ },
18
+ "homepage": "https://github.com/Chance722/dsh-inbox#readme",
19
+ "bugs": {
20
+ "url": "https://github.com/Chance722/dsh-inbox/issues"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "type": "module",
26
+ "license": "MIT",
27
+ "main": "lib/index.js",
28
+ "types": "lib/types/host/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./lib/types/host/index.d.ts",
32
+ "default": "./lib/index.js"
33
+ },
34
+ "./client": {
35
+ "types": "./lib/types/client/index.d.ts",
36
+ "default": "./lib/client.js"
37
+ },
38
+ "./package.json": "./package.json"
39
+ },
40
+ "files": [
41
+ "lib/index.js",
42
+ "lib/client.js",
43
+ "lib/cli.js",
44
+ "cordis.patch.yml",
45
+ "lib/types/**/*.d.ts"
46
+ ],
47
+ "bin": {
48
+ "dsh-inbox": "./lib/cli.js"
49
+ },
50
+ "dsh": {
51
+ "bundle": {
52
+ "patch": "./cordis.patch.yml"
53
+ },
54
+ "client": {
55
+ "platform": "web",
56
+ "inject": [
57
+ "@deepseek-ai/dsh-client-connection",
58
+ "@deepseek-ai/dsh-client-ui-layout",
59
+ "@deepseek-ai/dsh-client-ui-sidebar"
60
+ ]
61
+ }
62
+ },
63
+ "scripts": {
64
+ "build": "node scripts/build.mjs && tsc -p tsconfig.build.json",
65
+ "typecheck": "tsc --noEmit",
66
+ "test": "vitest run",
67
+ "prepublishOnly": "pnpm build"
68
+ },
69
+ "dependencies": {
70
+ "lucide-react": "^1.47.0",
71
+ "zod": "^4.4.3"
72
+ },
73
+ "peerDependencies": {
74
+ "@deepseek-ai/cordis": "^4.0.2",
75
+ "@deepseek-ai/dsh-attachment": "^0.1.5-rc.2",
76
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.2",
77
+ "@deepseek-ai/dsh-commands": "^0.1.5-rc.2",
78
+ "@deepseek-ai/dsh-storage-domain": "^0.1.5-rc.2",
79
+ "@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
80
+ "@deepseek-ai/dsh-web": "^0.1.5-rc.2"
81
+ },
82
+ "devDependencies": {
83
+ "@deepseek-ai/cordis": "^4.0.2",
84
+ "@deepseek-ai/dsh-attachment": "^0.1.5-rc.2",
85
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.2",
86
+ "@deepseek-ai/dsh-commands": "^0.1.5-rc.2",
87
+ "@deepseek-ai/dsh-storage": "^0.1.5-rc.2",
88
+ "@deepseek-ai/dsh-storage-domain": "^0.1.5-rc.2",
89
+ "@deepseek-ai/dsh-storage-json": "^0.1.5-rc.2",
90
+ "@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
91
+ "@deepseek-ai/dsh-web": "0.1.5-rc.2",
92
+ "@deepseek-ai/schemastery": "3.18.2",
93
+ "@types/node": "^22.10.0",
94
+ "@types/react": "~18.3.1",
95
+ "esbuild": "^0.25.0",
96
+ "react": "^18.2.0",
97
+ "typescript": "^5.7.0",
98
+ "vitest": "^3.0.0",
99
+ "zod": "^4.4.3"
100
+ }
101
+ }