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/update.d.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  export declare const PACKAGE_NAME = "dsh-reference-anything";
2
2
  export declare const NPM_LATEST_URL = "https://registry.npmjs.org/dsh-reference-anything/latest";
3
+ export declare const GITHUB_RELEASE_URL = "https://api.github.com/repos/Chael-Chael/dsh-reference-anything/releases/tags";
3
4
  export interface PackageUpdateStatus {
4
5
  currentVersion: string;
5
6
  latestVersion: string;
6
7
  updateAvailable: boolean;
7
8
  checkedAt: number;
9
+ releaseNotes?: string;
10
+ releaseUrl?: string;
8
11
  error?: string;
9
12
  }
10
13
  export interface PackageUpdateResult {
@@ -15,6 +18,7 @@ interface UpdateManagerOptions {
15
18
  packageRoot?: string;
16
19
  dshHome?: string;
17
20
  fetchLatest?: (signal?: AbortSignal) => Promise<string>;
21
+ fetchReleaseNotes?: (version: string, signal?: AbortSignal) => Promise<Pick<PackageUpdateStatus, 'releaseNotes' | 'releaseUrl'>>;
18
22
  install?: (profileDir: string, version: string, signal?: AbortSignal) => Promise<void>;
19
23
  afterInstall?: (profileDir: string, version: string, signal?: AbortSignal) => Promise<void>;
20
24
  }
@@ -23,6 +27,7 @@ export declare class PackageUpdateManager {
23
27
  private readonly packageRoot;
24
28
  private readonly dshHome?;
25
29
  private readonly fetchLatest;
30
+ private readonly fetchReleaseNotes;
26
31
  private readonly install;
27
32
  private readonly afterInstall?;
28
33
  private cached?;
@@ -37,6 +42,7 @@ export declare class PackageUpdateManager {
37
42
  private runCheck;
38
43
  }
39
44
  export declare function fetchLatestVersion(signal?: AbortSignal): Promise<string>;
45
+ export declare function fetchGitHubReleaseNotes(version: string, signal?: AbortSignal): Promise<Pick<PackageUpdateStatus, 'releaseNotes' | 'releaseUrl'>>;
40
46
  export declare function readPackageVersion(packageRoot?: string): Promise<string>;
41
47
  /** Find the profile that owns this exact installed package, including pnpm links. */
42
48
  export declare function findOwningProfileDir(packageRoot?: string, configuredDshHome?: string): Promise<string>;
package/lib/update.js CHANGED
@@ -7,6 +7,7 @@ import { promisify } from 'node:util';
7
7
  const execFile = promisify(nodeExecFile);
8
8
  export const PACKAGE_NAME = 'dsh-reference-anything';
9
9
  export const NPM_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
10
+ export const GITHUB_RELEASE_URL = 'https://api.github.com/repos/Chael-Chael/dsh-reference-anything/releases/tags';
10
11
  const PACKAGE_ROOT = resolve(fileURLToPath(new URL('../', import.meta.url)));
11
12
  const UPDATE_TIMEOUT_MS = 5 * 60_000;
12
13
  const CHECK_TIMEOUT_MS = 10_000;
@@ -15,6 +16,7 @@ export class PackageUpdateManager {
15
16
  packageRoot;
16
17
  dshHome;
17
18
  fetchLatest;
19
+ fetchReleaseNotes;
18
20
  install;
19
21
  afterInstall;
20
22
  cached;
@@ -23,6 +25,7 @@ export class PackageUpdateManager {
23
25
  this.packageRoot = resolve(options.packageRoot ?? PACKAGE_ROOT);
24
26
  this.dshHome = options.dshHome;
25
27
  this.fetchLatest = options.fetchLatest ?? fetchLatestVersion;
28
+ this.fetchReleaseNotes = options.fetchReleaseNotes ?? fetchGitHubReleaseNotes;
26
29
  this.install = options.install ?? installPackageVersion;
27
30
  this.afterInstall = options.afterInstall;
28
31
  }
@@ -60,6 +63,8 @@ export class PackageUpdateManager {
60
63
  latestVersion: status.latestVersion,
61
64
  updateAvailable: false,
62
65
  checkedAt: Date.now(),
66
+ releaseNotes: status.releaseNotes,
67
+ releaseUrl: status.releaseUrl,
63
68
  };
64
69
  return { version: status.latestVersion, restartRequired: true };
65
70
  }
@@ -69,11 +74,18 @@ export class PackageUpdateManager {
69
74
  try {
70
75
  currentVersion = await readPackageVersion(this.packageRoot);
71
76
  const latestVersion = await this.fetchLatest(signal);
77
+ const updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
78
+ let release = {};
79
+ try {
80
+ release = await this.fetchReleaseNotes(latestVersion, signal);
81
+ }
82
+ catch { /* Release notes are advisory. */ }
72
83
  const status = {
73
84
  currentVersion,
74
85
  latestVersion,
75
- updateAvailable: compareVersions(latestVersion, currentVersion) > 0,
86
+ updateAvailable,
76
87
  checkedAt,
88
+ ...release,
77
89
  };
78
90
  this.cached = status;
79
91
  return status;
@@ -107,6 +119,22 @@ export async function fetchLatestVersion(signal) {
107
119
  throw new Error('npm registry returned an invalid version');
108
120
  return body.version;
109
121
  }
122
+ export async function fetchGitHubReleaseNotes(version, signal) {
123
+ if (!isVersion(version))
124
+ throw new Error('refusing to request an invalid release version');
125
+ const timeout = AbortSignal.timeout(CHECK_TIMEOUT_MS);
126
+ const response = await fetch(`${GITHUB_RELEASE_URL}/v${version}`, {
127
+ headers: { accept: 'application/vnd.github+json', 'user-agent': PACKAGE_NAME, 'x-github-api-version': '2022-11-28' },
128
+ signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
129
+ });
130
+ if (!response.ok)
131
+ throw new Error(`GitHub returned HTTP ${String(response.status)}`);
132
+ const body = await response.json();
133
+ return {
134
+ releaseNotes: typeof body.body === 'string' ? body.body.trim().slice(0, 12_000) || undefined : undefined,
135
+ releaseUrl: typeof body.html_url === 'string' ? body.html_url : undefined,
136
+ };
137
+ }
110
138
  export async function readPackageVersion(packageRoot = PACKAGE_ROOT) {
111
139
  const manifest = await readJson(join(packageRoot, 'package.json'));
112
140
  if (typeof manifest.version !== 'string' || !isVersion(manifest.version))
package/lib/web.js CHANGED
@@ -1,7 +1,11 @@
1
1
  import { ReferenceAnythingRemote } from "./host.js";
2
2
  import { TYPERT_MANIFEST } from "./typert.js";
3
3
  export const name = 'reference-anything-web';
4
- export const inject = ['typert', 'referenceChatHistory'];
4
+ // `referenceCloudDrive` is consumed lazily by ReferenceAnythingRemote when the
5
+ // composer asks for @drive candidates. Declare it here so Cordis exposes the
6
+ // sibling service in this plugin scope instead of making driveSearch silently
7
+ // look like an unconfigured installation.
8
+ export const inject = ['typert', 'referenceChatHistory', 'openListManager', 'referenceCloudDrive'];
5
9
  export function apply(ctx) {
6
10
  new ReferenceAnythingRemote(ctx);
7
11
  ctx.effect(() => {
package/lib/wire.d.ts CHANGED
@@ -10,12 +10,33 @@ export declare const providerSchema: z.ZodEnum<{
10
10
  }>;
11
11
  export type ChatProvider = z.infer<typeof providerSchema>;
12
12
  export declare const ALL_PROVIDERS: readonly ChatProvider[];
13
+ export declare const localAgentSchema: z.ZodEnum<{
14
+ kimi: "kimi";
15
+ "claude-code": "claude-code";
16
+ codex: "codex";
17
+ cursor: "cursor";
18
+ qoder: "qoder";
19
+ reasonix: "reasonix";
20
+ openclaw: "openclaw";
21
+ grokbuild: "grokbuild";
22
+ hermes: "hermes";
23
+ "gemini-cli": "gemini-cli";
24
+ pi: "pi";
25
+ opencode: "opencode";
26
+ mimocode: "mimocode";
27
+ zcode: "zcode";
28
+ }>;
29
+ export type LocalAgent = z.infer<typeof localAgentSchema>;
30
+ export declare const ALL_LOCAL_AGENTS: readonly LocalAgent[];
31
+ export declare const LOCAL_AGENT_LABEL: Readonly<Record<LocalAgent, string>>;
13
32
  export declare const pickerSourceSchema: z.ZodEnum<{
14
33
  commands: "commands";
15
34
  skills: "skills";
16
35
  files: "files";
17
36
  sessions: "sessions";
37
+ agents: "agents";
18
38
  conversations: "conversations";
39
+ drives: "drives";
19
40
  }>;
20
41
  export type PickerSource = z.infer<typeof pickerSourceSchema>;
21
42
  declare const pickerSourceSettingsSchema: z.ZodObject<{
@@ -54,12 +75,24 @@ export declare const pickerSettingsSchema: z.ZodObject<{
54
75
  limit: z.ZodNumber;
55
76
  maxCandidates: z.ZodDefault<z.ZodNumber>;
56
77
  }, z.core.$strip>;
78
+ agents: z.ZodDefault<z.ZodObject<{
79
+ enabled: z.ZodBoolean;
80
+ order: z.ZodNumber;
81
+ limit: z.ZodNumber;
82
+ maxCandidates: z.ZodDefault<z.ZodNumber>;
83
+ }, z.core.$strip>>;
57
84
  conversations: z.ZodObject<{
58
85
  enabled: z.ZodBoolean;
59
86
  order: z.ZodNumber;
60
87
  limit: z.ZodNumber;
61
88
  maxCandidates: z.ZodDefault<z.ZodNumber>;
62
89
  }, z.core.$strip>;
90
+ drives: z.ZodDefault<z.ZodObject<{
91
+ enabled: z.ZodBoolean;
92
+ order: z.ZodNumber;
93
+ limit: z.ZodNumber;
94
+ maxCandidates: z.ZodDefault<z.ZodNumber>;
95
+ }, z.core.$strip>>;
63
96
  }, z.core.$strip>;
64
97
  export type PickerSettings = z.infer<typeof pickerSettingsSchema>;
65
98
  export type PickerDisplayMode = PickerSettings['displayMode'];
@@ -75,6 +108,7 @@ export declare function samePickerSettings(left: PickerSettings, right: PickerSe
75
108
  export declare const settingsRecordSchema: z.ZodObject<{
76
109
  opencliPath: z.ZodString;
77
110
  profile: z.ZodString;
111
+ cloudDriveDownloadDirectory: z.ZodDefault<z.ZodString>;
78
112
  detailConcurrency: z.ZodNumber;
79
113
  autoSync: z.ZodDefault<z.ZodBoolean>;
80
114
  syncOnStartup: z.ZodDefault<z.ZodBoolean>;
@@ -83,6 +117,7 @@ export declare const settingsRecordSchema: z.ZodObject<{
83
117
  "metadata-only": "metadata-only";
84
118
  "offline-mirror": "offline-mirror";
85
119
  }>>;
120
+ syncHistoryDays: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
86
121
  enabledProviders: z.ZodDefault<z.ZodArray<z.ZodEnum<{
87
122
  chatgpt: "chatgpt";
88
123
  claude: "claude";
@@ -91,6 +126,39 @@ export declare const settingsRecordSchema: z.ZodObject<{
91
126
  grok: "grok";
92
127
  kimi: "kimi";
93
128
  }>>>;
129
+ enabledAgents: z.ZodDefault<z.ZodArray<z.ZodEnum<{
130
+ kimi: "kimi";
131
+ "claude-code": "claude-code";
132
+ codex: "codex";
133
+ cursor: "cursor";
134
+ qoder: "qoder";
135
+ reasonix: "reasonix";
136
+ openclaw: "openclaw";
137
+ grokbuild: "grokbuild";
138
+ hermes: "hermes";
139
+ "gemini-cli": "gemini-cli";
140
+ pi: "pi";
141
+ opencode: "opencode";
142
+ mimocode: "mimocode";
143
+ zcode: "zcode";
144
+ }>>>;
145
+ agentDirectories: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
146
+ kimi: "kimi";
147
+ "claude-code": "claude-code";
148
+ codex: "codex";
149
+ cursor: "cursor";
150
+ qoder: "qoder";
151
+ reasonix: "reasonix";
152
+ openclaw: "openclaw";
153
+ grokbuild: "grokbuild";
154
+ hermes: "hermes";
155
+ "gemini-cli": "gemini-cli";
156
+ pi: "pi";
157
+ opencode: "opencode";
158
+ mimocode: "mimocode";
159
+ zcode: "zcode";
160
+ }> & z.core.$partial, z.ZodString>>;
161
+ enabledDriveMounts: z.ZodOptional<z.ZodArray<z.ZodString>>;
94
162
  maxReadTurns: z.ZodDefault<z.ZodNumber>;
95
163
  inputRenderMode: z.ZodDefault<z.ZodEnum<{
96
164
  pill: "pill";
@@ -125,12 +193,24 @@ export declare const settingsRecordSchema: z.ZodObject<{
125
193
  limit: z.ZodNumber;
126
194
  maxCandidates: z.ZodDefault<z.ZodNumber>;
127
195
  }, z.core.$strip>;
196
+ agents: z.ZodDefault<z.ZodObject<{
197
+ enabled: z.ZodBoolean;
198
+ order: z.ZodNumber;
199
+ limit: z.ZodNumber;
200
+ maxCandidates: z.ZodDefault<z.ZodNumber>;
201
+ }, z.core.$strip>>;
128
202
  conversations: z.ZodObject<{
129
203
  enabled: z.ZodBoolean;
130
204
  order: z.ZodNumber;
131
205
  limit: z.ZodNumber;
132
206
  maxCandidates: z.ZodDefault<z.ZodNumber>;
133
207
  }, z.core.$strip>;
208
+ drives: z.ZodDefault<z.ZodObject<{
209
+ enabled: z.ZodBoolean;
210
+ order: z.ZodNumber;
211
+ limit: z.ZodNumber;
212
+ maxCandidates: z.ZodDefault<z.ZodNumber>;
213
+ }, z.core.$strip>>;
134
214
  }, z.core.$strip>>;
135
215
  }, z.core.$strip>;
136
216
  export type SettingsRecord = z.infer<typeof settingsRecordSchema>;
package/lib/wire.js CHANGED
@@ -2,7 +2,14 @@
2
2
  import { z } from 'zod';
3
3
  export const providerSchema = z.enum(['chatgpt', 'claude', 'gemini', 'deepseek', 'grok', 'kimi']);
4
4
  export const ALL_PROVIDERS = providerSchema.options;
5
- export const pickerSourceSchema = z.enum(['commands', 'skills', 'files', 'sessions', 'conversations']);
5
+ export const localAgentSchema = z.enum(['claude-code', 'codex', 'cursor', 'qoder', 'reasonix', 'openclaw', 'kimi', 'grokbuild', 'hermes', 'gemini-cli', 'pi', 'opencode', 'mimocode', 'zcode']);
6
+ export const ALL_LOCAL_AGENTS = localAgentSchema.options;
7
+ export const LOCAL_AGENT_LABEL = {
8
+ 'claude-code': 'Claude Code', codex: 'Codex', cursor: 'Cursor', qoder: 'Qoder', reasonix: 'Reasonix',
9
+ openclaw: 'OpenClaw', kimi: 'Kimi', grokbuild: 'Grok Build', hermes: 'Hermes', 'gemini-cli': 'Gemini CLI',
10
+ pi: 'Pi', opencode: 'opencode', mimocode: 'mimocode', zcode: 'zcode',
11
+ };
12
+ export const pickerSourceSchema = z.enum(['commands', 'skills', 'files', 'sessions', 'agents', 'conversations', 'drives']);
6
13
  const pickerSourceSettingsSchema = z.object({
7
14
  enabled: z.boolean(),
8
15
  order: z.number().int().min(0).max(100),
@@ -15,7 +22,15 @@ export const pickerSettingsSchema = z.object({
15
22
  skills: pickerSourceSettingsSchema,
16
23
  files: pickerSourceSettingsSchema,
17
24
  sessions: pickerSourceSettingsSchema,
25
+ // Defaulted, unlike its neighbours, because this key arrived after settings
26
+ // were already on disk. `settingsRecordSchema` is the durable read boundary:
27
+ // a saved record that has `picker` but predates this key would fail the whole
28
+ // parse without it, and the medium — every setting in it — would be rejected.
29
+ // Any key added here from now on needs the same treatment.
30
+ agents: pickerSourceSettingsSchema.default({ enabled: true, order: 25, limit: 6, maxCandidates: 50 }),
18
31
  conversations: pickerSourceSettingsSchema,
32
+ // Defaulted for the same reason as `agents` above: it arrived later still.
33
+ drives: pickerSourceSettingsSchema.default({ enabled: true, order: 35, limit: 6, maxCandidates: 50 }),
19
34
  });
20
35
  export const inputRenderModeSchema = z.enum(['pill', 'raw-text']);
21
36
  /** Defaults used before the user saves the General section. */
@@ -26,7 +41,9 @@ export function defaultPickerSettings() {
26
41
  skills: { enabled: true, order: 5, limit: 6, maxCandidates: 50 },
27
42
  files: { enabled: true, order: 10, limit: 6, maxCandidates: 50 },
28
43
  sessions: { enabled: true, order: 20, limit: 6, maxCandidates: 50 },
44
+ agents: { enabled: true, order: 25, limit: 6, maxCandidates: 50 },
29
45
  conversations: { enabled: true, order: 30, limit: 6, maxCandidates: 50 },
46
+ drives: { enabled: true, order: 35, limit: 6, maxCandidates: 50 },
30
47
  };
31
48
  }
32
49
  /** Picker settings have a fixed schema, so explicit comparison is cheap and stable. */
@@ -41,12 +58,24 @@ export function samePickerSettings(left, right) {
41
58
  export const settingsRecordSchema = z.object({
42
59
  opencliPath: z.string(),
43
60
  profile: z.string(),
61
+ /** Empty keeps the original system-temporary-directory behavior. */
62
+ cloudDriveDownloadDirectory: z.string().max(4096).default(''),
44
63
  detailConcurrency: z.number().int().min(1).max(8),
45
64
  autoSync: z.boolean().default(false),
46
65
  syncOnStartup: z.boolean().default(false),
47
66
  autoSyncMinutes: z.number().int().min(15).max(1440).default(60),
48
67
  historyMode: z.enum(['metadata-only', 'offline-mirror']).default('metadata-only'),
68
+ /** Null keeps all web-chat history; old settings default to that upgrade-safe behavior. */
69
+ syncHistoryDays: z.number().int().min(1).max(36500).nullable().default(null),
49
70
  enabledProviders: z.array(providerSchema).default([...ALL_PROVIDERS]),
71
+ enabledAgents: z.array(localAgentSchema).default([...ALL_LOCAL_AGENTS]),
72
+ /** One optional additional transcript root per local Agent. */
73
+ agentDirectories: z.partialRecord(localAgentSchema, z.string().max(4096)).optional(),
74
+ /**
75
+ * OpenList mount paths shown by the drive picker. Undefined preserves the
76
+ * legacy behaviour (all enabled mounts); an explicit empty array means none.
77
+ */
78
+ enabledDriveMounts: z.array(z.string().startsWith('/')).optional(),
50
79
  maxReadTurns: z.number().int().min(1).max(100).default(10),
51
80
  inputRenderMode: inputRenderModeSchema.default('pill'),
52
81
  // Optional keeps existing on-disk settings forward-compatible. The client
@@ -1,23 +1,23 @@
1
- import { domFallbackScript, registerProvider, sinceGuardSource } from './common.js'
2
- // OpenCLI discovery marker: registerProvider below performs the cli() calls.
3
-
4
- export const timestampSource = String.raw`value => {
5
- if (value === undefined || value === null || value === '') return ''
6
- if (typeof value === 'string' && Number.isNaN(Number(value))) {
7
- const parsed = Date.parse(value)
8
- return Number.isNaN(parsed) ? '' : new Date(parsed).toISOString()
9
- }
10
- const numeric = Number(value)
11
- if (!Number.isFinite(numeric) || numeric <= 0) return ''
12
- const milliseconds = numeric < 1e11 ? numeric * 1000
13
- : numeric < 1e14 ? numeric : numeric < 1e17 ? numeric / 1000 : numeric / 1e6
14
- const date = new Date(milliseconds)
15
- return Number.isNaN(date.getTime()) ? '' : date.toISOString()
16
- }`
17
-
18
- export const historyScript = String.raw`async function (args) {
19
- const stopEarly = (${sinceGuardSource})(args && args.since)
20
- const toIso = (${timestampSource})
1
+ import { domFallbackScript, registerProvider, sinceGuardSource } from './common.js'
2
+ // OpenCLI discovery marker: registerProvider below performs the cli() calls.
3
+
4
+ export const timestampSource = String.raw`value => {
5
+ if (value === undefined || value === null || value === '') return ''
6
+ if (typeof value === 'string' && Number.isNaN(Number(value))) {
7
+ const parsed = Date.parse(value)
8
+ return Number.isNaN(parsed) ? '' : new Date(parsed).toISOString()
9
+ }
10
+ const numeric = Number(value)
11
+ if (!Number.isFinite(numeric) || numeric <= 0) return ''
12
+ const milliseconds = numeric < 1e11 ? numeric * 1000
13
+ : numeric < 1e14 ? numeric : numeric < 1e17 ? numeric / 1000 : numeric / 1e6
14
+ const date = new Date(milliseconds)
15
+ return Number.isNaN(date.getTime()) ? '' : date.toISOString()
16
+ }`
17
+
18
+ export const historyScript = String.raw`async function (args) {
19
+ const stopEarly = (${sinceGuardSource})(args && args.since)
20
+ const toIso = (${timestampSource})
21
21
  const fallback = () => Array.from(document.querySelectorAll('a[href*="/c/"]')).map((a, i) => {
22
22
  const match = a.href.match(/\/c\/([^/?#]+)/)
23
23
  return match ? { provider: 'chatgpt', accountScope: '', id: match[1], title: (a.innerText || a.textContent || 'Untitled').trim(), url: a.href, createdAt: '', updatedAt: '', messageCount: '', cursor: '', partial: true } : null
@@ -40,8 +40,8 @@ export const historyScript = String.raw`async function (args) {
40
40
  for (const item of items) rows.push({
41
41
  provider: 'chatgpt', accountScope: '', id: String(item.id), title: item.title || 'Untitled Conversation',
42
42
  url: location.origin + '/c/' + item.id,
43
- createdAt: toIso(item.create_time),
44
- updatedAt: toIso(item.update_time),
43
+ createdAt: toIso(item.create_time),
44
+ updatedAt: toIso(item.update_time),
45
45
  messageCount: item.message_count || item.messageCount || '', cursor: '', partial: false,
46
46
  })
47
47
  if (items.length < 100) return JSON.stringify({ ok: true, rows })
@@ -110,16 +110,19 @@ const detailScript = String.raw`async function (args) {
110
110
  }
111
111
  }`
112
112
 
113
- registerProvider({
114
- site: 'dsh-chatgpt', provider: 'ChatGPT', domain: 'chatgpt.com', home: 'https://chatgpt.com/',
115
- conversationUrl: id => `https://chatgpt.com/c/${encodeURIComponent(id)}`,
116
- whoamiScript: String.raw`async function () {
113
+ export const whoamiScript = String.raw`async function () {
117
114
  const response = await fetch('/api/auth/session', { credentials: 'include' })
118
115
  if (response.status === 401 || response.status === 403) return JSON.stringify({ ok: false, code: 'AUTH' })
116
+ if (response.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
119
117
  if (!response.ok) return JSON.stringify({ ok: false, message: 'session HTTP ' + response.status })
120
118
  const payload = await response.json(); const user = payload.user || payload.account || {}
121
119
  const identity = String(user.id || user.account_id || user.email || '')
122
120
  return JSON.stringify({ ok: Boolean(identity), identity, code: identity ? undefined : 'AUTH' })
123
- }`,
121
+ }`
122
+
123
+ registerProvider({
124
+ site: 'dsh-chatgpt', provider: 'ChatGPT', domain: 'chatgpt.com', home: 'https://chatgpt.com/',
125
+ conversationUrl: id => `https://chatgpt.com/c/${encodeURIComponent(id)}`,
126
+ whoamiScript,
124
127
  historyScript, detailScript, fallbackScript: domFallbackScript,
125
128
  })
@@ -119,16 +119,20 @@ const detailScript = String.raw`async function (args) {
119
119
  }
120
120
  }`
121
121
 
122
+ export const whoamiScript = String.raw`async function () {
123
+ const response = await fetch('/api/account', { credentials: 'include', headers: { Accept: 'application/json' } })
124
+ if (response.status === 401 || response.status === 403) return JSON.stringify({ ok: false, code: 'AUTH' })
125
+ if (response.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
126
+ if (!response.ok) return JSON.stringify({ ok: false, message: 'account HTTP ' + response.status })
127
+ const payload = await response.json()
128
+ const identity = typeof payload.email_address === 'string' ? payload.email_address.trim() : ''
129
+ if (!identity) return JSON.stringify({ ok: false, message: 'Claude stable account identity unavailable' })
130
+ return JSON.stringify({ ok: true, identity })
131
+ }`
132
+
122
133
  registerProvider({
123
134
  site: 'dsh-claude', provider: 'Claude', domain: 'claude.ai', home: 'https://claude.ai/',
124
135
  conversationUrl: id => `https://claude.ai/chat/${encodeURIComponent(id)}`,
125
- whoamiScript: String.raw`async function () {
126
- const response = await fetch('/api/organizations', { credentials: 'include' })
127
- if (response.status === 401 || response.status === 403) return JSON.stringify({ ok: false, code: 'AUTH' })
128
- if (!response.ok) return JSON.stringify({ ok: false, message: 'organizations HTTP ' + response.status })
129
- const payload = await response.json(); const values = Array.isArray(payload) ? payload : payload.organizations || []
130
- const item = values.find(value => value && (value.uuid || value.id)); const identity = String(item && (item.uuid || item.id) || '')
131
- return JSON.stringify({ ok: Boolean(identity), identity, code: identity ? undefined : 'AUTH' })
132
- }`,
136
+ whoamiScript,
133
137
  historyScript, detailScript, fallbackScript: domFallbackScript,
134
138
  })