dsh-reference-anything 0.3.1 → 0.3.2

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 (107) hide show
  1. package/LICENSE +22 -22
  2. package/NOTICE.md +52 -25
  3. package/README.md +334 -285
  4. package/cordis.patch.yml +89 -18
  5. package/lib/client.js +13530 -517
  6. package/lib/contract.d.ts +155 -3
  7. package/lib/contract.js +68 -4
  8. package/lib/download-directory.d.ts +18 -0
  9. package/lib/download-directory.js +55 -0
  10. package/lib/errors.d.ts +4 -0
  11. package/lib/host.d.ts +96 -3
  12. package/lib/host.js +88 -5
  13. package/lib/index.d.ts +22 -2
  14. package/lib/index.js +57 -7
  15. package/lib/opencli.d.ts +5 -3
  16. package/lib/opencli.js +13 -6
  17. package/lib/openlist/host.d.ts +157 -0
  18. package/lib/openlist/host.js +873 -0
  19. package/lib/openlist/index.d.ts +84 -0
  20. package/lib/openlist/index.js +507 -0
  21. package/lib/render.d.ts +12 -7
  22. package/lib/render.js +27 -21
  23. package/lib/sources/cloud-drive/cache.d.ts +60 -0
  24. package/lib/sources/cloud-drive/cache.js +107 -0
  25. package/lib/sources/cloud-drive/index.d.ts +190 -0
  26. package/lib/sources/cloud-drive/index.js +526 -0
  27. package/lib/sources/cloud-drive/providers/http.d.ts +27 -0
  28. package/lib/sources/cloud-drive/providers/http.js +73 -0
  29. package/lib/sources/cloud-drive/providers/openlist.d.ts +59 -0
  30. package/lib/sources/cloud-drive/providers/openlist.js +535 -0
  31. package/lib/sources/cloud-drive/registry.d.ts +53 -0
  32. package/lib/sources/cloud-drive/registry.js +101 -0
  33. package/lib/sources/cloud-drive/types.d.ts +169 -0
  34. package/lib/sources/cloud-drive/types.js +17 -0
  35. package/lib/sources/file.d.ts +2 -0
  36. package/lib/sources/file.js +2 -0
  37. package/lib/sources/local-agent/adapters/claude-code.d.ts +16 -0
  38. package/lib/sources/local-agent/adapters/claude-code.js +171 -0
  39. package/lib/sources/local-agent/adapters/codex.d.ts +20 -0
  40. package/lib/sources/local-agent/adapters/codex.js +204 -0
  41. package/lib/sources/local-agent/adapters/cursor.d.ts +23 -0
  42. package/lib/sources/local-agent/adapters/cursor.js +121 -0
  43. package/lib/sources/local-agent/adapters/gemini-cli.d.ts +20 -0
  44. package/lib/sources/local-agent/adapters/gemini-cli.js +141 -0
  45. package/lib/sources/local-agent/adapters/grokbuild.d.ts +22 -0
  46. package/lib/sources/local-agent/adapters/grokbuild.js +76 -0
  47. package/lib/sources/local-agent/adapters/hermes.d.ts +19 -0
  48. package/lib/sources/local-agent/adapters/hermes.js +85 -0
  49. package/lib/sources/local-agent/adapters/kimi.d.ts +28 -0
  50. package/lib/sources/local-agent/adapters/kimi.js +261 -0
  51. package/lib/sources/local-agent/adapters/openclaw.d.ts +21 -0
  52. package/lib/sources/local-agent/adapters/openclaw.js +120 -0
  53. package/lib/sources/local-agent/adapters/opencode.d.ts +44 -0
  54. package/lib/sources/local-agent/adapters/opencode.js +236 -0
  55. package/lib/sources/local-agent/adapters/pi.d.ts +26 -0
  56. package/lib/sources/local-agent/adapters/pi.js +242 -0
  57. package/lib/sources/local-agent/adapters/qoder.d.ts +19 -0
  58. package/lib/sources/local-agent/adapters/qoder.js +93 -0
  59. package/lib/sources/local-agent/adapters/reasonix.d.ts +23 -0
  60. package/lib/sources/local-agent/adapters/reasonix.js +90 -0
  61. package/lib/sources/local-agent/adapters/shared.d.ts +163 -0
  62. package/lib/sources/local-agent/adapters/shared.js +415 -0
  63. package/lib/sources/local-agent/adapters/sqlite-shared.d.ts +92 -0
  64. package/lib/sources/local-agent/adapters/sqlite-shared.js +215 -0
  65. package/lib/sources/local-agent/adapters/zcode.d.ts +41 -0
  66. package/lib/sources/local-agent/adapters/zcode.js +159 -0
  67. package/lib/sources/local-agent/index.d.ts +222 -0
  68. package/lib/sources/local-agent/index.js +656 -0
  69. package/lib/sources/local-agent/page.d.ts +55 -0
  70. package/lib/sources/local-agent/page.js +83 -0
  71. package/lib/sources/local-agent/path.d.ts +8 -0
  72. package/lib/sources/local-agent/path.js +43 -0
  73. package/lib/sources/local-agent/registry.d.ts +68 -0
  74. package/lib/sources/local-agent/registry.js +116 -0
  75. package/lib/sources/local-agent/scan.d.ts +175 -0
  76. package/lib/sources/local-agent/scan.js +472 -0
  77. package/lib/sources/local-agent/sqlite.d.ts +45 -0
  78. package/lib/sources/local-agent/sqlite.js +113 -0
  79. package/lib/sources/local-agent/store.d.ts +93 -0
  80. package/lib/sources/local-agent/store.js +113 -0
  81. package/lib/sources/local-agent/types.d.ts +338 -0
  82. package/lib/sources/local-agent/types.js +20 -0
  83. package/lib/sources/web-chat/index.d.ts +5 -1
  84. package/lib/sources/web-chat/index.js +47 -8
  85. package/lib/store/spec.d.ts +50 -0
  86. package/lib/store/spec.js +2 -2
  87. package/lib/sync/index.d.ts +1 -0
  88. package/lib/sync/index.js +28 -8
  89. package/lib/tool.d.ts +5 -0
  90. package/lib/tool.js +418 -45
  91. package/lib/typert.js +13 -1
  92. package/lib/types.d.ts +28 -2
  93. package/lib/update.d.ts +6 -0
  94. package/lib/update.js +29 -1
  95. package/lib/web.js +5 -1
  96. package/lib/wire.d.ts +80 -0
  97. package/lib/wire.js +30 -1
  98. package/opencli-plugin/chatgpt.js +30 -27
  99. package/opencli-plugin/claude.js +12 -8
  100. package/opencli-plugin/common.js +266 -187
  101. package/opencli-plugin/deepseek.js +8 -5
  102. package/opencli-plugin/gemini.js +42 -38
  103. package/opencli-plugin/grok.js +22 -21
  104. package/opencli-plugin/kimi.js +34 -31
  105. package/opencli-plugin/opencli-plugin.json +6 -6
  106. package/opencli-plugin/package.json +8 -8
  107. package/package.json +200 -185
package/lib/contract.d.ts CHANGED
@@ -1,5 +1,49 @@
1
1
  import { z } from 'zod';
2
2
  import type { InvocationDescriptor } from '@deepseek-ai/dsh-typert-protocol';
3
+ /**
4
+ * One local-agent transcript as the `@` menu sees it.
5
+ *
6
+ * `id` is the source-scoped `kind:relPath`, not a `dsh-ref:` URI — the client
7
+ * wraps it, the way it does for web chats, so the `'local-agent'` source string
8
+ * stays hardcoded in exactly one place. Deliberately carries no `origin`: the
9
+ * absolute path adds nothing a menu row can use and everything a screen-share
10
+ * leaks.
11
+ */
12
+ export declare const agentCandidateSchema: z.ZodReadonly<z.ZodObject<{
13
+ id: z.ZodString;
14
+ kind: z.ZodString;
15
+ label: z.ZodString;
16
+ provider: z.ZodString;
17
+ updatedAt: z.ZodOptional<z.ZodNumber>;
18
+ }, z.core.$strip>>;
19
+ export declare const agentStatsSchema: z.ZodReadonly<z.ZodObject<{
20
+ agent: z.ZodEnum<{
21
+ kimi: "kimi";
22
+ "claude-code": "claude-code";
23
+ codex: "codex";
24
+ cursor: "cursor";
25
+ qoder: "qoder";
26
+ reasonix: "reasonix";
27
+ openclaw: "openclaw";
28
+ grokbuild: "grokbuild";
29
+ hermes: "hermes";
30
+ "gemini-cli": "gemini-cli";
31
+ pi: "pi";
32
+ opencode: "opencode";
33
+ mimocode: "mimocode";
34
+ zcode: "zcode";
35
+ }>;
36
+ conversations: z.ZodNumber;
37
+ }, z.core.$strip>>;
38
+ export declare const driveCandidateSchema: z.ZodReadonly<z.ZodObject<{
39
+ id: z.ZodString;
40
+ label: z.ZodString;
41
+ provider: z.ZodString;
42
+ origin: z.ZodOptional<z.ZodString>;
43
+ updatedAt: z.ZodOptional<z.ZodNumber>;
44
+ searchIncomplete: z.ZodOptional<z.ZodBoolean>;
45
+ isDirectory: z.ZodOptional<z.ZodBoolean>;
46
+ }, z.core.$strict>>;
3
47
  export declare const searchInputSchema: z.ZodReadonly<z.ZodObject<{
4
48
  query: z.ZodString;
5
49
  provider: z.ZodOptional<z.ZodEnum<{
@@ -92,6 +136,8 @@ export declare const packageUpdateStatusSchema: z.ZodReadonly<z.ZodObject<{
92
136
  latestVersion: z.ZodString;
93
137
  updateAvailable: z.ZodBoolean;
94
138
  checkedAt: z.ZodNumber;
139
+ releaseNotes: z.ZodOptional<z.ZodString>;
140
+ releaseUrl: z.ZodOptional<z.ZodString>;
95
141
  error: z.ZodOptional<z.ZodString>;
96
142
  }, z.core.$strip>>;
97
143
  export declare const packageUpdateResultSchema: z.ZodReadonly<z.ZodObject<{
@@ -274,9 +320,6 @@ export declare const clearProviderInputSchema: z.ZodReadonly<z.ZodObject<{
274
320
  kimi: "kimi";
275
321
  }>;
276
322
  }, z.core.$strip>>;
277
- export declare const clearOlderInputSchema: z.ZodReadonly<z.ZodObject<{
278
- days: z.ZodNumber;
279
- }, z.core.$strip>>;
280
323
  export declare const providerSyncStateSchema: z.ZodReadonly<z.ZodObject<{
281
324
  provider: z.ZodEnum<{
282
325
  chatgpt: "chatgpt";
@@ -296,4 +339,113 @@ export declare const providerSyncStateSchema: z.ZodReadonly<z.ZodObject<{
296
339
  lastCompleteScanAt: z.ZodString;
297
340
  error: z.ZodString;
298
341
  }, z.core.$strip>>;
342
+ export declare const openListStatusSchema: z.ZodReadonly<z.ZodObject<{
343
+ state: z.ZodEnum<{
344
+ failed: "failed";
345
+ running: "running";
346
+ install: "install";
347
+ downloading: "downloading";
348
+ upgrade: "upgrade";
349
+ }>;
350
+ installed: z.ZodBoolean;
351
+ mode: z.ZodOptional<z.ZodEnum<{
352
+ managed: "managed";
353
+ external: "external";
354
+ }>>;
355
+ version: z.ZodOptional<z.ZodString>;
356
+ endpoint: z.ZodOptional<z.ZodString>;
357
+ supportsRollback: z.ZodBoolean;
358
+ upgradeAvailable: z.ZodBoolean;
359
+ newerVersion: z.ZodOptional<z.ZodBoolean>;
360
+ error: z.ZodOptional<z.ZodString>;
361
+ }, z.core.$strict>>;
362
+ export declare const openListDriverFieldSchema: z.ZodReadonly<z.ZodObject<{
363
+ name: z.ZodString;
364
+ label: z.ZodString;
365
+ type: z.ZodEnum<{
366
+ number: "number";
367
+ boolean: "boolean";
368
+ text: "text";
369
+ password: "password";
370
+ select: "select";
371
+ }>;
372
+ secret: z.ZodBoolean;
373
+ required: z.ZodBoolean;
374
+ hasDefault: z.ZodOptional<z.ZodBoolean>;
375
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
376
+ options: z.ZodOptional<z.ZodArray<z.ZodReadonly<z.ZodObject<{
377
+ label: z.ZodString;
378
+ value: z.ZodString;
379
+ }, z.core.$strip>>>>;
380
+ }, z.core.$strict>>;
381
+ export declare const openListDriverSchema: z.ZodReadonly<z.ZodObject<{
382
+ name: z.ZodString;
383
+ description: z.ZodOptional<z.ZodString>;
384
+ quickAuth: z.ZodBoolean;
385
+ fields: z.ZodArray<z.ZodReadonly<z.ZodObject<{
386
+ name: z.ZodString;
387
+ label: z.ZodString;
388
+ type: z.ZodEnum<{
389
+ number: "number";
390
+ boolean: "boolean";
391
+ text: "text";
392
+ password: "password";
393
+ select: "select";
394
+ }>;
395
+ secret: z.ZodBoolean;
396
+ required: z.ZodBoolean;
397
+ hasDefault: z.ZodOptional<z.ZodBoolean>;
398
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
399
+ options: z.ZodOptional<z.ZodArray<z.ZodReadonly<z.ZodObject<{
400
+ label: z.ZodString;
401
+ value: z.ZodString;
402
+ }, z.core.$strip>>>>;
403
+ }, z.core.$strict>>>;
404
+ }, z.core.$strict>>;
405
+ export declare const openListMountSchema: z.ZodReadonly<z.ZodObject<{
406
+ id: z.ZodString;
407
+ name: z.ZodString;
408
+ driver: z.ZodString;
409
+ enabled: z.ZodBoolean;
410
+ status: z.ZodOptional<z.ZodEnum<{
411
+ error: "error";
412
+ ready: "ready";
413
+ disabled: "disabled";
414
+ }>>;
415
+ error: z.ZodOptional<z.ZodString>;
416
+ capacityUsed: z.ZodOptional<z.ZodNumber>;
417
+ capacityTotal: z.ZodOptional<z.ZodNumber>;
418
+ indexStatus: z.ZodOptional<z.ZodEnum<{
419
+ complete: "complete";
420
+ failed: "failed";
421
+ running: "running";
422
+ idle: "idle";
423
+ }>>;
424
+ indexProgress: z.ZodOptional<z.ZodNumber>;
425
+ indexCount: z.ZodOptional<z.ZodNumber>;
426
+ }, z.core.$strict>>;
427
+ export declare const openListExternalConnectSchema: z.ZodReadonly<z.ZodObject<{
428
+ endpoint: z.ZodString;
429
+ username: z.ZodOptional<z.ZodString>;
430
+ password: z.ZodOptional<z.ZodString>;
431
+ token: z.ZodOptional<z.ZodString>;
432
+ }, z.core.$strict>>;
433
+ export declare const openListMountCreateSchema: z.ZodReadonly<z.ZodObject<{
434
+ id: z.ZodOptional<z.ZodString>;
435
+ mountPath: z.ZodString;
436
+ driver: z.ZodString;
437
+ addition: z.ZodRecord<z.ZodString, z.ZodUnknown>;
438
+ order: z.ZodOptional<z.ZodNumber>;
439
+ remark: z.ZodOptional<z.ZodString>;
440
+ }, z.core.$strict>>;
441
+ export declare const openListMountInputSchema: z.ZodReadonly<z.ZodObject<{
442
+ id: z.ZodString;
443
+ }, z.core.$strict>>;
444
+ export declare const openListDisableMountSchema: z.ZodReadonly<z.ZodObject<{
445
+ id: z.ZodString;
446
+ disabled: z.ZodOptional<z.ZodBoolean>;
447
+ }, z.core.$strict>>;
448
+ export declare const openListUpgradeInputSchema: z.ZodReadonly<z.ZodObject<{
449
+ rollback: z.ZodOptional<z.ZodBoolean>;
450
+ }, z.core.$strict>>;
299
451
  export declare const REFERENCE_ANYTHING_INVOCATIONS: readonly InvocationDescriptor[];
package/lib/contract.js CHANGED
@@ -1,5 +1,31 @@
1
1
  import { z } from 'zod';
2
- import { providerSchema, settingsRecordSchema } from "./wire.js";
2
+ import { localAgentSchema, providerSchema, settingsRecordSchema } from "./wire.js";
3
+ /**
4
+ * One local-agent transcript as the `@` menu sees it.
5
+ *
6
+ * `id` is the source-scoped `kind:relPath`, not a `dsh-ref:` URI — the client
7
+ * wraps it, the way it does for web chats, so the `'local-agent'` source string
8
+ * stays hardcoded in exactly one place. Deliberately carries no `origin`: the
9
+ * absolute path adds nothing a menu row can use and everything a screen-share
10
+ * leaks.
11
+ */
12
+ export const agentCandidateSchema = z.object({ id: z.string(), kind: z.string(), label: z.string(), provider: z.string(), updatedAt: z.number().optional() }).readonly();
13
+ export const agentStatsSchema = z.object({ agent: localAgentSchema, conversations: z.number().int().nonnegative() }).readonly();
14
+ // `origin` is the drive's own display path, shown under the candidate. It is
15
+ // never a download URL: those are signed with the account's credential and
16
+ // must not cross this wire.
17
+ export const driveCandidateSchema = z.object({
18
+ id: z.string(), label: z.string(), provider: z.string(),
19
+ origin: z.string().optional(), updatedAt: z.number().optional(),
20
+ searchIncomplete: z.boolean().optional(), isDirectory: z.boolean().optional(),
21
+ }).strict().readonly();
22
+ /**
23
+ * Binds the calling session to the invocation, so the Host can scope a search
24
+ * to the session's cwd. Reintroduced here after 0.3.0 handed file and session
25
+ * lookup back to DSH's native Remotes and removed its last other caller.
26
+ */
27
+ const agentLookup = { name: 'agent', wire: 'agentId', source: 'lookup', lookup: 'agent',
28
+ codec: { mode: 'strict', typeSymbol: '@deepseek-ai/dsh-session/types#SessionId', schema: z.string().min(1) } };
3
29
  export const searchInputSchema = z.object({
4
30
  query: z.string(), provider: providerSchema.optional(), limit: z.number().int().min(1).max(100),
5
31
  }).readonly();
@@ -24,7 +50,7 @@ export const openCliDiscoverySchema = z.object({
24
50
  }).readonly();
25
51
  export const packageUpdateStatusSchema = z.object({
26
52
  currentVersion: z.string(), latestVersion: z.string(), updateAvailable: z.boolean(),
27
- checkedAt: z.number().int().nonnegative(), error: z.string().optional(),
53
+ checkedAt: z.number().int().nonnegative(), releaseNotes: z.string().max(12_000).optional(), releaseUrl: z.string().url().optional(), error: z.string().optional(),
28
54
  }).readonly();
29
55
  export const packageUpdateResultSchema = z.object({
30
56
  version: z.string(), restartRequired: z.boolean(),
@@ -62,12 +88,39 @@ export const storageStatsSchema = z.object({
62
88
  remoteMissing: z.number().int().nonnegative().default(0), oldAccountConversations: z.number().int().nonnegative().default(0),
63
89
  }).readonly();
64
90
  export const clearProviderInputSchema = z.object({ provider: providerSchema }).readonly();
65
- export const clearOlderInputSchema = z.object({ days: z.number().int().min(1).max(36500) }).readonly();
66
91
  export const providerSyncStateSchema = z.object({
67
92
  provider: providerSchema, status: z.enum(['idle', 'running', 'cancelled', 'failed']),
68
93
  lastSyncAt: z.string(), lastCompleteScanAt: z.string(), error: z.string(),
69
94
  }).readonly();
95
+ // OpenList wire DTOs intentionally omit every credential, storage addition,
96
+ // raw/signed URL, and server-supplied diagnostic. The Host keeps those in its
97
+ // restricted credential file and translates failures to a generic message.
98
+ export const openListStatusSchema = z.object({
99
+ state: z.enum(['install', 'downloading', 'running', 'failed', 'upgrade']), installed: z.boolean(),
100
+ mode: z.enum(['managed', 'external']).optional(), version: z.string().optional(), endpoint: z.string().optional(),
101
+ supportsRollback: z.boolean(), upgradeAvailable: z.boolean(), newerVersion: z.boolean().optional(), error: z.string().optional(),
102
+ }).strict().readonly();
103
+ export const openListDriverFieldSchema = z.object({
104
+ name: z.string(), label: z.string(), type: z.enum(['text', 'password', 'number', 'boolean', 'select']), secret: z.boolean(), required: z.boolean(), hasDefault: z.boolean().optional(), default: z.union([z.string(), z.number(), z.boolean()]).optional(),
105
+ options: z.array(z.object({ label: z.string(), value: z.string() }).readonly()).optional(),
106
+ }).strict().readonly();
107
+ export const openListDriverSchema = z.object({ name: z.string(), description: z.string().optional(), quickAuth: z.boolean(), fields: z.array(openListDriverFieldSchema) }).strict().readonly();
108
+ export const openListMountSchema = z.object({
109
+ id: z.string(), name: z.string(), driver: z.string(), enabled: z.boolean(), status: z.enum(['ready', 'disabled', 'error']).optional(), error: z.string().optional(),
110
+ capacityUsed: z.number().nonnegative().optional(), capacityTotal: z.number().nonnegative().optional(),
111
+ indexStatus: z.enum(['idle', 'running', 'complete', 'failed']).optional(), indexProgress: z.number().min(0).max(1).optional(), indexCount: z.number().nonnegative().optional(),
112
+ }).strict().readonly();
113
+ export const openListExternalConnectSchema = z.object({ endpoint: z.string().min(1), username: z.string().optional(), password: z.string().optional(), token: z.string().optional() }).strict().readonly();
114
+ export const openListMountCreateSchema = z.object({ id: z.string().min(1).optional(), mountPath: z.string().min(1), driver: z.string().min(1), addition: z.record(z.string(), z.unknown()), order: z.number().int().optional(), remark: z.string().optional() }).strict().readonly();
115
+ export const openListMountInputSchema = z.object({ id: z.string().min(1) }).strict().readonly();
116
+ export const openListDisableMountSchema = z.object({ id: z.string().min(1), disabled: z.boolean().optional() }).strict().readonly();
117
+ export const openListUpgradeInputSchema = z.object({ rollback: z.boolean().optional() }).strict().readonly();
70
118
  export const REFERENCE_ANYTHING_INVOCATIONS = [
119
+ descriptor('agentSearch', [agentLookup, { name: 'input', wire: 'input', source: 'json', codec: strict('AgentSearchInput', z.object({ query: z.string(), limit: z.number().int().min(1).max(100) }).readonly()) }], strict('AgentCandidate[]', z.array(agentCandidateSchema)), true),
120
+ descriptor('agentStats', [], strict('AgentStats[]', z.array(agentStatsSchema)), true),
121
+ // No session lookup, unlike `agentSearch`: a drive is the same drive from
122
+ // every workspace, so there is nothing to scope the query to.
123
+ descriptor('driveSearch', [{ name: 'input', wire: 'input', source: 'json', codec: strict('DriveSearchInput', z.object({ query: z.string(), limit: z.number().int().min(1).max(100) }).readonly()) }], strict('DriveCandidate[]', z.array(driveCandidateSchema)), true),
71
124
  descriptor('search', [{ name: 'input', wire: 'input', source: 'json', codec: strict('SearchInput', searchInputSchema) }], strict('SearchResult[]', z.array(searchResultSchema)), true),
72
125
  descriptor('health', [], strict('Health', healthSchema), true),
73
126
  descriptor('quickHealth', [], strict('Health', healthSchema), true),
@@ -85,14 +138,25 @@ export const REFERENCE_ANYTHING_INVOCATIONS = [
85
138
  descriptor('syncCancel', [{ name: 'input', wire: 'input', source: 'json', codec: strict('JobInput', jobInputSchema) }], strict('Boolean', z.boolean())),
86
139
  descriptor('settingsGet', [], strict('Settings', settingsRecordSchema)),
87
140
  descriptor('settingsUpdate', [{ name: 'settings', wire: 'settings', source: 'json', codec: strict('Settings', settingsRecordSchema) }], strict('Settings', settingsRecordSchema)),
141
+ descriptor('openDownloadDirectory', [], strict('Boolean', z.boolean()), true),
88
142
  descriptor('browse', [{ name: 'input', wire: 'input', source: 'json', codec: strict('BrowseInput', browseInputSchema) }], strict('BrowsePage', browsePageSchema), true),
89
143
  descriptor('deleteConversation', [{ name: 'input', wire: 'input', source: 'json', codec: strict('DeleteInput', deleteInputSchema) }], strict('Boolean', z.boolean()), true),
90
144
  descriptor('storageStats', [], strict('StorageStats', storageStatsSchema)),
91
145
  descriptor('clearProvider', [{ name: 'input', wire: 'input', source: 'json', codec: strict('ClearProviderInput', clearProviderInputSchema) }], strict('Count', z.number().int().nonnegative()), true),
92
- descriptor('clearOlder', [{ name: 'input', wire: 'input', source: 'json', codec: strict('ClearOlderInput', clearOlderInputSchema) }], strict('Count', z.number().int().nonnegative()), true),
93
146
  descriptor('clearRemoteMissing', [], strict('Count', z.number().int().nonnegative()), true),
94
147
  descriptor('clearOldAccounts', [], strict('Count', z.number().int().nonnegative()), true),
95
148
  descriptor('syncStates', [], strict('ProviderSyncState[]', z.array(providerSyncStateSchema))),
149
+ descriptor('openListStatus', [], strict('OpenListStatus', openListStatusSchema), true),
150
+ descriptor('openListInstall', [], strict('OpenListStatus', openListStatusSchema), true),
151
+ descriptor('openListUpgrade', [{ name: 'input', wire: 'input', source: 'json', codec: strict('OpenListUpgradeInput', openListUpgradeInputSchema) }], strict('OpenListStatus', openListStatusSchema), true),
152
+ descriptor('openListConnectExternal', [{ name: 'input', wire: 'input', source: 'json', codec: strict('OpenListExternalConnect', openListExternalConnectSchema) }], strict('OpenListStatus', openListStatusSchema), true),
153
+ descriptor('openListDisconnect', [], strict('OpenListStatus', openListStatusSchema), true),
154
+ descriptor('openListDrivers', [], strict('OpenListDriver[]', z.array(openListDriverSchema)), true),
155
+ descriptor('openListMounts', [], strict('OpenListMount[]', z.array(openListMountSchema)), true),
156
+ descriptor('openListCreateMount', [{ name: 'input', wire: 'input', source: 'json', codec: strict('OpenListMountCreate', openListMountCreateSchema) }], strict('OpenListMount', openListMountSchema), true),
157
+ descriptor('openListDisableMount', [{ name: 'input', wire: 'input', source: 'json', codec: strict('OpenListDisableMountInput', openListDisableMountSchema) }], strict('Boolean', z.boolean()), true),
158
+ descriptor('openListRemoveMount', [{ name: 'input', wire: 'input', source: 'json', codec: strict('OpenListMountInput', openListMountInputSchema) }], strict('Boolean', z.boolean()), true),
159
+ descriptor('openListReindex', [{ name: 'input', wire: 'input', source: 'json', codec: strict('OpenListMountInput', openListMountInputSchema) }], strict('OpenListReindex', z.object({ supported: z.boolean(), reason: z.string().optional() }).readonly()), true),
96
160
  ];
97
161
  function strict(type, schema) {
98
162
  return { mode: 'strict', typeSymbol: `dsh-reference-anything#${type}`, schema };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Unlike `path.isAbsolute`, this excludes Windows paths rooted on the current
3
+ * drive (`\foo`) and drive-relative paths (`C:foo`). Those forms can silently
4
+ * resolve somewhere other than the directory displayed in Settings.
5
+ */
6
+ export declare function isFullyQualifiedDownloadDirectory(value: string, platform?: NodeJS.Platform): boolean;
7
+ /** Append an mkdtemp prefix without lexically resolving symlinks or `..`. */
8
+ export declare function appendDownloadDirectoryPrefix(directory: string, prefix: string, platform?: NodeJS.Platform): string;
9
+ export interface DownloadDirectoryValidationOptions {
10
+ platform?: NodeJS.Platform;
11
+ lstatPath?: (path: string) => Promise<{
12
+ isDirectory(): boolean;
13
+ isSymbolicLink(): boolean;
14
+ }>;
15
+ accessPath?: (path: string, mode: number) => Promise<void>;
16
+ }
17
+ /** Validate the user-selected base without changing its filesystem semantics. */
18
+ export declare function validateDownloadDirectory(value: string, options?: DownloadDirectoryValidationOptions): Promise<string>;
@@ -0,0 +1,55 @@
1
+ import { constants } from 'node:fs';
2
+ import { access, lstat } from 'node:fs/promises';
3
+ import { ReferenceAnythingError } from "./errors.js";
4
+ /**
5
+ * Unlike `path.isAbsolute`, this excludes Windows paths rooted on the current
6
+ * drive (`\foo`) and drive-relative paths (`C:foo`). Those forms can silently
7
+ * resolve somewhere other than the directory displayed in Settings.
8
+ */
9
+ export function isFullyQualifiedDownloadDirectory(value, platform = process.platform) {
10
+ if (platform !== 'win32')
11
+ return value.startsWith('/');
12
+ // Device and NT namespaces bypass ordinary filesystem parsing and can name
13
+ // objects outside the selected volume/share boundary. This feature only
14
+ // accepts conventional DOS drive paths and UNC shares.
15
+ if (/^\\\\[?.][\\/]/.test(value))
16
+ return false;
17
+ if (/^[A-Za-z]:[\\/]/.test(value))
18
+ return true;
19
+ return /^\\\\[^\\/]+[\\/][^\\/]+(?:[\\/].*)?$/.test(value);
20
+ }
21
+ /** Append an mkdtemp prefix without lexically resolving symlinks or `..`. */
22
+ export function appendDownloadDirectoryPrefix(directory, prefix, platform = process.platform) {
23
+ if (!isFullyQualifiedDownloadDirectory(directory, platform) || directory.includes('\0')) {
24
+ throw new ReferenceAnythingError('Cloud-drive download directory is invalid', 'REFERENCE_INVALID_CONFIG');
25
+ }
26
+ if (prefix === '' || prefix === '.' || prefix === '..' || prefix.includes('\0') || /[\\/]/.test(prefix)) {
27
+ throw new ReferenceAnythingError('Cloud-drive temporary-directory prefix is invalid', 'REFERENCE_INVALID_CONFIG');
28
+ }
29
+ const hasSeparator = platform === 'win32' ? /[\\/]$/.test(directory) : directory.endsWith('/');
30
+ return `${directory}${hasSeparator ? '' : platform === 'win32' ? '\\' : '/'}${prefix}`;
31
+ }
32
+ /** Validate the user-selected base without changing its filesystem semantics. */
33
+ export async function validateDownloadDirectory(value, options = {}) {
34
+ if (value === '')
35
+ return '';
36
+ const platform = options.platform ?? process.platform;
37
+ if (value.includes('\0') || !isFullyQualifiedDownloadDirectory(value, platform)) {
38
+ throw new ReferenceAnythingError('Cloud-drive download directory must be a fully-qualified absolute path', 'REFERENCE_INVALID_CONFIG');
39
+ }
40
+ const lstatPath = options.lstatPath ?? (async (path) => lstat(path));
41
+ const accessPath = options.accessPath ?? (async (path, mode) => access(path, mode));
42
+ try {
43
+ const metadata = await lstatPath(value);
44
+ if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
45
+ throw new ReferenceAnythingError('Cloud-drive download directory must name a real directory, not a symbolic link', 'REFERENCE_INVALID_CONFIG');
46
+ }
47
+ await accessPath(value, constants.W_OK | constants.X_OK);
48
+ }
49
+ catch (cause) {
50
+ if (cause instanceof ReferenceAnythingError)
51
+ throw cause;
52
+ throw new ReferenceAnythingError('Cloud-drive download directory does not exist or is not writable', 'REFERENCE_INVALID_CONFIG', { cause });
53
+ }
54
+ return value;
55
+ }
package/lib/errors.d.ts CHANGED
@@ -16,8 +16,12 @@ export type ReferenceErrorCode =
16
16
  | 'REFERENCE_NOT_FOUND'
17
17
  /** A live browser read targeted a conversation owned by another account. */
18
18
  | 'REFERENCE_ACCOUNT_MISMATCH'
19
+ /** The active provider account temporarily refused identity verification due to rate limiting. */
20
+ | 'PROVIDER_RATE_LIMIT'
19
21
  /** The source failed while reading; its own error is the `cause`. */
20
22
  | 'REFERENCE_READ_FAILED'
23
+ /** A transcript whose format must be read whole is larger than the scan budget. */
24
+ | 'REFERENCE_TRANSCRIPT_TOO_LARGE'
21
25
  /** The caller cancelled before the read settled. */
22
26
  | 'REFERENCE_CANCELLED'
23
27
  /** A `dsh-ref:` URI was malformed, non-canonical, or not a reference at all. */
package/lib/host.d.ts CHANGED
@@ -2,8 +2,52 @@ import type { Context } from '@deepseek-ai/cordis';
2
2
  import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
3
3
  import type { ChatProvider, SettingsRecord } from './store/spec.ts';
4
4
  import type { SyncMode } from './sync/index.ts';
5
+ import type { Agent } from '@deepseek-ai/dsh-agent';
5
6
  export declare class ReferenceAnythingRemote extends TypertRemoteService {
6
7
  constructor(ctx: Context);
8
+ /**
9
+ * Discovery for the `@` menu's local-agent group.
10
+ *
11
+ * Resolved optionally rather than injected: `reference-anything-web` must
12
+ * still load in a profile where the local-agent plugin is switched off, and an
13
+ * absent service means an empty group, not a broken composer. Workspace
14
+ * scoping takes the session's own cwd, so a Web server serving projects from
15
+ * anywhere still scopes to the project the user is in.
16
+ */
17
+ agentSearch(agent: Agent, input: {
18
+ query: string;
19
+ limit: number;
20
+ }, signal: AbortSignal): Promise<{
21
+ searchIncomplete?: boolean | undefined;
22
+ updatedAt?: number | undefined;
23
+ id: string;
24
+ kind: string;
25
+ label: string;
26
+ provider: string;
27
+ }[]>;
28
+ agentStats(signal: AbortSignal): Promise<{
29
+ agent: import("./sources/local-agent/types.ts").AgentKind;
30
+ conversations: number;
31
+ }[]>;
32
+ /**
33
+ * Discovery for the `@` menu's cloud-drive group.
34
+ *
35
+ * Optional for the same reason as `agentSearch` above. It takes no session:
36
+ * a drive is not scoped to a workspace, so there is nothing a cwd could
37
+ * narrow. `list` already refuses to name a download URL, so what comes back
38
+ * here is a label and a display path.
39
+ */
40
+ driveSearch(input: {
41
+ query: string;
42
+ limit: number;
43
+ }, signal: AbortSignal): Promise<{
44
+ isDirectory?: boolean | undefined;
45
+ updatedAt?: number | undefined;
46
+ origin?: string | undefined;
47
+ id: string;
48
+ label: string;
49
+ provider: string;
50
+ }[]>;
7
51
  search(input: {
8
52
  query: string;
9
53
  provider?: ChatProvider;
@@ -34,14 +78,19 @@ export declare class ReferenceAnythingRemote extends TypertRemoteService {
34
78
  settingsUpdate(settings: SettingsRecord): Promise<{
35
79
  opencliPath: string;
36
80
  profile: string;
81
+ cloudDriveDownloadDirectory: string;
37
82
  detailConcurrency: number;
38
83
  autoSync: boolean;
39
84
  syncOnStartup: boolean;
40
85
  autoSyncMinutes: number;
41
86
  historyMode: "metadata-only" | "offline-mirror";
87
+ syncHistoryDays: number | null;
42
88
  enabledProviders: ("chatgpt" | "claude" | "gemini" | "deepseek" | "grok" | "kimi")[];
89
+ enabledAgents: ("kimi" | "claude-code" | "codex" | "cursor" | "qoder" | "reasonix" | "openclaw" | "grokbuild" | "hermes" | "gemini-cli" | "pi" | "opencode" | "mimocode" | "zcode")[];
43
90
  maxReadTurns: number;
44
91
  inputRenderMode: "pill" | "raw-text";
92
+ agentDirectories?: Partial<Record<"kimi" | "claude-code" | "codex" | "cursor" | "qoder" | "reasonix" | "openclaw" | "grokbuild" | "hermes" | "gemini-cli" | "pi" | "opencode" | "mimocode" | "zcode", string>> | undefined;
93
+ enabledDriveMounts?: string[] | undefined;
45
94
  picker?: {
46
95
  displayMode: "collapse" | "native-scroll";
47
96
  commands: {
@@ -68,14 +117,27 @@ export declare class ReferenceAnythingRemote extends TypertRemoteService {
68
117
  limit: number;
69
118
  maxCandidates: number;
70
119
  };
120
+ agents: {
121
+ enabled: boolean;
122
+ order: number;
123
+ limit: number;
124
+ maxCandidates: number;
125
+ };
71
126
  conversations: {
72
127
  enabled: boolean;
73
128
  order: number;
74
129
  limit: number;
75
130
  maxCandidates: number;
76
131
  };
132
+ drives: {
133
+ enabled: boolean;
134
+ order: number;
135
+ limit: number;
136
+ maxCandidates: number;
137
+ };
77
138
  } | undefined;
78
139
  }>;
140
+ openDownloadDirectory(signal: AbortSignal): Promise<boolean>;
79
141
  browse(input: {
80
142
  query: string;
81
143
  provider?: ChatProvider;
@@ -92,10 +154,41 @@ export declare class ReferenceAnythingRemote extends TypertRemoteService {
92
154
  clearProvider(input: {
93
155
  provider: ChatProvider;
94
156
  }, signal: AbortSignal): Promise<number>;
95
- clearOlder(input: {
96
- days: number;
97
- }, signal: AbortSignal): Promise<number>;
98
157
  clearRemoteMissing(signal: AbortSignal): Promise<number>;
99
158
  clearOldAccounts(signal: AbortSignal): Promise<number>;
100
159
  syncStates(): import("./store/store.ts").ProviderSyncState[];
160
+ openListStatus(signal: AbortSignal): Promise<import("./openlist/index.ts").OpenListStatus>;
161
+ openListInstall(signal: AbortSignal): Promise<import("./openlist/index.ts").OpenListStatus>;
162
+ openListUpgrade(input: {
163
+ rollback?: boolean;
164
+ }, signal: AbortSignal): Promise<import("./openlist/index.ts").OpenListStatus>;
165
+ openListConnectExternal(input: {
166
+ endpoint: string;
167
+ username?: string;
168
+ password?: string;
169
+ token?: string;
170
+ }, signal: AbortSignal): Promise<import("./openlist/index.ts").OpenListStatus>;
171
+ openListDisconnect(signal: AbortSignal): Promise<import("./openlist/index.ts").OpenListStatus>;
172
+ openListDrivers(signal: AbortSignal): Promise<import("./openlist/host.ts").OpenListDriver[]>;
173
+ openListMounts(signal: AbortSignal): Promise<import("./openlist/host.ts").OpenListMount[]>;
174
+ openListCreateMount(input: {
175
+ id?: string;
176
+ mountPath: string;
177
+ driver: string;
178
+ addition: Record<string, unknown>;
179
+ order?: number;
180
+ remark?: string;
181
+ }, signal: AbortSignal): Promise<import("./openlist/host.ts").OpenListMount>;
182
+ openListDisableMount(input: {
183
+ id: string;
184
+ disabled?: boolean;
185
+ }, signal: AbortSignal): Promise<boolean>;
186
+ openListRemoveMount(input: {
187
+ id: string;
188
+ }, signal: AbortSignal): Promise<boolean>;
189
+ openListReindex(input: {
190
+ id: string;
191
+ }, signal: AbortSignal): Promise<{
192
+ supported: true;
193
+ }>;
101
194
  }
package/lib/host.js CHANGED
@@ -1,6 +1,65 @@
1
1
  import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
2
+ import { tmpdir } from 'node:os';
3
+ import { spawn } from 'node:child_process';
4
+ import { validateDownloadDirectory } from "./download-directory.js";
2
5
  export class ReferenceAnythingRemote extends TypertRemoteService {
3
6
  constructor(ctx) { super(ctx, 'referenceAnything'); }
7
+ /**
8
+ * Discovery for the `@` menu's local-agent group.
9
+ *
10
+ * Resolved optionally rather than injected: `reference-anything-web` must
11
+ * still load in a profile where the local-agent plugin is switched off, and an
12
+ * absent service means an empty group, not a broken composer. Workspace
13
+ * scoping takes the session's own cwd, so a Web server serving projects from
14
+ * anywhere still scopes to the project the user is in.
15
+ */
16
+ async agentSearch(agent, input, signal) {
17
+ const agents = this.ctx.get('referenceLocalAgents');
18
+ if (agents === undefined)
19
+ return [];
20
+ // A session with no cwd of its own is rare and has no workspace to scope
21
+ // to; falling back to the process's directory is the same guess the
22
+ // `ReferenceSource.list` seam makes, rather than an empty group.
23
+ const rows = await agents.listForWorkspace(input.query, input.limit, agent.session.header.cwd ?? process.cwd(), signal);
24
+ return rows.map(row => ({
25
+ id: row.ref.id,
26
+ kind: row.ref.id.slice(0, row.ref.id.indexOf(':')),
27
+ label: row.label,
28
+ provider: row.provider ?? '',
29
+ ...row.updatedAt === undefined ? {} : { updatedAt: row.updatedAt },
30
+ ...row.searchIncomplete === true ? { searchIncomplete: true } : {},
31
+ }));
32
+ }
33
+ async agentStats(signal) {
34
+ signal.throwIfAborted();
35
+ const agents = this.ctx.get('referenceLocalAgents');
36
+ if (agents === undefined)
37
+ return [];
38
+ agents.setAgentDirectories(this.ctx.referenceChatHistory.getSettings().agentDirectories ?? {});
39
+ return (await agents.stats(signal)).map(row => ({ agent: row.kind, conversations: row.conversations }));
40
+ }
41
+ /**
42
+ * Discovery for the `@` menu's cloud-drive group.
43
+ *
44
+ * Optional for the same reason as `agentSearch` above. It takes no session:
45
+ * a drive is not scoped to a workspace, so there is nothing a cwd could
46
+ * narrow. `list` already refuses to name a download URL, so what comes back
47
+ * here is a label and a display path.
48
+ */
49
+ async driveSearch(input, signal) {
50
+ const drives = this.ctx.get('referenceCloudDrive');
51
+ if (drives === undefined)
52
+ return [];
53
+ const rows = await drives.pickerList(input.query, input.limit, signal);
54
+ return rows.map(row => ({
55
+ id: row.ref.id,
56
+ label: row.label,
57
+ provider: row.provider ?? '',
58
+ ...row.origin === undefined ? {} : { origin: row.origin },
59
+ ...row.updatedAt === undefined ? {} : { updatedAt: row.updatedAt },
60
+ ...row.isDirectory === true ? { isDirectory: true } : {},
61
+ }));
62
+ }
4
63
  search(input, signal) {
5
64
  signal.throwIfAborted();
6
65
  return this.ctx.referenceChatHistory.search(input.query, input.provider, input.limit, signal);
@@ -22,7 +81,24 @@ export class ReferenceAnythingRemote extends TypertRemoteService {
22
81
  syncStatus(input) { return this.ctx.referenceChatHistory.sync.status(input.jobId); }
23
82
  syncCancel(input) { return this.ctx.referenceChatHistory.sync.cancel(input.jobId); }
24
83
  settingsGet() { return this.ctx.referenceChatHistory.getSettings(); }
25
- settingsUpdate(settings) { return this.ctx.referenceChatHistory.updateSettings(settings); }
84
+ async settingsUpdate(settings) {
85
+ const value = await this.ctx.referenceChatHistory.updateSettings(settings);
86
+ this.ctx.get('referenceLocalAgents')?.setAgentDirectories(value.agentDirectories ?? {});
87
+ return value;
88
+ }
89
+ async openDownloadDirectory(signal) {
90
+ signal.throwIfAborted();
91
+ const configured = this.ctx.referenceChatHistory.getSettings().cloudDriveDownloadDirectory;
92
+ const path = configured || tmpdir();
93
+ await validateDownloadDirectory(path);
94
+ const child = process.platform === 'win32'
95
+ ? spawn('explorer.exe', [path], { detached: true, stdio: 'ignore' })
96
+ : process.platform === 'darwin'
97
+ ? spawn('open', [path], { detached: true, stdio: 'ignore' })
98
+ : spawn('xdg-open', [path], { detached: true, stdio: 'ignore' });
99
+ child.unref();
100
+ return true;
101
+ }
26
102
  browse(input, signal) {
27
103
  signal.throwIfAborted();
28
104
  return this.ctx.referenceChatHistory.browse(input.query, input.provider, input.limit, input.offset);
@@ -36,10 +112,6 @@ export class ReferenceAnythingRemote extends TypertRemoteService {
36
112
  signal.throwIfAborted();
37
113
  return this.ctx.referenceChatHistory.removeProvider(input.provider);
38
114
  }
39
- clearOlder(input, signal) {
40
- signal.throwIfAborted();
41
- return this.ctx.referenceChatHistory.removeOlderThan(input.days);
42
- }
43
115
  clearRemoteMissing(signal) {
44
116
  signal.throwIfAborted();
45
117
  return this.ctx.referenceChatHistory.removeRemoteMissing();
@@ -49,4 +121,15 @@ export class ReferenceAnythingRemote extends TypertRemoteService {
49
121
  return this.ctx.referenceChatHistory.removeOldAccounts();
50
122
  }
51
123
  syncStates() { return this.ctx.referenceChatHistory.syncStates(); }
124
+ openListStatus(signal) { return this.ctx.openListManager.status(signal); }
125
+ openListInstall(signal) { return this.ctx.openListManager.install(signal); }
126
+ openListUpgrade(input, signal) { return this.ctx.openListManager.upgrade(input, signal); }
127
+ openListConnectExternal(input, signal) { return this.ctx.openListManager.connectExternal(input, signal); }
128
+ openListDisconnect(signal) { return this.ctx.openListManager.disconnect(signal); }
129
+ openListDrivers(signal) { return this.ctx.openListManager.drivers(signal); }
130
+ openListMounts(signal) { return this.ctx.openListManager.mounts(signal); }
131
+ openListCreateMount(input, signal) { return this.ctx.openListManager.createMount(input, signal); }
132
+ async openListDisableMount(input, signal) { await this.ctx.openListManager.disableMount(input.id, input.disabled ?? true, signal); return true; }
133
+ async openListRemoveMount(input, signal) { await this.ctx.openListManager.removeMount(input.id, signal); return true; }
134
+ openListReindex(input, signal) { return this.ctx.openListManager.reindexMount(input.id, signal); }
52
135
  }