@harperfast/harper 5.2.0 → 5.2.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/bin/copyDb.ts +21 -4
- package/bin/harper.ts +20 -51
- package/bin/help.ts +216 -0
- package/components/Application.ts +236 -46
- package/components/ApplicationScope.ts +26 -0
- package/components/EntryHandler.ts +410 -105
- package/components/RuntimeModuleTracker.ts +189 -0
- package/components/Scope.ts +68 -24
- package/components/componentLoader.ts +70 -16
- package/components/deployLifecycle.ts +119 -33
- package/components/mcp/tools/operations.ts +6 -0
- package/components/operations.js +4 -6
- package/config/configUtils.ts +12 -9
- package/config-root.schema.json +10 -0
- package/dataLayer/harperBridge/ResourceBridge.ts +26 -5
- package/dataLayer/hdbInfoController.ts +8 -0
- package/dataLayer/schemaDescribe.ts +2 -1
- package/dist/bin/copyDb.js +13 -2
- package/dist/bin/copyDb.js.map +1 -1
- package/dist/bin/harper.d.ts +6 -0
- package/dist/bin/harper.js +18 -50
- package/dist/bin/harper.js.map +1 -1
- package/dist/bin/help.d.ts +8 -0
- package/dist/bin/help.js +192 -0
- package/dist/bin/help.js.map +1 -0
- package/dist/components/Application.d.ts +16 -1
- package/dist/components/Application.js +210 -38
- package/dist/components/Application.js.map +1 -1
- package/dist/components/ApplicationScope.d.ts +7 -0
- package/dist/components/ApplicationScope.js +22 -0
- package/dist/components/ApplicationScope.js.map +1 -1
- package/dist/components/EntryHandler.d.ts +4 -4
- package/dist/components/EntryHandler.js +386 -95
- package/dist/components/EntryHandler.js.map +1 -1
- package/dist/components/RuntimeModuleTracker.d.ts +11 -0
- package/dist/components/RuntimeModuleTracker.js +189 -0
- package/dist/components/RuntimeModuleTracker.js.map +1 -0
- package/dist/components/Scope.d.ts +1 -0
- package/dist/components/Scope.js +69 -22
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/componentLoader.js +69 -10
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/deployLifecycle.d.ts +6 -2
- package/dist/components/deployLifecycle.js +109 -31
- package/dist/components/deployLifecycle.js.map +1 -1
- package/dist/components/mcp/tools/operations.js +6 -0
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/operations.js +4 -6
- package/dist/components/operations.js.map +1 -1
- package/dist/config/configUtils.js +13 -9
- package/dist/config/configUtils.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +12 -5
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/hdbInfoController.js +4 -0
- package/dist/dataLayer/hdbInfoController.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +2 -1
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/resources/DatabaseTransaction.d.ts +55 -0
- package/dist/resources/DatabaseTransaction.js +282 -103
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +8 -2
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/Resources.js +22 -4
- package/dist/resources/Resources.js.map +1 -1
- package/dist/resources/Table.d.ts +6 -5
- package/dist/resources/Table.js +92 -15
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/write.js +6 -6
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/blob.d.ts +0 -1
- package/dist/resources/blob.js +15 -7
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/databases.d.ts +22 -5
- package/dist/resources/databases.js +118 -9
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/jsResource.d.ts +4 -26
- package/dist/resources/jsResource.js +5 -59
- package/dist/resources/jsResource.js.map +1 -1
- package/dist/resources/models/Models.d.ts +11 -1
- package/dist/resources/models/Models.js +10 -1
- package/dist/resources/models/Models.js.map +1 -1
- package/dist/resources/models/backendRegistry.d.ts +9 -0
- package/dist/resources/models/backendRegistry.js +10 -0
- package/dist/resources/models/backendRegistry.js.map +1 -1
- package/dist/resources/models/openaiStream.d.ts +16 -1
- package/dist/resources/models/openaiStream.js +113 -21
- package/dist/resources/models/openaiStream.js.map +1 -1
- package/dist/resources/models/v1/chatCompletions.d.ts +54 -0
- package/dist/resources/models/v1/chatCompletions.js +115 -0
- package/dist/resources/models/v1/chatCompletions.js.map +1 -0
- package/dist/resources/models/v1/embeddings.d.ts +11 -0
- package/dist/resources/models/v1/embeddings.js +71 -0
- package/dist/resources/models/v1/embeddings.js.map +1 -0
- package/dist/resources/models/v1/errors.d.ts +54 -0
- package/dist/resources/models/v1/errors.js +130 -0
- package/dist/resources/models/v1/errors.js.map +1 -0
- package/dist/resources/models/v1/index.d.ts +36 -0
- package/dist/resources/models/v1/index.js +75 -0
- package/dist/resources/models/v1/index.js.map +1 -0
- package/dist/resources/models/v1/models.d.ts +26 -0
- package/dist/resources/models/v1/models.js +44 -0
- package/dist/resources/models/v1/models.js.map +1 -0
- package/dist/resources/models/v1/translation.d.ts +133 -0
- package/dist/resources/models/v1/translation.js +298 -0
- package/dist/resources/models/v1/translation.js.map +1 -0
- package/dist/resources/roles.d.ts +1 -1
- package/dist/resources/roles.js +54 -7
- package/dist/resources/roles.js.map +1 -1
- package/dist/security/jsLoader.js +84 -33
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/role.js +4 -0
- package/dist/security/role.js.map +1 -1
- package/dist/security/superUserGuard.d.ts +7 -0
- package/dist/security/superUserGuard.js +23 -0
- package/dist/security/superUserGuard.js.map +1 -0
- package/dist/security/tokenAuthentication.d.ts +0 -1
- package/dist/security/tokenAuthentication.js +6 -2
- package/dist/security/tokenAuthentication.js.map +1 -1
- package/dist/security/user.d.ts +6 -1
- package/dist/security/user.js +23 -1
- package/dist/security/user.js.map +1 -1
- package/dist/server/http.d.ts +23 -2
- package/dist/server/http.js +99 -12
- package/dist/server/http.js.map +1 -1
- package/dist/server/operationsServer.d.ts +0 -2
- package/dist/server/operationsServer.js.map +1 -1
- package/dist/server/serverHelpers/multipartParser.js +9 -0
- package/dist/server/serverHelpers/multipartParser.js.map +1 -1
- package/dist/server/serverHelpers/operationAuthorizationState.d.ts +2 -0
- package/dist/server/serverHelpers/operationAuthorizationState.js +13 -0
- package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -0
- package/dist/server/serverHelpers/registeredOperations.d.ts +3 -2
- package/dist/server/serverHelpers/registeredOperations.js +14 -13
- package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
- package/dist/server/serverHelpers/serverHandlers.js +19 -3
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +1 -1
- package/dist/server/serverHelpers/serverUtilities.js +10 -7
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/static.js +57 -18
- package/dist/server/static.js.map +1 -1
- package/dist/server/storageReclamation.d.ts +17 -0
- package/dist/server/storageReclamation.js +90 -6
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/socketRouter.js +20 -0
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadServer.js +12 -0
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/sqlEngine/diff/differential.js +7 -3
- package/dist/sqlEngine/diff/differential.js.map +1 -1
- package/dist/sqlTranslator/index.js +6 -1
- package/dist/sqlTranslator/index.js.map +1 -1
- package/dist/upgrade/upgradePrompt.d.ts +2 -2
- package/dist/upgrade/upgradePrompt.js +22 -3
- package/dist/upgrade/upgradePrompt.js.map +1 -1
- package/dist/utility/errors/commonErrors.d.ts +1 -0
- package/dist/utility/errors/commonErrors.js +1 -0
- package/dist/utility/errors/commonErrors.js.map +1 -1
- package/dist/utility/hdbTerms.d.ts +2 -0
- package/dist/utility/hdbTerms.js +2 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/install/installer.js +32 -0
- package/dist/utility/install/installer.js.map +1 -1
- package/dist/utility/logging/harper_logger.d.ts +7 -0
- package/dist/utility/logging/harper_logger.js +57 -24
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/npm-shrinkwrap.json +197 -193
- package/package.json +9 -7
- package/resources/DatabaseTransaction.ts +310 -101
- package/resources/ResourceInterface.ts +8 -2
- package/resources/Resources.ts +22 -4
- package/resources/Table.ts +321 -243
- package/resources/analytics/write.ts +22 -20
- package/resources/blob.ts +15 -8
- package/resources/databases.ts +123 -12
- package/resources/jsResource.ts +5 -62
- package/resources/models/Models.ts +14 -1
- package/resources/models/backendRegistry.ts +10 -0
- package/resources/models/openaiStream.ts +131 -19
- package/resources/models/v1/chatCompletions.ts +128 -0
- package/resources/models/v1/embeddings.ts +70 -0
- package/resources/models/v1/errors.ts +141 -0
- package/resources/models/v1/index.ts +72 -0
- package/resources/models/v1/models.ts +53 -0
- package/resources/models/v1/translation.ts +362 -0
- package/resources/roles.ts +67 -7
- package/security/jsLoader.ts +84 -30
- package/security/role.ts +7 -0
- package/security/superUserGuard.ts +20 -0
- package/security/tokenAuthentication.ts +6 -3
- package/security/user.ts +26 -1
- package/server/DESIGN.md +45 -34
- package/server/http.ts +100 -13
- package/server/operationsServer.ts +0 -2
- package/server/serverHelpers/multipartParser.ts +9 -0
- package/server/serverHelpers/operationAuthorizationState.ts +11 -0
- package/server/serverHelpers/registeredOperations.ts +19 -15
- package/server/serverHelpers/serverHandlers.js +20 -3
- package/server/serverHelpers/serverUtilities.ts +10 -7
- package/server/static.ts +75 -20
- package/server/storageReclamation.ts +104 -8
- package/server/threads/socketRouter.ts +20 -0
- package/server/threads/threadServer.js +11 -0
- package/sqlTranslator/index.ts +6 -1
- package/static/defaultConfig.yaml +2 -0
- package/studio/web/assets/{Chat-DoVWScmq.js → Chat-aApwhRmz.js} +2 -2
- package/studio/web/assets/{Chat-DoVWScmq.js.map → Chat-aApwhRmz.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js → FloatingChat-sC0H91n9.js} +4 -4
- package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js.map → FloatingChat-sC0H91n9.js.map} +1 -1
- package/studio/web/assets/{apiToken-BUI_04o7.js → apiToken-DZ8JhHOI.js} +2 -2
- package/studio/web/assets/{apiToken-BUI_04o7.js.map → apiToken-DZ8JhHOI.js.map} +1 -1
- package/studio/web/assets/{applications-D03NA7wW.js → applications-9SEVLIO5.js} +2 -2
- package/studio/web/assets/{applications-D03NA7wW.js.map → applications-9SEVLIO5.js.map} +1 -1
- package/studio/web/assets/{index-Bh_CNAHr.js → index-Dy3uDGXb.js} +6 -6
- package/studio/web/assets/{index-Bh_CNAHr.js.map → index-Dy3uDGXb.js.map} +1 -1
- package/studio/web/assets/{index.lazy-Dx3MpyDC.js → index.lazy-CN1zq4I4.js} +4 -4
- package/studio/web/assets/{index.lazy-Dx3MpyDC.js.map → index.lazy-CN1zq4I4.js.map} +1 -1
- package/studio/web/assets/{notifications-0edoFTsb.js → notifications-BFAF07xr.js} +2 -2
- package/studio/web/assets/{notifications-0edoFTsb.js.map → notifications-BFAF07xr.js.map} +1 -1
- package/studio/web/assets/{notifications-CwKhipK7.js → notifications-CKlYVvVN.js} +2 -2
- package/studio/web/assets/{notifications-CwKhipK7.js.map → notifications-CKlYVvVN.js.map} +1 -1
- package/studio/web/assets/{profile-DUfEPQtx.js → profile-A1zhEdFG.js} +2 -2
- package/studio/web/assets/{profile-DUfEPQtx.js.map → profile-A1zhEdFG.js.map} +1 -1
- package/studio/web/assets/{setComponentFile-DMPo4UjC.js → setComponentFile-SEtBt_GV.js} +2 -2
- package/studio/web/assets/{setComponentFile-DMPo4UjC.js.map → setComponentFile-SEtBt_GV.js.map} +1 -1
- package/studio/web/assets/{setup-B56Oz1_u.js → setup-BisINqdH.js} +2 -2
- package/studio/web/assets/{setup-B56Oz1_u.js.map → setup-BisINqdH.js.map} +1 -1
- package/studio/web/assets/{status-BAod7p3o.js → status-BuQoCc7l.js} +2 -2
- package/studio/web/assets/{status-BAod7p3o.js.map → status-BuQoCc7l.js.map} +1 -1
- package/studio/web/assets/{swagger-ui-react-lQrBxfwM.js → swagger-ui-react-CAi_s1PC.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-lQrBxfwM.js.map → swagger-ui-react-CAi_s1PC.js.map} +1 -1
- package/studio/web/assets/{tsMode-CrHCRjTK.js → tsMode-DT74tlkM.js} +2 -2
- package/studio/web/assets/{tsMode-CrHCRjTK.js.map → tsMode-DT74tlkM.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js → useEntityRestURL-CGRGc1n7.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js.map → useEntityRestURL-CGRGc1n7.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/upgradePrompt.ts +22 -3
- package/utility/errors/commonErrors.ts +2 -0
- package/utility/hdbTerms.ts +2 -0
- package/utility/install/installer.ts +37 -0
- package/utility/logging/harper_logger.ts +57 -26
package/bin/copyDb.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getDatabases,
|
|
3
|
+
getDefaultCompression,
|
|
4
|
+
resetDatabases,
|
|
5
|
+
getRocksCompression,
|
|
6
|
+
toRocksCompression,
|
|
7
|
+
} from '../resources/databases.ts';
|
|
2
8
|
import { open, asBinary } from 'lmdb';
|
|
3
9
|
import { join } from 'path';
|
|
4
10
|
import { move, remove } from 'fs-extra';
|
|
@@ -329,6 +335,10 @@ export function shapeForStructure(value: any): any {
|
|
|
329
335
|
|
|
330
336
|
function openRocksDb(path: string, options: RocksDatabaseOptions & { dupSort?: boolean } = {}) {
|
|
331
337
|
options.disableWAL ??= false;
|
|
338
|
+
// Migration creates a complete replacement database, so use the deployment codec for the files
|
|
339
|
+
// it writes; runtime opens additionally reconcile pre-existing sibling column families.
|
|
340
|
+
const legacyOptions = options as { compression?: unknown };
|
|
341
|
+
legacyOptions.compression = getRocksCompression() ?? toRocksCompression(legacyOptions.compression);
|
|
332
342
|
if (!existsSync(path)) {
|
|
333
343
|
mkdirSync(path, { recursive: true });
|
|
334
344
|
}
|
|
@@ -510,7 +520,14 @@ export async function migrateDatabaseToRocks(sourceRootStore, databaseName: stri
|
|
|
510
520
|
export async function copyDbToRocks(sourceRootStore, sourceDatabase: string, targetPath: string) {
|
|
511
521
|
console.log(`Migrating database ${sourceDatabase} to RocksDB at ${targetPath}`);
|
|
512
522
|
const sourceDbisDb = sourceRootStore.dbisDb;
|
|
513
|
-
|
|
523
|
+
// Runtime Harper stores disable RocksDB's native WAL for data/index column families and recover
|
|
524
|
+
// them from rocksdb-js transaction logs. This copier does not write those transaction logs, so
|
|
525
|
+
// only use the same fast write path for migrateOnStart's disposable staging directory: after an
|
|
526
|
+
// interruption it is deleted and recopied from LMDB. Direct copyDbToRocks callers retain WAL.
|
|
527
|
+
const disableDataWAL = targetPath.endsWith(MIGRATING_DIR_SUFFIX);
|
|
528
|
+
|
|
529
|
+
// Keep native WAL for the root/log-owner and __dbis__ handles, matching Harper's runtime policy.
|
|
530
|
+
// Their migration writes are metadata-sized; the 600 GB bulk is in the data/index handles below.
|
|
514
531
|
const targetRootStore = openRocksDb(targetPath, { disableWAL: false });
|
|
515
532
|
// Every handle opened on targetPath. All must be closed before returning so the caller can
|
|
516
533
|
// atomically rename a staging directory into place — rocksdb-js registers descriptors by
|
|
@@ -587,10 +604,10 @@ export async function copyDbToRocks(sourceRootStore, sourceDatabase: string, tar
|
|
|
587
604
|
let observerEncoder: any;
|
|
588
605
|
let canonicalStructures: any;
|
|
589
606
|
if (!isPrimary) {
|
|
590
|
-
targetDbi = openRocksDb(targetPath, { dupSort: true, name: key });
|
|
607
|
+
targetDbi = openRocksDb(targetPath, { disableWAL: disableDataWAL, dupSort: true, name: key });
|
|
591
608
|
targetHandles.push(targetDbi);
|
|
592
609
|
} else {
|
|
593
|
-
targetDbi = openRocksDb(targetPath, { name: key });
|
|
610
|
+
targetDbi = openRocksDb(targetPath, { disableWAL: disableDataWAL, name: key });
|
|
594
611
|
targetHandles.push(targetDbi);
|
|
595
612
|
// Patch the existing encoder (encoder is a getter-only property on RocksDatabase, cannot be replaced)
|
|
596
613
|
// to install RecordEncoder's encode method so metadata headers (timestamps, HAS_BLOBS flag) are written
|
package/bin/harper.ts
CHANGED
|
@@ -5,6 +5,7 @@ import * as fs from 'node:fs';
|
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import logger from '../utility/logging/harper_logger.ts';
|
|
7
7
|
import * as cliOperations from './cliOperations.ts';
|
|
8
|
+
import { help } from './help.ts';
|
|
8
9
|
import { packageJson } from '../utility/packageUtils.js';
|
|
9
10
|
import checkNode from '../launchServiceScripts/utility/checkNodeVersion.js';
|
|
10
11
|
import * as hdbTerms from '../utility/hdbTerms.ts';
|
|
@@ -13,55 +14,6 @@ if (typeof process.setSourceMapsEnabled === 'function') {
|
|
|
13
14
|
process.setSourceMapsEnabled(true); // this is necessary for source maps to work, at least on the main thread.
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
const HELP = `
|
|
17
|
-
Usage: harperdb [command]
|
|
18
|
-
|
|
19
|
-
With no command, harper will simply run Harper (in the foreground)
|
|
20
|
-
|
|
21
|
-
Documentation: https://docs.harperdb.io/
|
|
22
|
-
|
|
23
|
-
By default, the CLI also supports certain Operation APIs. Specify the operation name and any required parameters, and omit the 'operation' command.
|
|
24
|
-
|
|
25
|
-
Commands:
|
|
26
|
-
agent [message] - Chat with the built-in agent (interactive, or one-shot with a message; alias: chat)
|
|
27
|
-
copy-db <source> <target> - Copies a database from source path to target path
|
|
28
|
-
dev <path> - Run the application in dev mode with debugging, foreground logging, no auth
|
|
29
|
-
install - Install harperdb
|
|
30
|
-
<api-operation> <param>=<value> - Run an API operation and return result to the CLI, not all operations are supported
|
|
31
|
-
To authenticate as a different user than the one being operated on
|
|
32
|
-
(e.g. add_user/alter_user), set HARPER_CLI_USERNAME/HARPER_CLI_PASSWORD
|
|
33
|
-
or run 'harper login'. The equivalent auth_username=<value>
|
|
34
|
-
auth_password=<value> args also work, but a password passed as an
|
|
35
|
-
argument is exposed in shell history, process listings and CI logs.
|
|
36
|
-
A saved login token always outranks username=/password=, so a
|
|
37
|
-
stale token that fails to refresh will 401 rather than falling
|
|
38
|
-
back to them — run 'harper logout' or pass auth_username=/
|
|
39
|
-
auth_password= to override it.
|
|
40
|
-
login [target] [username] - Login to a remote or local Harper instance
|
|
41
|
-
--for-ci prints the CI/CD credentials (target + long-lived
|
|
42
|
-
refresh token) to stdout in dotenv format, and everything else
|
|
43
|
-
to stderr, so it pipes without the token hitting your screen:
|
|
44
|
-
harper login --for-ci | gh secret set --env-file -
|
|
45
|
-
Log in as a user dedicated to that one CI consumer: Harper
|
|
46
|
-
stores a single refresh token per user, so this revokes any
|
|
47
|
-
refresh token that user already holds — another runner, another
|
|
48
|
-
machine, or an earlier 'harper login' will 401 on its next
|
|
49
|
-
refresh. Two consumers cannot share a user.
|
|
50
|
-
logout [target] - Logout from Harper and clear saved JWT
|
|
51
|
-
mcp [subcommand] - MCP stdio bridge / print-config / doctor (see 'harper mcp help')
|
|
52
|
-
register - Register harperdb
|
|
53
|
-
renew-certs - Generate a new set of self-signed certificates
|
|
54
|
-
restart - Restart the harperdb background process
|
|
55
|
-
run <path> - Run the application in the specified path
|
|
56
|
-
start - Starts a separate background process for harperdb and CLI will exit
|
|
57
|
-
status - Print the status of Harper
|
|
58
|
-
stop - Stop the harperdb background process
|
|
59
|
-
help - Display this output
|
|
60
|
-
upgrade - Upgrade harperdb
|
|
61
|
-
version - Print the version
|
|
62
|
-
deploy - Deploy the application locally or remotely with target=<remote url>
|
|
63
|
-
`;
|
|
64
|
-
|
|
65
17
|
/**
|
|
66
18
|
* Format a CLI error for the terminal. Expected, user-facing errors (a `ClientError` from an
|
|
67
19
|
* operation — bad args, not found, a locked backup repo — which carry a numeric `statusCode`) get
|
|
@@ -74,6 +26,19 @@ export function formatCliError(error: any): string {
|
|
|
74
26
|
return message;
|
|
75
27
|
}
|
|
76
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Whether a `-h`/`--help` anywhere in `argv` should print the top-level help. Returns false for the
|
|
31
|
+
* subcommands that own their own `--help` — mcp and agent/chat parse `process.argv.slice(3)`
|
|
32
|
+
* themselves downstream — so `harper mcp --help` reaches the mcp handler instead of this help.
|
|
33
|
+
*/
|
|
34
|
+
export function wantsTopLevelHelp(argv: readonly string[], service: string | undefined): boolean {
|
|
35
|
+
const delegatesHelp =
|
|
36
|
+
service === SERVICE_ACTIONS_ENUM.MCP ||
|
|
37
|
+
service === SERVICE_ACTIONS_ENUM.AGENT ||
|
|
38
|
+
service === SERVICE_ACTIONS_ENUM.CHAT;
|
|
39
|
+
return !delegatesHelp && (argv.includes('-h') || argv.includes('--help'));
|
|
40
|
+
}
|
|
41
|
+
|
|
77
42
|
async function harper() {
|
|
78
43
|
let nodeResults = checkNode();
|
|
79
44
|
|
|
@@ -90,13 +55,17 @@ async function harper() {
|
|
|
90
55
|
|
|
91
56
|
let service;
|
|
92
57
|
|
|
93
|
-
if (process.argv
|
|
58
|
+
if (process.argv?.[2] && !process.argv[2].startsWith('-')) {
|
|
94
59
|
service = process.argv[2].toLowerCase();
|
|
95
60
|
}
|
|
96
61
|
|
|
62
|
+
if (wantsTopLevelHelp(process.argv, service)) {
|
|
63
|
+
return help();
|
|
64
|
+
}
|
|
65
|
+
|
|
97
66
|
switch (service) {
|
|
98
67
|
case SERVICE_ACTIONS_ENUM.HELP:
|
|
99
|
-
return
|
|
68
|
+
return help();
|
|
100
69
|
case SERVICE_ACTIONS_ENUM.START:
|
|
101
70
|
return require('./run').launch();
|
|
102
71
|
case SERVICE_ACTIONS_ENUM.INSTALL:
|
package/bin/help.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `harper help` / `harper -h` output, kept as structured data so {@link help} can render it to
|
|
3
|
+
* the current terminal width (capped at 120 columns) at call time. Content lives separately from
|
|
4
|
+
* layout: editing the text can't break the wrapping, command names stay column-aligned across
|
|
5
|
+
* sections, and the CI/CD sample stays a verbatim, copy-pasteable line at any width.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const MAX_WIDTH = 120;
|
|
9
|
+
const INDENT = 2; // leading spaces for command names and detail blocks
|
|
10
|
+
const DASH = ' - '; // separator between a command name and its description
|
|
11
|
+
// Minimum description width for the two-column command layout. On terminals too narrow for it
|
|
12
|
+
// (width < descCol + this), command rows stack the name above an indented description instead of
|
|
13
|
+
// overflowing. Set to the widest word in any command description so two-column never overflows.
|
|
14
|
+
const MIN_DESC_WIDTH = 20;
|
|
15
|
+
|
|
16
|
+
/** A paragraph of prose, reflowed to the target width at `indent` spaces. */
|
|
17
|
+
interface TextBlock {
|
|
18
|
+
text: string;
|
|
19
|
+
indent?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A verbatim line (a code sample); never joined or wrapped so it stays copy-pasteable. */
|
|
23
|
+
interface PreBlock {
|
|
24
|
+
pre: string;
|
|
25
|
+
indent?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A two-column list of `[name, description]` rows; descriptions wrap with a hanging indent. */
|
|
29
|
+
interface CommandsBlock {
|
|
30
|
+
commands: [name: string, description: string][];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type Block = TextBlock | PreBlock | CommandsBlock;
|
|
34
|
+
|
|
35
|
+
interface Section {
|
|
36
|
+
heading?: string;
|
|
37
|
+
blocks: Block[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const SECTIONS: Section[] = [
|
|
41
|
+
{ blocks: [{ text: 'Usage: harperdb [command]' }] },
|
|
42
|
+
{ blocks: [{ text: 'With no command, harper will simply run Harper (in the foreground)' }] },
|
|
43
|
+
{ blocks: [{ text: 'Documentation: https://docs.harperdb.io/' }] },
|
|
44
|
+
{
|
|
45
|
+
blocks: [
|
|
46
|
+
{
|
|
47
|
+
text: [
|
|
48
|
+
'By default, the CLI also supports certain Operation APIs. Specify the operation name and any required',
|
|
49
|
+
"parameters, and omit the 'operation' command.",
|
|
50
|
+
].join(' '),
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{ blocks: [{ text: 'Commands:' }] },
|
|
55
|
+
{
|
|
56
|
+
heading: 'Server',
|
|
57
|
+
blocks: [
|
|
58
|
+
{
|
|
59
|
+
commands: [
|
|
60
|
+
['start', 'Starts a separate background process for harperdb and CLI will exit'],
|
|
61
|
+
['stop', 'Stop the harperdb background process'],
|
|
62
|
+
['restart', 'Restart the harperdb background process'],
|
|
63
|
+
['status', 'Print the status of Harper'],
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
heading: 'Applications',
|
|
70
|
+
blocks: [
|
|
71
|
+
{
|
|
72
|
+
commands: [
|
|
73
|
+
['run <path>', 'Run the application in the specified path'],
|
|
74
|
+
['dev <path>', 'Run the application in dev mode with debugging, foreground logging, no auth'],
|
|
75
|
+
['deploy', 'Deploy the application locally or remotely with target=<remote url>'],
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
heading: 'Install & maintenance',
|
|
82
|
+
blocks: [
|
|
83
|
+
{
|
|
84
|
+
commands: [
|
|
85
|
+
['install', 'Install harperdb'],
|
|
86
|
+
['upgrade', 'Upgrade harperdb'],
|
|
87
|
+
['register', 'Register harperdb'],
|
|
88
|
+
['renew-certs', 'Generate a new set of self-signed certificates'],
|
|
89
|
+
['copy-db <source> <target>', 'Copies a database from source path to target path'],
|
|
90
|
+
['version', 'Print the version'],
|
|
91
|
+
['help', 'Display this output'],
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
heading: 'Accounts',
|
|
98
|
+
blocks: [
|
|
99
|
+
{ pre: 'login [target] [username]', indent: INDENT },
|
|
100
|
+
{
|
|
101
|
+
text: [
|
|
102
|
+
'Login to a remote or local Harper instance. --for-ci prints the CI/CD credentials (target +',
|
|
103
|
+
'long-lived refresh token) to stdout in dotenv format, and everything else to stderr, so it pipes',
|
|
104
|
+
'without the token hitting your screen:',
|
|
105
|
+
].join(' '),
|
|
106
|
+
indent: 4,
|
|
107
|
+
},
|
|
108
|
+
{ pre: 'harper login --for-ci | gh secret set --env-file -', indent: 6 },
|
|
109
|
+
{
|
|
110
|
+
text: [
|
|
111
|
+
'Log in as a user dedicated to that one CI consumer: Harper stores a single refresh token per user, so',
|
|
112
|
+
'this revokes any refresh token that user already holds — another runner, another machine, or an earlier',
|
|
113
|
+
"'harper login' will 401 on its next refresh. Two consumers cannot share a user.",
|
|
114
|
+
].join(' '),
|
|
115
|
+
indent: 4,
|
|
116
|
+
},
|
|
117
|
+
{ commands: [['logout [target]', 'Logout from Harper and clear saved JWT']] },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
heading: 'Assistants',
|
|
122
|
+
blocks: [
|
|
123
|
+
{
|
|
124
|
+
commands: [
|
|
125
|
+
['agent [message]', 'Chat with the built-in agent (interactive, or one-shot with a message; alias: chat)'],
|
|
126
|
+
['mcp [subcommand]', "MCP stdio bridge / print-config / doctor (see 'harper mcp help')"],
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
heading: 'Operations API',
|
|
133
|
+
blocks: [
|
|
134
|
+
{ pre: '<api-operation> <param>=<value>', indent: INDENT },
|
|
135
|
+
{
|
|
136
|
+
text: [
|
|
137
|
+
'Run an API operation and return the result to the CLI (not all operations are supported). See the full',
|
|
138
|
+
'list of operations at: https://docs.harperdb.io/reference/v5/operations-api/operations',
|
|
139
|
+
].join(' '),
|
|
140
|
+
indent: 4,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
text: [
|
|
144
|
+
'To authenticate as a different user than the one being operated on (e.g. add_user/alter_user), set',
|
|
145
|
+
"HARPER_CLI_USERNAME/HARPER_CLI_PASSWORD or run 'harper login'. The equivalent auth_username=<value>",
|
|
146
|
+
'auth_password=<value> args also work, but a password passed as an argument is exposed in shell',
|
|
147
|
+
'history, process listings and CI logs. A saved login token always outranks username=/password=, so a',
|
|
148
|
+
"stale token that fails to refresh will 401 rather than falling back to them — run 'harper logout' or",
|
|
149
|
+
'pass auth_username=/auth_password= to override it.',
|
|
150
|
+
].join(' '),
|
|
151
|
+
indent: 4,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
const pad = (count: number): string => ' '.repeat(count);
|
|
158
|
+
|
|
159
|
+
/** Greedy word wrap. Never splits a token, so URLs and flags survive intact (they overflow). */
|
|
160
|
+
function wrap(text: string, width: number): string[] {
|
|
161
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
162
|
+
if (words.length === 0) return [];
|
|
163
|
+
const lines: string[] = [];
|
|
164
|
+
let line = words[0];
|
|
165
|
+
for (let i = 1; i < words.length; i++) {
|
|
166
|
+
if (line.length + 1 + words[i].length > width) {
|
|
167
|
+
lines.push(line);
|
|
168
|
+
line = words[i];
|
|
169
|
+
} else {
|
|
170
|
+
line += ' ' + words[i];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
lines.push(line);
|
|
174
|
+
return lines;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function renderBlock(block: Block, width: number, nameWidth: number, descCol: number): string[] {
|
|
178
|
+
if ('commands' in block) {
|
|
179
|
+
// Two columns when the terminal leaves a readable description column; otherwise stack the name
|
|
180
|
+
// above an indented description so narrow terminals don't overflow the command rows.
|
|
181
|
+
const twoColumn = width - descCol >= MIN_DESC_WIDTH;
|
|
182
|
+
return block.commands.flatMap(([name, description]) => {
|
|
183
|
+
if (twoColumn) {
|
|
184
|
+
const wrapped = wrap(description, width - descCol);
|
|
185
|
+
const head = pad(INDENT) + name.padEnd(nameWidth) + DASH;
|
|
186
|
+
return [head + (wrapped[0] ?? ''), ...wrapped.slice(1).map((line) => pad(descCol) + line)];
|
|
187
|
+
}
|
|
188
|
+
const detail = INDENT * 2;
|
|
189
|
+
return [pad(INDENT) + name, ...wrap(description, Math.max(1, width - detail)).map((line) => pad(detail) + line)];
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if ('pre' in block) return [pad(block.indent ?? 0) + block.pre];
|
|
193
|
+
const indent = block.indent ?? 0;
|
|
194
|
+
return wrap(block.text, Math.max(1, width - indent)).map((line) => pad(indent) + line);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Render the CLI help, wrapped to the terminal width (capped at 120, and 120 when not a TTY). */
|
|
198
|
+
export function help(): string {
|
|
199
|
+
const width = Math.min(process.stdout.columns || MAX_WIDTH, MAX_WIDTH);
|
|
200
|
+
|
|
201
|
+
// One shared description column across every command list so names line up between sections,
|
|
202
|
+
// driven by the longest command name.
|
|
203
|
+
const names = SECTIONS.flatMap((section) =>
|
|
204
|
+
section.blocks.flatMap((block) => ('commands' in block ? block.commands.map(([name]) => name) : []))
|
|
205
|
+
);
|
|
206
|
+
const nameWidth = Math.max(...names.map((name) => name.length));
|
|
207
|
+
const descCol = INDENT + nameWidth + DASH.length;
|
|
208
|
+
|
|
209
|
+
const lines: string[] = [];
|
|
210
|
+
for (const section of SECTIONS) {
|
|
211
|
+
if (lines.length) lines.push(''); // blank line between sections
|
|
212
|
+
if (section.heading) lines.push(section.heading);
|
|
213
|
+
for (const block of section.blocks) lines.push(...renderBlock(block, width, nameWidth, descCol));
|
|
214
|
+
}
|
|
215
|
+
return '\n' + lines.join('\n') + '\n';
|
|
216
|
+
}
|