@karmaniverous/jeeves-watcher 0.18.12 → 0.19.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/config.schema.json +5 -13
- package/dist/cli/jeeves-watcher/{index-C2iJGo8u.js → index-BVfdWEhb.js} +179 -139
- package/dist/cli/jeeves-watcher/index.js +44 -18
- package/dist/index.d.ts +1 -1
- package/dist/index.js +278 -212
- package/package.json +32 -31
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ Add a `vcs` block to your config:
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
| Property | Default | Description |
|
|
52
|
-
|
|
52
|
+
| --- | --- | --- |
|
|
53
53
|
| `vcs.enabled` | `false` | Enable git-backed version control globally |
|
|
54
54
|
| `vcs.commitThrottleMs` | `30000` | Throttle interval (ms) for batching file changes into commits. Min: 1000 |
|
|
55
55
|
| `vcs.maxBatchSize` | `1000` | Maximum files per commit batch. Flushes immediately when exceeded. Min: 1 |
|
|
@@ -137,22 +137,22 @@ To prevent unbounded history growth, the squash retention system periodically co
|
|
|
137
137
|
|
|
138
138
|
### API Endpoints
|
|
139
139
|
|
|
140
|
-
| Endpoint
|
|
141
|
-
|
|
142
|
-
| `/vcs/status`
|
|
143
|
-
| `/vcs/history`
|
|
144
|
-
| `/vcs/show`
|
|
145
|
-
| `/vcs/diff`
|
|
146
|
-
| `/vcs/revert`
|
|
147
|
-
| `/vcs/exclude`
|
|
148
|
-
| `/vcs/check-exclusion` | GET
|
|
140
|
+
| Endpoint | Method | Description |
|
|
141
|
+
| ---------------------- | ------ | --------------------------------- |
|
|
142
|
+
| `/vcs/status` | GET | VCS state for all roots |
|
|
143
|
+
| `/vcs/history` | GET | Commit history for a path/glob |
|
|
144
|
+
| `/vcs/show` | GET | File content at a specific commit |
|
|
145
|
+
| `/vcs/diff` | GET | Diff between commits |
|
|
146
|
+
| `/vcs/revert` | POST | Restore files from a past commit |
|
|
147
|
+
| `/vcs/exclude` | POST | Manage gitignore exclusions |
|
|
148
|
+
| `/vcs/check-exclusion` | GET | Check gitignore status of a path |
|
|
149
149
|
|
|
150
150
|
## JsonMap Built-in Helpers
|
|
151
151
|
|
|
152
152
|
The following helpers are available in every JsonMap `lib` context:
|
|
153
153
|
|
|
154
154
|
| Helper | Description |
|
|
155
|
-
|
|
155
|
+
| --- | --- |
|
|
156
156
|
| `split(str, sep)` | Split a string into an array |
|
|
157
157
|
| `slice(arr, start, end?)` | Slice an array |
|
|
158
158
|
| `join(arr, sep)` | Join an array into a string |
|
package/config.schema.json
CHANGED
|
@@ -1088,19 +1088,11 @@
|
|
|
1088
1088
|
"__schema94": {
|
|
1089
1089
|
"anyOf": [
|
|
1090
1090
|
{
|
|
1091
|
-
"
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
"type": "number"
|
|
1097
|
-
},
|
|
1098
|
-
{
|
|
1099
|
-
"type": "boolean"
|
|
1100
|
-
},
|
|
1101
|
-
{
|
|
1102
|
-
"type": "null"
|
|
1103
|
-
}
|
|
1091
|
+
"type": [
|
|
1092
|
+
"string",
|
|
1093
|
+
"number",
|
|
1094
|
+
"boolean",
|
|
1095
|
+
"null"
|
|
1104
1096
|
]
|
|
1105
1097
|
},
|
|
1106
1098
|
{
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createConfigQueryHandler as createConfigQueryHandler$1, createStatusHandler,
|
|
3
|
-
import {
|
|
2
|
+
import { createConfigApplyHandler, createConfigQueryHandler as createConfigQueryHandler$1, createStatusHandler, DEFAULT_PORTS, getBindAddress } from '@karmaniverous/jeeves';
|
|
3
|
+
import { n as normalizeSlashes, a as normalizeError, e as execFileAsync, G as GIT_TIMEOUT_STANDARD, b as GIT_TIMEOUT_CHERRY_PICK, c as buildAuthenticatedPushUrl, d as GIT_TIMEOUT_PUSH, g as getExecErrorFields, r as retry, f as gitAddViaStdin, h as normalizePathCase, i as findRootForPath, j as GitignoreFilter, k as executeReindex, V as VALID_REINDEX_SCOPES, m as mergeInferenceRules, l as isPathWatched, o as getWatchRootBases, C as CONFIG_WATCH_VALID_SCOPES, s as sleep, p as loadConfig } from './index.js';
|
|
4
4
|
export { q as startFromConfig } from './index.js';
|
|
5
5
|
import { mkdirSync, existsSync, readFileSync, writeFileSync, rmSync, renameSync, readdirSync, statSync } from 'node:fs';
|
|
6
|
-
import { join, dirname, resolve, extname, basename,
|
|
7
|
-
import
|
|
6
|
+
import { join, dirname, resolve, extname, basename, sep, isAbsolute, relative } from 'node:path';
|
|
7
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import Ajv from 'ajv';
|
|
10
10
|
import addFormats from 'ajv-formats';
|
|
@@ -25,7 +25,6 @@ import { capitalize, title, camel, snake, dash, isEqual, get, omit } from 'radas
|
|
|
25
25
|
import rehypeParse from 'rehype-parse';
|
|
26
26
|
import { unified } from 'unified';
|
|
27
27
|
import { dump, load } from 'js-yaml';
|
|
28
|
-
import 'ignore';
|
|
29
28
|
import chokidar from 'chokidar';
|
|
30
29
|
import Fastify from 'fastify';
|
|
31
30
|
import crypto from 'crypto';
|
|
@@ -38,6 +37,7 @@ import mammoth from 'mammoth';
|
|
|
38
37
|
import { MarkdownTextSplitter, RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
|
|
39
38
|
import { QdrantClient } from '@qdrant/js-client-rest';
|
|
40
39
|
import 'cosmiconfig';
|
|
40
|
+
import 'ignore';
|
|
41
41
|
import 'node:child_process';
|
|
42
42
|
import 'node:util';
|
|
43
43
|
|
|
@@ -228,10 +228,12 @@ class EnrichmentStore {
|
|
|
228
228
|
this.logger = logger;
|
|
229
229
|
mkdirSync(stateDir, { recursive: true });
|
|
230
230
|
const dbPath = join(stateDir, 'enrichments.sqlite');
|
|
231
|
-
this.db = new
|
|
232
|
-
this.db.
|
|
233
|
-
this.db.
|
|
234
|
-
const
|
|
231
|
+
this.db = new DatabaseSync(dbPath);
|
|
232
|
+
this.db.exec('PRAGMA journal_mode = WAL;');
|
|
233
|
+
this.db.exec('PRAGMA busy_timeout = ' + BUSY_TIMEOUT_MS.toString() + ';');
|
|
234
|
+
const checkpointStatus = this.db
|
|
235
|
+
.prepare('PRAGMA wal_checkpoint(TRUNCATE)')
|
|
236
|
+
.get();
|
|
235
237
|
if (checkpointStatus && checkpointStatus.busy > 0) {
|
|
236
238
|
// EnrichmentStore is expected to be single-writer. If we see a busy WAL
|
|
237
239
|
// checkpoint at startup, it's most likely from an unclean shutdown where
|
|
@@ -3195,61 +3197,6 @@ async function applyRules(compiledRules, attributes, options = {}) {
|
|
|
3195
3197
|
return { metadata: merged, renderedContent, matchedRules, renderAs };
|
|
3196
3198
|
}
|
|
3197
3199
|
|
|
3198
|
-
/**
|
|
3199
|
-
* @module vcs/resolveWatchRoot
|
|
3200
|
-
* Resolves which VCS-enabled watch root(s) own a given absolute path or glob.
|
|
3201
|
-
*/
|
|
3202
|
-
/**
|
|
3203
|
-
* Resolve which VCS-enabled watch root owns an absolute path.
|
|
3204
|
-
*
|
|
3205
|
-
* @param coordinator - The VcsCoordinator with root information.
|
|
3206
|
-
* @param absolutePath - An absolute filesystem path.
|
|
3207
|
-
* @returns The matching root and relative path, or undefined if no root matches.
|
|
3208
|
-
*/
|
|
3209
|
-
function resolveWatchRoot(coordinator, absolutePath) {
|
|
3210
|
-
const normalized = normalizeSlashes(resolve(absolutePath));
|
|
3211
|
-
const root = findRootForPath(coordinator.getRoots(), normalized);
|
|
3212
|
-
if (!root)
|
|
3213
|
-
return undefined;
|
|
3214
|
-
return {
|
|
3215
|
-
root,
|
|
3216
|
-
relativePath: normalizeSlashes(relative(root, normalized)),
|
|
3217
|
-
};
|
|
3218
|
-
}
|
|
3219
|
-
/**
|
|
3220
|
-
* Resolve which VCS-enabled watch roots match a glob pattern.
|
|
3221
|
-
* For globs that could span multiple roots, returns all matching roots.
|
|
3222
|
-
*
|
|
3223
|
-
* @param coordinator - The VcsCoordinator with root information.
|
|
3224
|
-
* @param glob - An absolute glob pattern.
|
|
3225
|
-
* @returns Array of matching roots with relative glob patterns.
|
|
3226
|
-
*/
|
|
3227
|
-
function resolveWatchRootsForGlob(coordinator, glob) {
|
|
3228
|
-
const normalizedGlob = normalizeSlashes(glob);
|
|
3229
|
-
const roots = coordinator.getRoots();
|
|
3230
|
-
const results = [];
|
|
3231
|
-
const compareGlob = normalizePathCase(normalizedGlob);
|
|
3232
|
-
for (const root of roots) {
|
|
3233
|
-
const compareRoot = normalizePathCase(root);
|
|
3234
|
-
if (compareGlob.startsWith(compareRoot + '/') ||
|
|
3235
|
-
compareGlob === compareRoot) {
|
|
3236
|
-
// Glob is under this root
|
|
3237
|
-
results.push({
|
|
3238
|
-
root,
|
|
3239
|
-
relativePath: normalizeSlashes(relative(root, normalizedGlob)),
|
|
3240
|
-
});
|
|
3241
|
-
}
|
|
3242
|
-
else if (compareRoot.startsWith(compareGlob.replace(/[*?[\]{}]/g, ''))) {
|
|
3243
|
-
// Root is under the glob's base path — include with root-relative glob
|
|
3244
|
-
results.push({
|
|
3245
|
-
root,
|
|
3246
|
-
relativePath: '.',
|
|
3247
|
-
});
|
|
3248
|
-
}
|
|
3249
|
-
}
|
|
3250
|
-
return results;
|
|
3251
|
-
}
|
|
3252
|
-
|
|
3253
3200
|
/**
|
|
3254
3201
|
* @module vcs/validateStateDirOverlap
|
|
3255
3202
|
* Validates that stateDir does not overlap with any watch path.
|
|
@@ -3284,68 +3231,6 @@ function validateStateDirOverlap(stateDir, watchPaths) {
|
|
|
3284
3231
|
}
|
|
3285
3232
|
}
|
|
3286
3233
|
|
|
3287
|
-
/**
|
|
3288
|
-
* @module app/watcherFactory
|
|
3289
|
-
* Factory helpers for creating and rebuilding the filesystem watcher.
|
|
3290
|
-
* Extracted from initialization.ts to follow SRP.
|
|
3291
|
-
*/
|
|
3292
|
-
/**
|
|
3293
|
-
* Create file system watcher with gitignore filtering.
|
|
3294
|
-
*/
|
|
3295
|
-
function createWatcher(config, factories, queue, processor, logger, runtimeOptions, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete) {
|
|
3296
|
-
const respectGitignore = config.watch.respectGitignore ?? true;
|
|
3297
|
-
const gitignoreFilter = respectGitignore
|
|
3298
|
-
? new GitignoreFilter(extractWatchPathStrings(config.watch.paths))
|
|
3299
|
-
: undefined;
|
|
3300
|
-
const watcher = factories.createFileSystemWatcher(config.watch, queue, processor, logger, {
|
|
3301
|
-
maxRetries: config.maxRetries ?? runtimeOptions.maxRetries,
|
|
3302
|
-
maxBackoffMs: config.maxBackoffMs ?? runtimeOptions.maxBackoffMs,
|
|
3303
|
-
onFatalError: runtimeOptions.onFatalError,
|
|
3304
|
-
gitignoreFilter,
|
|
3305
|
-
initialScanTracker,
|
|
3306
|
-
contentHashCache,
|
|
3307
|
-
onVcsFileChange,
|
|
3308
|
-
onInitialScanComplete,
|
|
3309
|
-
});
|
|
3310
|
-
return { watcher, gitignoreFilter };
|
|
3311
|
-
}
|
|
3312
|
-
/**
|
|
3313
|
-
* Tear down and rebuild the filesystem watcher with new config.
|
|
3314
|
-
* Falls back to the old watcher if the new one fails to start.
|
|
3315
|
-
*/
|
|
3316
|
-
async function rebuildWatcher(newConfig, factories, queue, processor, logger, runtimeOptions, oldState, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete) {
|
|
3317
|
-
try {
|
|
3318
|
-
await oldState.watcher.stop();
|
|
3319
|
-
const { watcher: newWatcher, gitignoreFilter: newGitignoreFilter } = createWatcher(newConfig, factories, queue, processor, logger, runtimeOptions, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete);
|
|
3320
|
-
newWatcher.start();
|
|
3321
|
-
logger.info('Filesystem watcher rebuilt successfully');
|
|
3322
|
-
return { watcher: newWatcher, gitignoreFilter: newGitignoreFilter };
|
|
3323
|
-
}
|
|
3324
|
-
catch (error) {
|
|
3325
|
-
logger.error({ err: normalizeError(error) }, 'Failed to rebuild watcher, restoring previous');
|
|
3326
|
-
try {
|
|
3327
|
-
oldState.watcher.start();
|
|
3328
|
-
}
|
|
3329
|
-
catch (restartError) {
|
|
3330
|
-
logger.error({ err: normalizeError(restartError) }, 'Failed to restart previous watcher');
|
|
3331
|
-
}
|
|
3332
|
-
return oldState;
|
|
3333
|
-
}
|
|
3334
|
-
}
|
|
3335
|
-
/**
|
|
3336
|
-
* Check whether watch-relevant config fields changed between old and new config.
|
|
3337
|
-
*/
|
|
3338
|
-
function watchConfigChanged(oldConfig, newConfig) {
|
|
3339
|
-
return (JSON.stringify(oldConfig.watch.paths) !==
|
|
3340
|
-
JSON.stringify(newConfig.watch.paths) ||
|
|
3341
|
-
JSON.stringify(oldConfig.watch.ignored) !==
|
|
3342
|
-
JSON.stringify(newConfig.watch.ignored) ||
|
|
3343
|
-
JSON.stringify(oldConfig.watch.moveDetection) !==
|
|
3344
|
-
JSON.stringify(newConfig.watch.moveDetection) ||
|
|
3345
|
-
(oldConfig.watch.respectGitignore ?? true) !==
|
|
3346
|
-
(newConfig.watch.respectGitignore ?? true));
|
|
3347
|
-
}
|
|
3348
|
-
|
|
3349
3234
|
/**
|
|
3350
3235
|
* @module app/initialization
|
|
3351
3236
|
* Initialization helpers for JeevesWatcher. Extracted to follow SRP.
|
|
@@ -3488,6 +3373,68 @@ async function initVcs(config, logger) {
|
|
|
3488
3373
|
return true;
|
|
3489
3374
|
}
|
|
3490
3375
|
|
|
3376
|
+
/**
|
|
3377
|
+
* @module app/watcherFactory
|
|
3378
|
+
* Factory helpers for creating and rebuilding the filesystem watcher.
|
|
3379
|
+
* Extracted from initialization.ts to follow SRP.
|
|
3380
|
+
*/
|
|
3381
|
+
/**
|
|
3382
|
+
* Create file system watcher with gitignore filtering.
|
|
3383
|
+
*/
|
|
3384
|
+
function createWatcher(config, factories, queue, processor, logger, runtimeOptions, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete) {
|
|
3385
|
+
const respectGitignore = config.watch.respectGitignore ?? true;
|
|
3386
|
+
const gitignoreFilter = respectGitignore
|
|
3387
|
+
? new GitignoreFilter(extractWatchPathStrings(config.watch.paths))
|
|
3388
|
+
: undefined;
|
|
3389
|
+
const watcher = factories.createFileSystemWatcher(config.watch, queue, processor, logger, {
|
|
3390
|
+
maxRetries: config.maxRetries ?? runtimeOptions.maxRetries,
|
|
3391
|
+
maxBackoffMs: config.maxBackoffMs ?? runtimeOptions.maxBackoffMs,
|
|
3392
|
+
onFatalError: runtimeOptions.onFatalError,
|
|
3393
|
+
gitignoreFilter,
|
|
3394
|
+
initialScanTracker,
|
|
3395
|
+
contentHashCache,
|
|
3396
|
+
onVcsFileChange,
|
|
3397
|
+
onInitialScanComplete,
|
|
3398
|
+
});
|
|
3399
|
+
return { watcher, gitignoreFilter };
|
|
3400
|
+
}
|
|
3401
|
+
/**
|
|
3402
|
+
* Tear down and rebuild the filesystem watcher with new config.
|
|
3403
|
+
* Falls back to the old watcher if the new one fails to start.
|
|
3404
|
+
*/
|
|
3405
|
+
async function rebuildWatcher(newConfig, factories, queue, processor, logger, runtimeOptions, oldState, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete) {
|
|
3406
|
+
try {
|
|
3407
|
+
await oldState.watcher.stop();
|
|
3408
|
+
const { watcher: newWatcher, gitignoreFilter: newGitignoreFilter } = createWatcher(newConfig, factories, queue, processor, logger, runtimeOptions, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete);
|
|
3409
|
+
newWatcher.start();
|
|
3410
|
+
logger.info('Filesystem watcher rebuilt successfully');
|
|
3411
|
+
return { watcher: newWatcher, gitignoreFilter: newGitignoreFilter };
|
|
3412
|
+
}
|
|
3413
|
+
catch (error) {
|
|
3414
|
+
logger.error({ err: normalizeError(error) }, 'Failed to rebuild watcher, restoring previous');
|
|
3415
|
+
try {
|
|
3416
|
+
oldState.watcher.start();
|
|
3417
|
+
}
|
|
3418
|
+
catch (restartError) {
|
|
3419
|
+
logger.error({ err: normalizeError(restartError) }, 'Failed to restart previous watcher');
|
|
3420
|
+
}
|
|
3421
|
+
return oldState;
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
/**
|
|
3425
|
+
* Check whether watch-relevant config fields changed between old and new config.
|
|
3426
|
+
*/
|
|
3427
|
+
function watchConfigChanged(oldConfig, newConfig) {
|
|
3428
|
+
return (JSON.stringify(oldConfig.watch.paths) !==
|
|
3429
|
+
JSON.stringify(newConfig.watch.paths) ||
|
|
3430
|
+
JSON.stringify(oldConfig.watch.ignored) !==
|
|
3431
|
+
JSON.stringify(newConfig.watch.ignored) ||
|
|
3432
|
+
JSON.stringify(oldConfig.watch.moveDetection) !==
|
|
3433
|
+
JSON.stringify(newConfig.watch.moveDetection) ||
|
|
3434
|
+
(oldConfig.watch.respectGitignore ?? true) !==
|
|
3435
|
+
(newConfig.watch.respectGitignore ?? true));
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3491
3438
|
/**
|
|
3492
3439
|
* @module app/configReload
|
|
3493
3440
|
* Config-reload orchestration extracted from JeevesWatcher to follow SRP.
|
|
@@ -3579,6 +3526,49 @@ class ConfigWatcher {
|
|
|
3579
3526
|
}
|
|
3580
3527
|
}
|
|
3581
3528
|
|
|
3529
|
+
/**
|
|
3530
|
+
* @module api/handlers/configApply
|
|
3531
|
+
* Fastify route handler for POST /config/apply. Deep-merges a partial config
|
|
3532
|
+
* patch into the running config file, validates the result, writes it
|
|
3533
|
+
* atomically, and fires the apply callback.
|
|
3534
|
+
*/
|
|
3535
|
+
/**
|
|
3536
|
+
* Check whether a value is a plain object (not null, not an array).
|
|
3537
|
+
*
|
|
3538
|
+
* @param value - Value to check.
|
|
3539
|
+
* @returns True for plain objects.
|
|
3540
|
+
*/
|
|
3541
|
+
function isPlainObject$1(value) {
|
|
3542
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3543
|
+
}
|
|
3544
|
+
/**
|
|
3545
|
+
* Create handler for POST /config/apply.
|
|
3546
|
+
*
|
|
3547
|
+
* @remarks
|
|
3548
|
+
* Delegates to core `createConfigApplyHandler`, passing the service's own
|
|
3549
|
+
* config path so the patch is merged into the file the service actually
|
|
3550
|
+
* loaded (not a path derived from `configRoot`, which may be unset or
|
|
3551
|
+
* different in the service process). Accepts `{ patch }` (sent by the
|
|
3552
|
+
* plugin's `watcher_config_apply`) or `{ config }`.
|
|
3553
|
+
*
|
|
3554
|
+
* @param deps - Route dependencies.
|
|
3555
|
+
*/
|
|
3556
|
+
function createConfigApplyRouteHandler(deps) {
|
|
3557
|
+
const apply = createConfigApplyHandler({ ...deps.descriptor, onConfigApply: deps.onConfigApply }, deps.configPath || undefined);
|
|
3558
|
+
return async (request, reply) => {
|
|
3559
|
+
const body = request.body ?? {};
|
|
3560
|
+
const patch = body.patch ?? body.config ?? {};
|
|
3561
|
+
if (!isPlainObject$1(patch)) {
|
|
3562
|
+
return reply.status(400).send({
|
|
3563
|
+
error: 'Config validation failed',
|
|
3564
|
+
issues: [{ path: ['patch'], message: 'patch must be an object' }],
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
const result = await apply({ patch, replace: body.replace === true });
|
|
3568
|
+
return reply.status(result.status).send(result.body);
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
|
|
3582
3572
|
/**
|
|
3583
3573
|
* @module rules/attributes
|
|
3584
3574
|
* Builds file attribute objects for rule matching. Pure function: derives attributes from path, stats, and extracted data.
|
|
@@ -4784,6 +4774,61 @@ function createSearchHandler(deps) {
|
|
|
4784
4774
|
}, deps.logger, 'Search');
|
|
4785
4775
|
}
|
|
4786
4776
|
|
|
4777
|
+
/**
|
|
4778
|
+
* @module vcs/resolveWatchRoot
|
|
4779
|
+
* Resolves which VCS-enabled watch root(s) own a given absolute path or glob.
|
|
4780
|
+
*/
|
|
4781
|
+
/**
|
|
4782
|
+
* Resolve which VCS-enabled watch root owns an absolute path.
|
|
4783
|
+
*
|
|
4784
|
+
* @param coordinator - The VcsCoordinator with root information.
|
|
4785
|
+
* @param absolutePath - An absolute filesystem path.
|
|
4786
|
+
* @returns The matching root and relative path, or undefined if no root matches.
|
|
4787
|
+
*/
|
|
4788
|
+
function resolveWatchRoot(coordinator, absolutePath) {
|
|
4789
|
+
const normalized = normalizeSlashes(resolve(absolutePath));
|
|
4790
|
+
const root = findRootForPath(coordinator.getRoots(), normalized);
|
|
4791
|
+
if (!root)
|
|
4792
|
+
return undefined;
|
|
4793
|
+
return {
|
|
4794
|
+
root,
|
|
4795
|
+
relativePath: normalizeSlashes(relative(root, normalized)),
|
|
4796
|
+
};
|
|
4797
|
+
}
|
|
4798
|
+
/**
|
|
4799
|
+
* Resolve which VCS-enabled watch roots match a glob pattern.
|
|
4800
|
+
* For globs that could span multiple roots, returns all matching roots.
|
|
4801
|
+
*
|
|
4802
|
+
* @param coordinator - The VcsCoordinator with root information.
|
|
4803
|
+
* @param glob - An absolute glob pattern.
|
|
4804
|
+
* @returns Array of matching roots with relative glob patterns.
|
|
4805
|
+
*/
|
|
4806
|
+
function resolveWatchRootsForGlob(coordinator, glob) {
|
|
4807
|
+
const normalizedGlob = normalizeSlashes(glob);
|
|
4808
|
+
const roots = coordinator.getRoots();
|
|
4809
|
+
const results = [];
|
|
4810
|
+
const compareGlob = normalizePathCase(normalizedGlob);
|
|
4811
|
+
for (const root of roots) {
|
|
4812
|
+
const compareRoot = normalizePathCase(root);
|
|
4813
|
+
if (compareGlob.startsWith(compareRoot + '/') ||
|
|
4814
|
+
compareGlob === compareRoot) {
|
|
4815
|
+
// Glob is under this root
|
|
4816
|
+
results.push({
|
|
4817
|
+
root,
|
|
4818
|
+
relativePath: normalizeSlashes(relative(root, normalizedGlob)),
|
|
4819
|
+
});
|
|
4820
|
+
}
|
|
4821
|
+
else if (compareRoot.startsWith(compareGlob.replace(/[*?[\]{}]/g, ''))) {
|
|
4822
|
+
// Root is under the glob's base path — include with root-relative glob
|
|
4823
|
+
results.push({
|
|
4824
|
+
root,
|
|
4825
|
+
relativePath: '.',
|
|
4826
|
+
});
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
return results;
|
|
4830
|
+
}
|
|
4831
|
+
|
|
4787
4832
|
/**
|
|
4788
4833
|
* @module api/handlers/vcs/vcsCheckExclusion
|
|
4789
4834
|
* Fastify route handler for GET /vcs/check-exclusion. Checks gitignore status of a path.
|
|
@@ -5639,20 +5684,15 @@ function createApiServer(options) {
|
|
|
5639
5684
|
logger,
|
|
5640
5685
|
configDir: dirname(configPath),
|
|
5641
5686
|
}));
|
|
5642
|
-
|
|
5643
|
-
|
|
5687
|
+
app.post(getEndpoint('configApply').path, createConfigApplyRouteHandler({
|
|
5688
|
+
descriptor,
|
|
5689
|
+
configPath,
|
|
5644
5690
|
onConfigApply: () => {
|
|
5645
5691
|
const cfg = getConfig();
|
|
5646
|
-
|
|
5647
|
-
triggerReindex(reindexScope);
|
|
5692
|
+
triggerReindex(cfg.configWatch?.reindex ?? 'issues');
|
|
5648
5693
|
return Promise.resolve();
|
|
5649
5694
|
},
|
|
5650
|
-
});
|
|
5651
|
-
app.post(getEndpoint('configApply').path, async (request, reply) => {
|
|
5652
|
-
const { patch, replace } = request.body;
|
|
5653
|
-
const result = await coreConfigApplyHandler({ patch, replace });
|
|
5654
|
-
return reply.status(result.status).send(result.body);
|
|
5655
|
-
});
|
|
5695
|
+
}));
|
|
5656
5696
|
// Virtual rules and points deletion routes
|
|
5657
5697
|
if (virtualRuleStore) {
|
|
5658
5698
|
const onRulesChanged = createOnRulesChanged({
|
|
@@ -7261,14 +7301,14 @@ class VectorStoreClient {
|
|
|
7261
7301
|
* @returns An array of search results.
|
|
7262
7302
|
*/
|
|
7263
7303
|
async search(vector, limit, filter, offset) {
|
|
7264
|
-
const
|
|
7265
|
-
vector,
|
|
7304
|
+
const { points } = await this.client.query(this.collectionName, {
|
|
7305
|
+
query: vector,
|
|
7266
7306
|
limit,
|
|
7267
7307
|
with_payload: true,
|
|
7268
7308
|
...(filter ? { filter } : {}),
|
|
7269
7309
|
...(offset !== undefined ? { offset } : {}),
|
|
7270
7310
|
});
|
|
7271
|
-
return
|
|
7311
|
+
return points.map((r) => ({
|
|
7272
7312
|
id: String(r.id),
|
|
7273
7313
|
score: r.score,
|
|
7274
7314
|
payload: r.payload,
|
|
@@ -18,6 +18,45 @@ import { promisify } from 'node:util';
|
|
|
18
18
|
* @module api/handlers/configMerge
|
|
19
19
|
* Shared config merge utilities.
|
|
20
20
|
*/
|
|
21
|
+
/**
|
|
22
|
+
* Check whether a value is a plain object (not null, not an array).
|
|
23
|
+
*
|
|
24
|
+
* @param value - Value to check.
|
|
25
|
+
* @returns True for plain objects.
|
|
26
|
+
*/
|
|
27
|
+
function isPlainObject(value) {
|
|
28
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Deep-merge a config patch into a base config.
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* Plain objects are merged recursively; arrays and scalars in the patch
|
|
35
|
+
* replace the base value. `inferenceRules` is merged by rule name (see
|
|
36
|
+
* {@link mergeInferenceRules}). Neither input is mutated.
|
|
37
|
+
*
|
|
38
|
+
* @param base - The current config.
|
|
39
|
+
* @param patch - The partial config to apply.
|
|
40
|
+
* @returns A new merged config.
|
|
41
|
+
*/
|
|
42
|
+
function deepMergeConfig(base, patch) {
|
|
43
|
+
const result = { ...base };
|
|
44
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
45
|
+
const current = base[key];
|
|
46
|
+
if (key === 'inferenceRules' && Array.isArray(value)) {
|
|
47
|
+
result[key] = mergeInferenceRules(Array.isArray(current)
|
|
48
|
+
? current
|
|
49
|
+
: undefined, value);
|
|
50
|
+
}
|
|
51
|
+
else if (isPlainObject(current) && isPlainObject(value)) {
|
|
52
|
+
result[key] = deepMergeConfig(current, value);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
result[key] = value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
21
60
|
/**
|
|
22
61
|
* Merge inference rules by name: submitted rules replace existing by name, new ones are appended.
|
|
23
62
|
*/
|
|
@@ -226,7 +265,7 @@ async function startFromConfig(configPath, descriptor) {
|
|
|
226
265
|
const config = await loadConfig(resolvedPath);
|
|
227
266
|
// Dynamic import breaks the circular dependency between this module
|
|
228
267
|
// and app/index.ts (which defines JeevesWatcher and re-exports startFromConfig).
|
|
229
|
-
const { JeevesWatcher } = await import('./index-
|
|
268
|
+
const { JeevesWatcher } = await import('./index-BVfdWEhb.js');
|
|
230
269
|
const app = new JeevesWatcher(config, resolvedPath, descriptor);
|
|
231
270
|
installShutdownHandlers(() => app.stop());
|
|
232
271
|
await app.start();
|
|
@@ -1585,14 +1624,8 @@ const watcherDescriptor = {
|
|
|
1585
1624
|
// onConfigApply is overridden in createApiServer (api/index.ts) via the
|
|
1586
1625
|
// descriptor passed as a dependency, where it has access to the live
|
|
1587
1626
|
// reindex tracker and config getter.
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
return {
|
|
1591
|
-
...target,
|
|
1592
|
-
...source,
|
|
1593
|
-
inferenceRules: mergedRules,
|
|
1594
|
-
};
|
|
1595
|
-
},
|
|
1627
|
+
// Deep merge (like core's default) with inferenceRules merged by name.
|
|
1628
|
+
customMerge: deepMergeConfig,
|
|
1596
1629
|
startCommand: (configPath) => [
|
|
1597
1630
|
'node',
|
|
1598
1631
|
resolve(packageRoot, 'dist/cli/jeeves-watcher/index.js'),
|
|
@@ -1603,18 +1636,11 @@ const watcherDescriptor = {
|
|
|
1603
1636
|
run: async (configPath) => {
|
|
1604
1637
|
await startFromConfig(configPath, watcherDescriptor);
|
|
1605
1638
|
},
|
|
1606
|
-
// Content — generateToolsContent is wired in the plugin package
|
|
1607
|
-
// (watcherComponent.ts) where it has access to the API URL for menu generation.
|
|
1608
|
-
sectionId: 'Watcher',
|
|
1609
|
-
refreshIntervalSeconds: 71,
|
|
1610
|
-
generateToolsContent: () => '',
|
|
1611
1639
|
// Extension points
|
|
1612
1640
|
customCliCommands: (program) => {
|
|
1613
1641
|
registerCustomCommands(program);
|
|
1614
1642
|
},
|
|
1615
|
-
customPluginTools: (
|
|
1616
|
-
return [];
|
|
1617
|
-
},
|
|
1643
|
+
customPluginTools: () => [],
|
|
1618
1644
|
};
|
|
1619
1645
|
|
|
1620
1646
|
/**
|
|
@@ -1626,4 +1652,4 @@ const watcherDescriptor = {
|
|
|
1626
1652
|
const program = createServiceCli(watcherDescriptor);
|
|
1627
1653
|
program.parse();
|
|
1628
1654
|
|
|
1629
|
-
export { CONFIG_WATCH_VALID_SCOPES as C,
|
|
1655
|
+
export { CONFIG_WATCH_VALID_SCOPES as C, GIT_TIMEOUT_STANDARD as G, VALID_REINDEX_SCOPES as V, normalizeError as a, GIT_TIMEOUT_CHERRY_PICK as b, buildAuthenticatedPushUrl as c, GIT_TIMEOUT_PUSH as d, execFileAsync as e, gitAddViaStdin as f, getExecErrorFields as g, normalizePathCase as h, findRootForPath as i, GitignoreFilter as j, executeReindex as k, isPathWatched as l, mergeInferenceRules as m, normalizeSlashes as n, getWatchRootBases as o, loadConfig as p, startFromConfig as q, retry as r, sleep as s };
|
package/dist/index.d.ts
CHANGED
|
@@ -2029,4 +2029,4 @@ declare class SquashManager {
|
|
|
2029
2029
|
}
|
|
2030
2030
|
|
|
2031
2031
|
export { CommitMessageGenerator, ContentHashCache, DocumentProcessor, EnrichmentStore, EventQueue, FileSystemWatcher, GitignoreFilter, InitialScanTracker, IssuesManager, JeevesWatcher, ReindexTracker, SquashManager, SystemHealth, TemplateEngine, ValuesManager, VcsCoordinator, VcsManager, VectorStoreClient, VirtualRuleStore, applyRules, buildAttributes, buildTemplateEngine, compileRules, contentHash, createApiServer, createEmbeddingProvider, createHandlebarsInstance, createLogger, extractText, issueRecordSchema, loadConfig, loadCustomHelpers, mergeEnrichment, pointId, registerBuiltinHelpers, resolveTemplateSource, startFromConfig, watcherDescriptor };
|
|
2032
|
-
export type { AllHelpersIntrospection, ApiServerOptions, ApplyRulesOptions, ApplyRulesResult, CollectionInfo, CommitInfo, CompiledRule, CompiledTemplate, DocumentProcessorDeps, DocumentProcessorInterface, EmbeddingProvider, EnrichmentStoreInterface, EventQueueOptions, ExtractedText, Extractor, FileAttributes, FileSystemWatcherOptions, HelperModuleIntrospection, InitialScanStatus, IssueRecord, IssuesFile, JeevesWatcherFactories, JeevesWatcherRuntimeOptions, PayloadFieldSchema, PendingReversion, ProcessFn, ProcessorConfig, ProviderFactory, PushError, ReindexStatus, RenderResult, ResolvedWatchRoot, RuleLogger, ScrollPageResult, ScrolledPoint, SearchResult, SquashResult, SystemHealthOptions, ValuesIndex, VectorPoint, VectorStore, WatchEvent };
|
|
2032
|
+
export type { AllHelpersIntrospection, ApiServerOptions, ApplyRulesOptions, ApplyRulesResult, CollectionInfo, CommitInfo, CompiledRule, CompiledTemplate, DocumentProcessorDeps, DocumentProcessorInterface, EmbeddingProvider, EnrichmentStoreInterface, EventQueueOptions, ExtractedText, Extractor, FileAttributes, FileSystemWatcherOptions, HelperModuleIntrospection, InitialScanStatus, IssueRecord, IssuesFile, JeevesWatcherFactories, JeevesWatcherRuntimeOptions, PayloadFieldSchema, PendingReversion, ProcessFn, ProcessorConfig, ProviderFactory, PushError, ReindexStatus, RenderResult, ResolvedWatchRoot, RuleLogger, ScrollPageResult, ScrolledPoint, SearchResult, SquashManagerOptions, SquashResult, SystemHealthOptions, ValuesIndex, VectorPoint, VectorStore, WatchEvent };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join, dirname, resolve, relative, extname, basename, isAbsolute, sep } from 'node:path';
|
|
2
|
-
import { createConfigQueryHandler as createConfigQueryHandler$1, createStatusHandler,
|
|
2
|
+
import { createConfigApplyHandler, createConfigQueryHandler as createConfigQueryHandler$1, createStatusHandler, substituteEnvVars, DEFAULT_PORTS, getBindAddress, postJson, fetchJson, getPackageVersion } from '@karmaniverous/jeeves';
|
|
3
3
|
import { extractWatchPathStrings, inferenceRuleSchema, jeevesWatcherConfigSchema, normalizeWatchPaths, vcsRetentionConfigSchema, vcsAuthorConfigSchema, getEndpoint, LOGGING_DEFAULTS, API_DEFAULTS, EMBEDDING_DEFAULTS, CONFIG_WATCH_DEFAULTS, WATCH_DEFAULTS, ROOT_DEFAULTS, INIT_CONFIG_TEMPLATE, DEFAULT_PORT as DEFAULT_PORT$1, PLUGIN_PACKAGE, SERVICE_PACKAGE, COMPONENT_NAME } from '@karmaniverous/jeeves-watcher-core';
|
|
4
4
|
export { apiConfigSchema, configWatchConfigSchema, embeddingConfigSchema, inferenceRuleSchema, jeevesWatcherConfigSchema, loggingConfigSchema, vectorStoreConfigSchema, watchConfigSchema } from '@karmaniverous/jeeves-watcher-core';
|
|
5
5
|
import Fastify from 'fastify';
|
|
@@ -26,7 +26,7 @@ import { dump, load } from 'js-yaml';
|
|
|
26
26
|
import crypto from 'crypto';
|
|
27
27
|
import { packageDirectorySync } from 'package-directory';
|
|
28
28
|
import { homedir } from 'node:os';
|
|
29
|
-
import
|
|
29
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
30
30
|
import { deserialize, serialize } from 'node:v8';
|
|
31
31
|
import chokidar from 'chokidar';
|
|
32
32
|
import { cosmiconfig } from 'cosmiconfig';
|
|
@@ -1141,6 +1141,49 @@ async function getPathFileList(targetPath, config, gitignoreFilter) {
|
|
|
1141
1141
|
throw new Error(`Path is neither a file nor a directory: ${targetPath}`);
|
|
1142
1142
|
}
|
|
1143
1143
|
|
|
1144
|
+
/**
|
|
1145
|
+
* @module api/handlers/configApply
|
|
1146
|
+
* Fastify route handler for POST /config/apply. Deep-merges a partial config
|
|
1147
|
+
* patch into the running config file, validates the result, writes it
|
|
1148
|
+
* atomically, and fires the apply callback.
|
|
1149
|
+
*/
|
|
1150
|
+
/**
|
|
1151
|
+
* Check whether a value is a plain object (not null, not an array).
|
|
1152
|
+
*
|
|
1153
|
+
* @param value - Value to check.
|
|
1154
|
+
* @returns True for plain objects.
|
|
1155
|
+
*/
|
|
1156
|
+
function isPlainObject$2(value) {
|
|
1157
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Create handler for POST /config/apply.
|
|
1161
|
+
*
|
|
1162
|
+
* @remarks
|
|
1163
|
+
* Delegates to core `createConfigApplyHandler`, passing the service's own
|
|
1164
|
+
* config path so the patch is merged into the file the service actually
|
|
1165
|
+
* loaded (not a path derived from `configRoot`, which may be unset or
|
|
1166
|
+
* different in the service process). Accepts `{ patch }` (sent by the
|
|
1167
|
+
* plugin's `watcher_config_apply`) or `{ config }`.
|
|
1168
|
+
*
|
|
1169
|
+
* @param deps - Route dependencies.
|
|
1170
|
+
*/
|
|
1171
|
+
function createConfigApplyRouteHandler(deps) {
|
|
1172
|
+
const apply = createConfigApplyHandler({ ...deps.descriptor, onConfigApply: deps.onConfigApply }, deps.configPath || undefined);
|
|
1173
|
+
return async (request, reply) => {
|
|
1174
|
+
const body = request.body ?? {};
|
|
1175
|
+
const patch = body.patch ?? body.config ?? {};
|
|
1176
|
+
if (!isPlainObject$2(patch)) {
|
|
1177
|
+
return reply.status(400).send({
|
|
1178
|
+
error: 'Config validation failed',
|
|
1179
|
+
issues: [{ path: ['patch'], message: 'patch must be an object' }],
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
const result = await apply({ patch, replace: body.replace === true });
|
|
1183
|
+
return reply.status(result.status).send(result.body);
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1144
1187
|
/**
|
|
1145
1188
|
* @module rules/attributes
|
|
1146
1189
|
* Builds file attribute objects for rule matching. Pure function: derives attributes from path, stats, and extracted data.
|
|
@@ -2775,6 +2818,45 @@ async function applyRules(compiledRules, attributes, options = {}) {
|
|
|
2775
2818
|
* @module api/handlers/configMerge
|
|
2776
2819
|
* Shared config merge utilities.
|
|
2777
2820
|
*/
|
|
2821
|
+
/**
|
|
2822
|
+
* Check whether a value is a plain object (not null, not an array).
|
|
2823
|
+
*
|
|
2824
|
+
* @param value - Value to check.
|
|
2825
|
+
* @returns True for plain objects.
|
|
2826
|
+
*/
|
|
2827
|
+
function isPlainObject$1(value) {
|
|
2828
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2829
|
+
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Deep-merge a config patch into a base config.
|
|
2832
|
+
*
|
|
2833
|
+
* @remarks
|
|
2834
|
+
* Plain objects are merged recursively; arrays and scalars in the patch
|
|
2835
|
+
* replace the base value. `inferenceRules` is merged by rule name (see
|
|
2836
|
+
* {@link mergeInferenceRules}). Neither input is mutated.
|
|
2837
|
+
*
|
|
2838
|
+
* @param base - The current config.
|
|
2839
|
+
* @param patch - The partial config to apply.
|
|
2840
|
+
* @returns A new merged config.
|
|
2841
|
+
*/
|
|
2842
|
+
function deepMergeConfig(base, patch) {
|
|
2843
|
+
const result = { ...base };
|
|
2844
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
2845
|
+
const current = base[key];
|
|
2846
|
+
if (key === 'inferenceRules' && Array.isArray(value)) {
|
|
2847
|
+
result[key] = mergeInferenceRules(Array.isArray(current)
|
|
2848
|
+
? current
|
|
2849
|
+
: undefined, value);
|
|
2850
|
+
}
|
|
2851
|
+
else if (isPlainObject$1(current) && isPlainObject$1(value)) {
|
|
2852
|
+
result[key] = deepMergeConfig(current, value);
|
|
2853
|
+
}
|
|
2854
|
+
else {
|
|
2855
|
+
result[key] = value;
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
return result;
|
|
2859
|
+
}
|
|
2778
2860
|
/**
|
|
2779
2861
|
* Merge inference rules by name: submitted rules replace existing by name, new ones are appended.
|
|
2780
2862
|
*/
|
|
@@ -4290,91 +4372,6 @@ async function introspectAllHelpers(config, configDir) {
|
|
|
4290
4372
|
return result;
|
|
4291
4373
|
}
|
|
4292
4374
|
|
|
4293
|
-
/**
|
|
4294
|
-
* @module vcs/CommitMessageBuilder
|
|
4295
|
-
* Builds commit messages — both template-based and AI-generated.
|
|
4296
|
-
*/
|
|
4297
|
-
/**
|
|
4298
|
-
* Builds commit messages for VCS commits.
|
|
4299
|
-
* Supports template-based messages and AI-generated messages with fallback.
|
|
4300
|
-
*/
|
|
4301
|
-
class CommitMessageBuilder {
|
|
4302
|
-
rootPath;
|
|
4303
|
-
logger;
|
|
4304
|
-
generator;
|
|
4305
|
-
constructor(rootPath, logger, generator) {
|
|
4306
|
-
this.rootPath = rootPath;
|
|
4307
|
-
this.logger = logger;
|
|
4308
|
-
this.generator = generator;
|
|
4309
|
-
}
|
|
4310
|
-
/**
|
|
4311
|
-
* Build the template commit message (no AI).
|
|
4312
|
-
*/
|
|
4313
|
-
buildTemplateMessage(fileCount, isBaseline, pendingReversions) {
|
|
4314
|
-
if (pendingReversions.length === 0) {
|
|
4315
|
-
if (isBaseline) {
|
|
4316
|
-
return `baseline: batch (${String(fileCount)} files)`;
|
|
4317
|
-
}
|
|
4318
|
-
const timestamp = new Date().toISOString();
|
|
4319
|
-
return `watcher: batch ${timestamp} (${String(fileCount)} files)`;
|
|
4320
|
-
}
|
|
4321
|
-
// Count total reverted files across all pending reversions
|
|
4322
|
-
const revertedPaths = new Set(pendingReversions.flatMap((r) => r.paths));
|
|
4323
|
-
const revertedCount = revertedPaths.size;
|
|
4324
|
-
const otherCount = fileCount - revertedCount;
|
|
4325
|
-
// Use the first reversion's glob and commit for the message
|
|
4326
|
-
const { glob, commit } = pendingReversions[0];
|
|
4327
|
-
const shortCommit = commit.slice(0, 7);
|
|
4328
|
-
let message = `revert: ${glob} to ${shortCommit} — restored ${String(revertedCount)} files`;
|
|
4329
|
-
if (otherCount > 0) {
|
|
4330
|
-
message += ` (+ ${String(otherCount)} other changes)`;
|
|
4331
|
-
}
|
|
4332
|
-
return message;
|
|
4333
|
-
}
|
|
4334
|
-
/**
|
|
4335
|
-
* Build the commit message, using AI when available with template fallback.
|
|
4336
|
-
*/
|
|
4337
|
-
async buildCommitMessage(files, isBaseline, pendingReversions) {
|
|
4338
|
-
const fileCount = files.length;
|
|
4339
|
-
const templateMessage = this.buildTemplateMessage(fileCount, isBaseline, pendingReversions);
|
|
4340
|
-
if (!this.generator) {
|
|
4341
|
-
return templateMessage;
|
|
4342
|
-
}
|
|
4343
|
-
try {
|
|
4344
|
-
const diffSummary = await this.getStagedDiff();
|
|
4345
|
-
const aiMessage = await this.generator.generate(files, diffSummary);
|
|
4346
|
-
if (!aiMessage) {
|
|
4347
|
-
this.logger.warn('AI commit message generation returned null; using template');
|
|
4348
|
-
return templateMessage;
|
|
4349
|
-
}
|
|
4350
|
-
// For reversions: prefix with revert info + AI description
|
|
4351
|
-
if (pendingReversions.length > 0) {
|
|
4352
|
-
const { glob, commit } = pendingReversions[0];
|
|
4353
|
-
const shortCommit = commit.slice(0, 7);
|
|
4354
|
-
return `revert: ${glob} to ${shortCommit} — ${aiMessage}`;
|
|
4355
|
-
}
|
|
4356
|
-
return aiMessage;
|
|
4357
|
-
}
|
|
4358
|
-
catch (error) {
|
|
4359
|
-
this.logger.warn({ err: normalizeError(error) }, 'AI commit message generation failed; using template');
|
|
4360
|
-
return templateMessage;
|
|
4361
|
-
}
|
|
4362
|
-
}
|
|
4363
|
-
/**
|
|
4364
|
-
* Get the staged diff for AI context.
|
|
4365
|
-
*/
|
|
4366
|
-
async getStagedDiff() {
|
|
4367
|
-
try {
|
|
4368
|
-
const { stdout: stat } = await execFileAsync('git', ['diff', '--cached', '--stat'], { cwd: this.rootPath, timeout: GIT_TIMEOUT_STANDARD });
|
|
4369
|
-
const { stdout: patch } = await execFileAsync('git', ['diff', '--cached'], { cwd: this.rootPath, timeout: GIT_TIMEOUT_STANDARD });
|
|
4370
|
-
return `${stat}\n${patch}`;
|
|
4371
|
-
}
|
|
4372
|
-
catch {
|
|
4373
|
-
return '';
|
|
4374
|
-
}
|
|
4375
|
-
}
|
|
4376
|
-
}
|
|
4377
|
-
|
|
4378
4375
|
/**
|
|
4379
4376
|
* @module vcs/CommitMessageGenerator
|
|
4380
4377
|
* Generates AI-powered commit messages via the Anthropic Messages API.
|
|
@@ -4458,36 +4455,6 @@ class CommitMessageGenerator {
|
|
|
4458
4455
|
}
|
|
4459
4456
|
}
|
|
4460
4457
|
|
|
4461
|
-
/**
|
|
4462
|
-
* @module vcs/resolveCommitMessageApiKey
|
|
4463
|
-
* Resolves the API key for AI commit message generation.
|
|
4464
|
-
*/
|
|
4465
|
-
/**
|
|
4466
|
-
* Resolve the API key for commit message generation.
|
|
4467
|
-
* Checks config first, then falls back to OpenClaw gateway credentials.
|
|
4468
|
-
*/
|
|
4469
|
-
function resolveCommitMessageApiKey(provider, configApiKey, logger) {
|
|
4470
|
-
if (configApiKey) {
|
|
4471
|
-
logger.debug('Using commit message API key from config');
|
|
4472
|
-
return configApiKey;
|
|
4473
|
-
}
|
|
4474
|
-
try {
|
|
4475
|
-
const openclawPath = join(homedir(), '.openclaw', 'openclaw.json');
|
|
4476
|
-
const raw = readFileSync(openclawPath, 'utf8');
|
|
4477
|
-
const parsed = JSON.parse(raw);
|
|
4478
|
-
const gatewayKey = parsed.models?.providers?.[provider]?.apiKey;
|
|
4479
|
-
if (gatewayKey) {
|
|
4480
|
-
logger.debug({ provider }, 'Using commit message API key from OpenClaw gateway config');
|
|
4481
|
-
return gatewayKey;
|
|
4482
|
-
}
|
|
4483
|
-
logger.warn({ provider }, 'OpenClaw gateway config found but no API key for provider');
|
|
4484
|
-
}
|
|
4485
|
-
catch {
|
|
4486
|
-
logger.warn('No commit message API key in config or OpenClaw gateway');
|
|
4487
|
-
}
|
|
4488
|
-
return undefined;
|
|
4489
|
-
}
|
|
4490
|
-
|
|
4491
4458
|
/**
|
|
4492
4459
|
* @module vcs/SquashManager
|
|
4493
4460
|
* Handles scheduled squash retention for git-backed VCS roots.
|
|
@@ -5081,6 +5048,121 @@ function resolveIgnored(ignored) {
|
|
|
5081
5048
|
});
|
|
5082
5049
|
}
|
|
5083
5050
|
|
|
5051
|
+
/**
|
|
5052
|
+
* @module vcs/resolveCommitMessageApiKey
|
|
5053
|
+
* Resolves the API key for AI commit message generation.
|
|
5054
|
+
*/
|
|
5055
|
+
/**
|
|
5056
|
+
* Resolve the API key for commit message generation.
|
|
5057
|
+
* Checks config first, then falls back to OpenClaw gateway credentials.
|
|
5058
|
+
*/
|
|
5059
|
+
function resolveCommitMessageApiKey(provider, configApiKey, logger) {
|
|
5060
|
+
if (configApiKey) {
|
|
5061
|
+
logger.debug('Using commit message API key from config');
|
|
5062
|
+
return configApiKey;
|
|
5063
|
+
}
|
|
5064
|
+
try {
|
|
5065
|
+
const openclawPath = join(homedir(), '.openclaw', 'openclaw.json');
|
|
5066
|
+
const raw = readFileSync(openclawPath, 'utf8');
|
|
5067
|
+
const parsed = JSON.parse(raw);
|
|
5068
|
+
const gatewayKey = parsed.models?.providers?.[provider]?.apiKey;
|
|
5069
|
+
if (gatewayKey) {
|
|
5070
|
+
logger.debug({ provider }, 'Using commit message API key from OpenClaw gateway config');
|
|
5071
|
+
return gatewayKey;
|
|
5072
|
+
}
|
|
5073
|
+
logger.warn({ provider }, 'OpenClaw gateway config found but no API key for provider');
|
|
5074
|
+
}
|
|
5075
|
+
catch {
|
|
5076
|
+
logger.warn('No commit message API key in config or OpenClaw gateway');
|
|
5077
|
+
}
|
|
5078
|
+
return undefined;
|
|
5079
|
+
}
|
|
5080
|
+
|
|
5081
|
+
/**
|
|
5082
|
+
* @module vcs/CommitMessageBuilder
|
|
5083
|
+
* Builds commit messages — both template-based and AI-generated.
|
|
5084
|
+
*/
|
|
5085
|
+
/**
|
|
5086
|
+
* Builds commit messages for VCS commits.
|
|
5087
|
+
* Supports template-based messages and AI-generated messages with fallback.
|
|
5088
|
+
*/
|
|
5089
|
+
class CommitMessageBuilder {
|
|
5090
|
+
rootPath;
|
|
5091
|
+
logger;
|
|
5092
|
+
generator;
|
|
5093
|
+
constructor(rootPath, logger, generator) {
|
|
5094
|
+
this.rootPath = rootPath;
|
|
5095
|
+
this.logger = logger;
|
|
5096
|
+
this.generator = generator;
|
|
5097
|
+
}
|
|
5098
|
+
/**
|
|
5099
|
+
* Build the template commit message (no AI).
|
|
5100
|
+
*/
|
|
5101
|
+
buildTemplateMessage(fileCount, isBaseline, pendingReversions) {
|
|
5102
|
+
if (pendingReversions.length === 0) {
|
|
5103
|
+
if (isBaseline) {
|
|
5104
|
+
return `baseline: batch (${String(fileCount)} files)`;
|
|
5105
|
+
}
|
|
5106
|
+
const timestamp = new Date().toISOString();
|
|
5107
|
+
return `watcher: batch ${timestamp} (${String(fileCount)} files)`;
|
|
5108
|
+
}
|
|
5109
|
+
// Count total reverted files across all pending reversions
|
|
5110
|
+
const revertedPaths = new Set(pendingReversions.flatMap((r) => r.paths));
|
|
5111
|
+
const revertedCount = revertedPaths.size;
|
|
5112
|
+
const otherCount = fileCount - revertedCount;
|
|
5113
|
+
// Use the first reversion's glob and commit for the message
|
|
5114
|
+
const { glob, commit } = pendingReversions[0];
|
|
5115
|
+
const shortCommit = commit.slice(0, 7);
|
|
5116
|
+
let message = `revert: ${glob} to ${shortCommit} — restored ${String(revertedCount)} files`;
|
|
5117
|
+
if (otherCount > 0) {
|
|
5118
|
+
message += ` (+ ${String(otherCount)} other changes)`;
|
|
5119
|
+
}
|
|
5120
|
+
return message;
|
|
5121
|
+
}
|
|
5122
|
+
/**
|
|
5123
|
+
* Build the commit message, using AI when available with template fallback.
|
|
5124
|
+
*/
|
|
5125
|
+
async buildCommitMessage(files, isBaseline, pendingReversions) {
|
|
5126
|
+
const fileCount = files.length;
|
|
5127
|
+
const templateMessage = this.buildTemplateMessage(fileCount, isBaseline, pendingReversions);
|
|
5128
|
+
if (!this.generator) {
|
|
5129
|
+
return templateMessage;
|
|
5130
|
+
}
|
|
5131
|
+
try {
|
|
5132
|
+
const diffSummary = await this.getStagedDiff();
|
|
5133
|
+
const aiMessage = await this.generator.generate(files, diffSummary);
|
|
5134
|
+
if (!aiMessage) {
|
|
5135
|
+
this.logger.warn('AI commit message generation returned null; using template');
|
|
5136
|
+
return templateMessage;
|
|
5137
|
+
}
|
|
5138
|
+
// For reversions: prefix with revert info + AI description
|
|
5139
|
+
if (pendingReversions.length > 0) {
|
|
5140
|
+
const { glob, commit } = pendingReversions[0];
|
|
5141
|
+
const shortCommit = commit.slice(0, 7);
|
|
5142
|
+
return `revert: ${glob} to ${shortCommit} — ${aiMessage}`;
|
|
5143
|
+
}
|
|
5144
|
+
return aiMessage;
|
|
5145
|
+
}
|
|
5146
|
+
catch (error) {
|
|
5147
|
+
this.logger.warn({ err: normalizeError(error) }, 'AI commit message generation failed; using template');
|
|
5148
|
+
return templateMessage;
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5151
|
+
/**
|
|
5152
|
+
* Get the staged diff for AI context.
|
|
5153
|
+
*/
|
|
5154
|
+
async getStagedDiff() {
|
|
5155
|
+
try {
|
|
5156
|
+
const { stdout: stat } = await execFileAsync('git', ['diff', '--cached', '--stat'], { cwd: this.rootPath, timeout: GIT_TIMEOUT_STANDARD });
|
|
5157
|
+
const { stdout: patch } = await execFileAsync('git', ['diff', '--cached'], { cwd: this.rootPath, timeout: GIT_TIMEOUT_STANDARD });
|
|
5158
|
+
return `${stat}\n${patch}`;
|
|
5159
|
+
}
|
|
5160
|
+
catch {
|
|
5161
|
+
return '';
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
|
|
5084
5166
|
/**
|
|
5085
5167
|
* @module vcs/vcsPush
|
|
5086
5168
|
* Push logic for VCS commits.
|
|
@@ -5588,68 +5670,6 @@ class VcsCoordinator {
|
|
|
5588
5670
|
}
|
|
5589
5671
|
}
|
|
5590
5672
|
|
|
5591
|
-
/**
|
|
5592
|
-
* @module app/watcherFactory
|
|
5593
|
-
* Factory helpers for creating and rebuilding the filesystem watcher.
|
|
5594
|
-
* Extracted from initialization.ts to follow SRP.
|
|
5595
|
-
*/
|
|
5596
|
-
/**
|
|
5597
|
-
* Create file system watcher with gitignore filtering.
|
|
5598
|
-
*/
|
|
5599
|
-
function createWatcher(config, factories, queue, processor, logger, runtimeOptions, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete) {
|
|
5600
|
-
const respectGitignore = config.watch.respectGitignore ?? true;
|
|
5601
|
-
const gitignoreFilter = respectGitignore
|
|
5602
|
-
? new GitignoreFilter(extractWatchPathStrings(config.watch.paths))
|
|
5603
|
-
: undefined;
|
|
5604
|
-
const watcher = factories.createFileSystemWatcher(config.watch, queue, processor, logger, {
|
|
5605
|
-
maxRetries: config.maxRetries ?? runtimeOptions.maxRetries,
|
|
5606
|
-
maxBackoffMs: config.maxBackoffMs ?? runtimeOptions.maxBackoffMs,
|
|
5607
|
-
onFatalError: runtimeOptions.onFatalError,
|
|
5608
|
-
gitignoreFilter,
|
|
5609
|
-
initialScanTracker,
|
|
5610
|
-
contentHashCache,
|
|
5611
|
-
onVcsFileChange,
|
|
5612
|
-
onInitialScanComplete,
|
|
5613
|
-
});
|
|
5614
|
-
return { watcher, gitignoreFilter };
|
|
5615
|
-
}
|
|
5616
|
-
/**
|
|
5617
|
-
* Tear down and rebuild the filesystem watcher with new config.
|
|
5618
|
-
* Falls back to the old watcher if the new one fails to start.
|
|
5619
|
-
*/
|
|
5620
|
-
async function rebuildWatcher(newConfig, factories, queue, processor, logger, runtimeOptions, oldState, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete) {
|
|
5621
|
-
try {
|
|
5622
|
-
await oldState.watcher.stop();
|
|
5623
|
-
const { watcher: newWatcher, gitignoreFilter: newGitignoreFilter } = createWatcher(newConfig, factories, queue, processor, logger, runtimeOptions, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete);
|
|
5624
|
-
newWatcher.start();
|
|
5625
|
-
logger.info('Filesystem watcher rebuilt successfully');
|
|
5626
|
-
return { watcher: newWatcher, gitignoreFilter: newGitignoreFilter };
|
|
5627
|
-
}
|
|
5628
|
-
catch (error) {
|
|
5629
|
-
logger.error({ err: normalizeError(error) }, 'Failed to rebuild watcher, restoring previous');
|
|
5630
|
-
try {
|
|
5631
|
-
oldState.watcher.start();
|
|
5632
|
-
}
|
|
5633
|
-
catch (restartError) {
|
|
5634
|
-
logger.error({ err: normalizeError(restartError) }, 'Failed to restart previous watcher');
|
|
5635
|
-
}
|
|
5636
|
-
return oldState;
|
|
5637
|
-
}
|
|
5638
|
-
}
|
|
5639
|
-
/**
|
|
5640
|
-
* Check whether watch-relevant config fields changed between old and new config.
|
|
5641
|
-
*/
|
|
5642
|
-
function watchConfigChanged(oldConfig, newConfig) {
|
|
5643
|
-
return (JSON.stringify(oldConfig.watch.paths) !==
|
|
5644
|
-
JSON.stringify(newConfig.watch.paths) ||
|
|
5645
|
-
JSON.stringify(oldConfig.watch.ignored) !==
|
|
5646
|
-
JSON.stringify(newConfig.watch.ignored) ||
|
|
5647
|
-
JSON.stringify(oldConfig.watch.moveDetection) !==
|
|
5648
|
-
JSON.stringify(newConfig.watch.moveDetection) ||
|
|
5649
|
-
(oldConfig.watch.respectGitignore ?? true) !==
|
|
5650
|
-
(newConfig.watch.respectGitignore ?? true));
|
|
5651
|
-
}
|
|
5652
|
-
|
|
5653
5673
|
/**
|
|
5654
5674
|
* @module app/initialization
|
|
5655
5675
|
* Initialization helpers for JeevesWatcher. Extracted to follow SRP.
|
|
@@ -6173,20 +6193,15 @@ function createApiServer(options) {
|
|
|
6173
6193
|
logger,
|
|
6174
6194
|
configDir: dirname(configPath),
|
|
6175
6195
|
}));
|
|
6176
|
-
|
|
6177
|
-
|
|
6196
|
+
app.post(getEndpoint('configApply').path, createConfigApplyRouteHandler({
|
|
6197
|
+
descriptor,
|
|
6198
|
+
configPath,
|
|
6178
6199
|
onConfigApply: () => {
|
|
6179
6200
|
const cfg = getConfig();
|
|
6180
|
-
|
|
6181
|
-
triggerReindex(reindexScope);
|
|
6201
|
+
triggerReindex(cfg.configWatch?.reindex ?? 'issues');
|
|
6182
6202
|
return Promise.resolve();
|
|
6183
6203
|
},
|
|
6184
|
-
});
|
|
6185
|
-
app.post(getEndpoint('configApply').path, async (request, reply) => {
|
|
6186
|
-
const { patch, replace } = request.body;
|
|
6187
|
-
const result = await coreConfigApplyHandler({ patch, replace });
|
|
6188
|
-
return reply.status(result.status).send(result.body);
|
|
6189
|
-
});
|
|
6204
|
+
}));
|
|
6190
6205
|
// Virtual rules and points deletion routes
|
|
6191
6206
|
if (virtualRuleStore) {
|
|
6192
6207
|
const onRulesChanged = createOnRulesChanged({
|
|
@@ -6356,10 +6371,12 @@ class EnrichmentStore {
|
|
|
6356
6371
|
this.logger = logger;
|
|
6357
6372
|
mkdirSync(stateDir, { recursive: true });
|
|
6358
6373
|
const dbPath = join(stateDir, 'enrichments.sqlite');
|
|
6359
|
-
this.db = new
|
|
6360
|
-
this.db.
|
|
6361
|
-
this.db.
|
|
6362
|
-
const
|
|
6374
|
+
this.db = new DatabaseSync(dbPath);
|
|
6375
|
+
this.db.exec('PRAGMA journal_mode = WAL;');
|
|
6376
|
+
this.db.exec('PRAGMA busy_timeout = ' + BUSY_TIMEOUT_MS.toString() + ';');
|
|
6377
|
+
const checkpointStatus = this.db
|
|
6378
|
+
.prepare('PRAGMA wal_checkpoint(TRUNCATE)')
|
|
6379
|
+
.get();
|
|
6363
6380
|
if (checkpointStatus && checkpointStatus.busy > 0) {
|
|
6364
6381
|
// EnrichmentStore is expected to be single-writer. If we see a busy WAL
|
|
6365
6382
|
// checkpoint at startup, it's most likely from an unclean shutdown where
|
|
@@ -6738,6 +6755,68 @@ class ValuesManager extends BinaryFileStore {
|
|
|
6738
6755
|
}
|
|
6739
6756
|
}
|
|
6740
6757
|
|
|
6758
|
+
/**
|
|
6759
|
+
* @module app/watcherFactory
|
|
6760
|
+
* Factory helpers for creating and rebuilding the filesystem watcher.
|
|
6761
|
+
* Extracted from initialization.ts to follow SRP.
|
|
6762
|
+
*/
|
|
6763
|
+
/**
|
|
6764
|
+
* Create file system watcher with gitignore filtering.
|
|
6765
|
+
*/
|
|
6766
|
+
function createWatcher(config, factories, queue, processor, logger, runtimeOptions, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete) {
|
|
6767
|
+
const respectGitignore = config.watch.respectGitignore ?? true;
|
|
6768
|
+
const gitignoreFilter = respectGitignore
|
|
6769
|
+
? new GitignoreFilter(extractWatchPathStrings(config.watch.paths))
|
|
6770
|
+
: undefined;
|
|
6771
|
+
const watcher = factories.createFileSystemWatcher(config.watch, queue, processor, logger, {
|
|
6772
|
+
maxRetries: config.maxRetries ?? runtimeOptions.maxRetries,
|
|
6773
|
+
maxBackoffMs: config.maxBackoffMs ?? runtimeOptions.maxBackoffMs,
|
|
6774
|
+
onFatalError: runtimeOptions.onFatalError,
|
|
6775
|
+
gitignoreFilter,
|
|
6776
|
+
initialScanTracker,
|
|
6777
|
+
contentHashCache,
|
|
6778
|
+
onVcsFileChange,
|
|
6779
|
+
onInitialScanComplete,
|
|
6780
|
+
});
|
|
6781
|
+
return { watcher, gitignoreFilter };
|
|
6782
|
+
}
|
|
6783
|
+
/**
|
|
6784
|
+
* Tear down and rebuild the filesystem watcher with new config.
|
|
6785
|
+
* Falls back to the old watcher if the new one fails to start.
|
|
6786
|
+
*/
|
|
6787
|
+
async function rebuildWatcher(newConfig, factories, queue, processor, logger, runtimeOptions, oldState, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete) {
|
|
6788
|
+
try {
|
|
6789
|
+
await oldState.watcher.stop();
|
|
6790
|
+
const { watcher: newWatcher, gitignoreFilter: newGitignoreFilter } = createWatcher(newConfig, factories, queue, processor, logger, runtimeOptions, initialScanTracker, contentHashCache, onVcsFileChange, onInitialScanComplete);
|
|
6791
|
+
newWatcher.start();
|
|
6792
|
+
logger.info('Filesystem watcher rebuilt successfully');
|
|
6793
|
+
return { watcher: newWatcher, gitignoreFilter: newGitignoreFilter };
|
|
6794
|
+
}
|
|
6795
|
+
catch (error) {
|
|
6796
|
+
logger.error({ err: normalizeError(error) }, 'Failed to rebuild watcher, restoring previous');
|
|
6797
|
+
try {
|
|
6798
|
+
oldState.watcher.start();
|
|
6799
|
+
}
|
|
6800
|
+
catch (restartError) {
|
|
6801
|
+
logger.error({ err: normalizeError(restartError) }, 'Failed to restart previous watcher');
|
|
6802
|
+
}
|
|
6803
|
+
return oldState;
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
/**
|
|
6807
|
+
* Check whether watch-relevant config fields changed between old and new config.
|
|
6808
|
+
*/
|
|
6809
|
+
function watchConfigChanged(oldConfig, newConfig) {
|
|
6810
|
+
return (JSON.stringify(oldConfig.watch.paths) !==
|
|
6811
|
+
JSON.stringify(newConfig.watch.paths) ||
|
|
6812
|
+
JSON.stringify(oldConfig.watch.ignored) !==
|
|
6813
|
+
JSON.stringify(newConfig.watch.ignored) ||
|
|
6814
|
+
JSON.stringify(oldConfig.watch.moveDetection) !==
|
|
6815
|
+
JSON.stringify(newConfig.watch.moveDetection) ||
|
|
6816
|
+
(oldConfig.watch.respectGitignore ?? true) !==
|
|
6817
|
+
(newConfig.watch.respectGitignore ?? true));
|
|
6818
|
+
}
|
|
6819
|
+
|
|
6741
6820
|
/**
|
|
6742
6821
|
* @module app/configReload
|
|
6743
6822
|
* Config-reload orchestration extracted from JeevesWatcher to follow SRP.
|
|
@@ -8468,14 +8547,14 @@ class VectorStoreClient {
|
|
|
8468
8547
|
* @returns An array of search results.
|
|
8469
8548
|
*/
|
|
8470
8549
|
async search(vector, limit, filter, offset) {
|
|
8471
|
-
const
|
|
8472
|
-
vector,
|
|
8550
|
+
const { points } = await this.client.query(this.collectionName, {
|
|
8551
|
+
query: vector,
|
|
8473
8552
|
limit,
|
|
8474
8553
|
with_payload: true,
|
|
8475
8554
|
...(filter ? { filter } : {}),
|
|
8476
8555
|
...(offset !== undefined ? { offset } : {}),
|
|
8477
8556
|
});
|
|
8478
|
-
return
|
|
8557
|
+
return points.map((r) => ({
|
|
8479
8558
|
id: String(r.id),
|
|
8480
8559
|
score: r.score,
|
|
8481
8560
|
payload: r.payload,
|
|
@@ -9707,14 +9786,8 @@ const watcherDescriptor = {
|
|
|
9707
9786
|
// onConfigApply is overridden in createApiServer (api/index.ts) via the
|
|
9708
9787
|
// descriptor passed as a dependency, where it has access to the live
|
|
9709
9788
|
// reindex tracker and config getter.
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
return {
|
|
9713
|
-
...target,
|
|
9714
|
-
...source,
|
|
9715
|
-
inferenceRules: mergedRules,
|
|
9716
|
-
};
|
|
9717
|
-
},
|
|
9789
|
+
// Deep merge (like core's default) with inferenceRules merged by name.
|
|
9790
|
+
customMerge: deepMergeConfig,
|
|
9718
9791
|
startCommand: (configPath) => [
|
|
9719
9792
|
'node',
|
|
9720
9793
|
resolve(packageRoot, 'dist/cli/jeeves-watcher/index.js'),
|
|
@@ -9725,18 +9798,11 @@ const watcherDescriptor = {
|
|
|
9725
9798
|
run: async (configPath) => {
|
|
9726
9799
|
await startFromConfig(configPath, watcherDescriptor);
|
|
9727
9800
|
},
|
|
9728
|
-
// Content — generateToolsContent is wired in the plugin package
|
|
9729
|
-
// (watcherComponent.ts) where it has access to the API URL for menu generation.
|
|
9730
|
-
sectionId: 'Watcher',
|
|
9731
|
-
refreshIntervalSeconds: 71,
|
|
9732
|
-
generateToolsContent: () => '',
|
|
9733
9801
|
// Extension points
|
|
9734
9802
|
customCliCommands: (program) => {
|
|
9735
9803
|
registerCustomCommands(program);
|
|
9736
9804
|
},
|
|
9737
|
-
customPluginTools: (
|
|
9738
|
-
return [];
|
|
9739
|
-
},
|
|
9805
|
+
customPluginTools: () => [],
|
|
9740
9806
|
};
|
|
9741
9807
|
|
|
9742
9808
|
export { CommitMessageGenerator, ContentHashCache, DocumentProcessor, EnrichmentStore, EventQueue, FileSystemWatcher, GitignoreFilter, InitialScanTracker, IssuesManager, JeevesWatcher, ReindexTracker, SquashManager, SystemHealth, TemplateEngine, ValuesManager, VcsCoordinator, VcsManager, VectorStoreClient, VirtualRuleStore, applyRules, buildAttributes, buildTemplateEngine, compileRules, contentHash, createApiServer, createEmbeddingProvider, createHandlebarsInstance, createLogger, extractText, issueRecordSchema, loadConfig, loadCustomHelpers, mergeEnrichment, pointId, registerBuiltinHelpers, resolveTemplateSource, startFromConfig, watcherDescriptor };
|
package/package.json
CHANGED
|
@@ -7,66 +7,63 @@
|
|
|
7
7
|
"url": "https://github.com/karmaniverous/jeeves-watcher/issues"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@commander-js/extra-typings": "^
|
|
11
|
-
"@karmaniverous/jeeves": "
|
|
12
|
-
"@karmaniverous/jeeves-watcher-core": "^0.2.
|
|
10
|
+
"@commander-js/extra-typings": "^15.0.0",
|
|
11
|
+
"@karmaniverous/jeeves": "0.6.0-7",
|
|
12
|
+
"@karmaniverous/jeeves-watcher-core": "^0.2.6-0",
|
|
13
13
|
"@karmaniverous/jsonmap": "^2.1.1",
|
|
14
14
|
"@langchain/textsplitters": "*",
|
|
15
|
-
"@qdrant/js-client-rest": "
|
|
16
|
-
"ajv": "~8.
|
|
15
|
+
"@qdrant/js-client-rest": "^1.19.0",
|
|
16
|
+
"ajv": "~8.20.0",
|
|
17
17
|
"ajv-formats": "*",
|
|
18
|
-
"better-sqlite3": "^12.10.0",
|
|
19
18
|
"cheerio": "^1.2.0",
|
|
20
19
|
"chokidar": "^5.0.0",
|
|
21
20
|
"cosmiconfig": "*",
|
|
22
|
-
"dayjs": "^1.11.
|
|
21
|
+
"dayjs": "^1.11.23",
|
|
23
22
|
"fastify": "*",
|
|
24
23
|
"handlebars": "^4.7.9",
|
|
25
24
|
"hast-util-to-mdast": "^10.1.2",
|
|
26
|
-
"ignore": "^7.0.
|
|
27
|
-
"js-yaml": "^4.
|
|
28
|
-
"mammoth": "^1.12.
|
|
25
|
+
"ignore": "^7.0.10",
|
|
26
|
+
"js-yaml": "^5.4.2",
|
|
27
|
+
"mammoth": "^1.12.3",
|
|
29
28
|
"mdast-util-from-adf": "^2.2.0",
|
|
30
29
|
"mdast-util-to-markdown": "^2.1.2",
|
|
31
30
|
"package-directory": "^8.2.0",
|
|
32
|
-
"picomatch": "^4.0.
|
|
31
|
+
"picomatch": "^4.0.7",
|
|
33
32
|
"pino": "*",
|
|
34
33
|
"radash": "^12.1.1",
|
|
35
34
|
"rehype-parse": "^9.0.1",
|
|
36
35
|
"unified": "^11.0.5",
|
|
37
|
-
"unpdf": "^1.
|
|
36
|
+
"unpdf": "^1.8.1",
|
|
38
37
|
"uuid": "*",
|
|
39
|
-
"zod": "^4.
|
|
38
|
+
"zod": "^4.6.5"
|
|
40
39
|
},
|
|
41
40
|
"description": "Filesystem watcher that keeps a Qdrant vector store in sync with document changes",
|
|
42
41
|
"devDependencies": {
|
|
43
|
-
"@dotenvx/dotenvx": "^
|
|
42
|
+
"@dotenvx/dotenvx": "^2.30.0",
|
|
44
43
|
"@rollup/plugin-alias": "^6.0.0",
|
|
45
|
-
"@rollup/plugin-commonjs": "^29.0.
|
|
44
|
+
"@rollup/plugin-commonjs": "^29.0.3",
|
|
46
45
|
"@rollup/plugin-json": "^6.1.0",
|
|
47
46
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
48
47
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
49
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
50
48
|
"@types/fs-extra": "^11.0.4",
|
|
51
|
-
"@types/
|
|
52
|
-
"@types/node": "^25.9.1",
|
|
49
|
+
"@types/node": "^26.6.3",
|
|
53
50
|
"@types/picomatch": "^4.0.3",
|
|
54
|
-
"@vitest/coverage-v8": "^
|
|
51
|
+
"@vitest/coverage-v8": "^5.0.2",
|
|
55
52
|
"cross-env": "^10.1.0",
|
|
56
|
-
"fs-extra": "^11.
|
|
57
|
-
"happy-dom": "^20.
|
|
58
|
-
"knip": "^6.
|
|
59
|
-
"release-it": "^
|
|
60
|
-
"rollup": "^4.
|
|
61
|
-
"rollup-plugin-dts": "^6.
|
|
53
|
+
"fs-extra": "^11.4.1",
|
|
54
|
+
"happy-dom": "^20.14.5",
|
|
55
|
+
"knip": "^6.38.0",
|
|
56
|
+
"release-it": "^21.1.0",
|
|
57
|
+
"rollup": "^4.63.5",
|
|
58
|
+
"rollup-plugin-dts": "^6.5.1",
|
|
62
59
|
"tslib": "^2.8.1",
|
|
63
|
-
"typedoc": "^0.28.
|
|
60
|
+
"typedoc": "^0.28.20",
|
|
64
61
|
"typedoc-plugin-mdn-links": "^5.1.1",
|
|
65
62
|
"typedoc-plugin-replace-text": "^4.2.0",
|
|
66
|
-
"vitest": "^
|
|
63
|
+
"vitest": "^5.0.2"
|
|
67
64
|
},
|
|
68
65
|
"engines": {
|
|
69
|
-
"node": ">=22"
|
|
66
|
+
"node": ">=22.13"
|
|
70
67
|
},
|
|
71
68
|
"exports": {
|
|
72
69
|
".": {
|
|
@@ -110,6 +107,10 @@
|
|
|
110
107
|
"release": true
|
|
111
108
|
},
|
|
112
109
|
"hooks": {
|
|
110
|
+
"after:bump": [
|
|
111
|
+
"npm install --package-lock-only --ignore-scripts",
|
|
112
|
+
"git add ../../package-lock.json"
|
|
113
|
+
],
|
|
113
114
|
"after:init": [
|
|
114
115
|
"npm run lint",
|
|
115
116
|
"npm run typecheck",
|
|
@@ -136,18 +137,18 @@
|
|
|
136
137
|
"url": "git+https://github.com/karmaniverous/jeeves-watcher.git"
|
|
137
138
|
},
|
|
138
139
|
"scripts": {
|
|
139
|
-
"build": "npm run generate:schema && rimraf dist && cross-env NO_COLOR=1 rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
|
|
140
|
+
"build": "npm run generate:schema && rimraf dist && cross-env NO_COLOR=1 rollup --config rollup.config.ts --configPlugin \"@rollup/plugin-typescript={paths:{}}\"",
|
|
140
141
|
"changelog": "git-cliff -o CHANGELOG.md",
|
|
141
142
|
"diagrams": "cd diagrams && plantuml -tpng -o ../assets -r .",
|
|
142
143
|
"generate:schema": "tsx src/config/generate-schema.ts",
|
|
143
144
|
"lint": "eslint .",
|
|
144
145
|
"lint:fix": "eslint --fix .",
|
|
145
146
|
"release": "dotenvx run -f .env.local -- release-it",
|
|
146
|
-
"release:pre": "dotenvx run -f .env.local -- release-it --no-git.requireBranch --github.
|
|
147
|
+
"release:pre": "dotenvx run -f .env.local -- release-it --no-git.requireBranch --github.preRelease --preRelease",
|
|
147
148
|
"test": "vitest run",
|
|
148
149
|
"typecheck": "tsc"
|
|
149
150
|
},
|
|
150
151
|
"type": "module",
|
|
151
152
|
"types": "dist/index.d.ts",
|
|
152
|
-
"version": "0.
|
|
153
|
+
"version": "0.19.0-1"
|
|
153
154
|
}
|