@harperfast/harper 5.2.0 → 5.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/copyDb.ts +40 -11
- 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/deploymentRecorder.ts +102 -11
- package/components/mcp/tools/operations.ts +6 -0
- package/components/operations.js +8 -6
- package/config/configUtils.ts +78 -20
- package/config/harperConfigEnvVars.ts +114 -17
- package/config-root.schema.json +10 -0
- package/dataLayer/harperBridge/ResourceBridge.ts +26 -5
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
- package/dataLayer/hdbInfoController.ts +8 -0
- package/dataLayer/schemaDescribe.ts +2 -1
- package/dist/bin/copyDb.js +33 -9
- 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/deploymentRecorder.d.ts +6 -0
- package/dist/components/deploymentRecorder.js +94 -12
- package/dist/components/deploymentRecorder.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 +8 -6
- package/dist/components/operations.js.map +1 -1
- package/dist/config/configUtils.d.ts +6 -0
- package/dist/config/configUtils.js +78 -18
- package/dist/config/configUtils.js.map +1 -1
- package/dist/config/harperConfigEnvVars.js +108 -16
- package/dist/config/harperConfigEnvVars.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +12 -5
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.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 +57 -0
- package/dist/resources/DatabaseTransaction.js +284 -104
- 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 +8 -6
- package/dist/resources/Table.js +158 -31
- 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/auditStore.js +27 -1
- package/dist/resources/auditStore.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/indexes/HierarchicalNavigableSmallWorld.d.ts +15 -2
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +128 -39
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.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/resources/search.d.ts +1 -1
- package/dist/resources/search.js +7 -3
- package/dist/resources/search.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/manageThreads.js +8 -0
- package/dist/server/threads/manageThreads.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/sqlEngine/parser/normalizer.js +37 -9
- package/dist/sqlEngine/parser/normalizer.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/environment/environmentManager.d.ts +11 -0
- package/dist/utility/environment/environmentManager.js +154 -0
- package/dist/utility/environment/environmentManager.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/lmdb/environmentUtility.js +2 -1
- package/dist/utility/lmdb/environmentUtility.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 +208 -198
- package/package.json +10 -8
- package/resources/DESIGN.md +1 -0
- package/resources/DatabaseTransaction.ts +313 -102
- package/resources/ResourceInterface.ts +8 -2
- package/resources/Resources.ts +22 -4
- package/resources/Table.ts +398 -259
- package/resources/analytics/write.ts +22 -20
- package/resources/auditStore.ts +32 -1
- package/resources/blob.ts +15 -8
- package/resources/databases.ts +123 -12
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +119 -45
- 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/resources/search.ts +8 -3
- 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/manageThreads.js +8 -0
- 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-BMUjPklt.js} +5 -5
- package/studio/web/assets/{Chat-DoVWScmq.js.map → Chat-BMUjPklt.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js → FloatingChat-Cbghuxe5.js} +5 -5
- package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js.map → FloatingChat-Cbghuxe5.js.map} +1 -1
- package/studio/web/assets/{apiToken-BUI_04o7.js → apiToken-CkgTQAqc.js} +2 -2
- package/studio/web/assets/{apiToken-BUI_04o7.js.map → apiToken-CkgTQAqc.js.map} +1 -1
- package/studio/web/assets/{applications-D03NA7wW.js → applications-WvF4NOTN.js} +13 -13
- package/studio/web/assets/applications-WvF4NOTN.js.map +1 -0
- package/studio/web/assets/chevron-right-anDJCGlp.js +2 -0
- package/studio/web/assets/chevron-right-anDJCGlp.js.map +1 -0
- package/studio/web/assets/index-CDeqVx71.css +1 -0
- package/studio/web/assets/index-Sevt3MVx.js +819 -0
- package/studio/web/assets/index-Sevt3MVx.js.map +1 -0
- package/studio/web/assets/index.lazy-Dg5YYPGg.js +14 -0
- package/studio/web/assets/{index.lazy-Dx3MpyDC.js.map → index.lazy-Dg5YYPGg.js.map} +1 -1
- package/studio/web/assets/{jsonMode-Cjj7dtlr.js → jsonMode-CBSEleE7.js} +2 -2
- package/studio/web/assets/{jsonMode-Cjj7dtlr.js.map → jsonMode-CBSEleE7.js.map} +1 -1
- package/studio/web/assets/languageServices-gxedeeTo.js +2 -0
- package/studio/web/assets/languageServices-gxedeeTo.js.map +1 -0
- package/studio/web/assets/{notifications-0edoFTsb.js → notifications-Ap84YyQp.js} +2 -2
- package/studio/web/assets/{notifications-0edoFTsb.js.map → notifications-Ap84YyQp.js.map} +1 -1
- package/studio/web/assets/notifications-vehqyOLM.js +2 -0
- package/studio/web/assets/{notifications-CwKhipK7.js.map → notifications-vehqyOLM.js.map} +1 -1
- package/studio/web/assets/{pollUnlessForbidden-HHdQZW1N.js → pollUnlessForbidden-BhzefVRn.js} +2 -2
- package/studio/web/assets/pollUnlessForbidden-BhzefVRn.js.map +1 -0
- package/studio/web/assets/{profile-DUfEPQtx.js → profile-DzDpRLHY.js} +2 -2
- package/studio/web/assets/{profile-DUfEPQtx.js.map → profile-DzDpRLHY.js.map} +1 -1
- package/studio/web/assets/queryClient-CgtkoiCY.js +2 -0
- package/studio/web/assets/queryClient-CgtkoiCY.js.map +1 -0
- package/studio/web/assets/regions-DpJ8s7iN.js +2 -0
- package/studio/web/assets/regions-DpJ8s7iN.js.map +1 -0
- package/studio/web/assets/register-BDy01-q3.js +3 -0
- package/studio/web/assets/register-BDy01-q3.js.map +1 -0
- package/studio/web/assets/register-CQHRwNP4.js +3 -0
- package/studio/web/assets/register-CQHRwNP4.js.map +1 -0
- package/studio/web/assets/{setComponentFile-DMPo4UjC.js → setComponentFile-D0aPcI3W.js} +2 -2
- package/studio/web/assets/{setComponentFile-DMPo4UjC.js.map → setComponentFile-D0aPcI3W.js.map} +1 -1
- package/studio/web/assets/setup-I15FAF9H.js +3 -0
- package/studio/web/assets/{setup-B56Oz1_u.js.map → setup-I15FAF9H.js.map} +1 -1
- package/studio/web/assets/status-BF1eJshJ.js +62 -0
- package/studio/web/assets/status-BF1eJshJ.js.map +1 -0
- package/studio/web/assets/{swagger-ui-react-lQrBxfwM.js → swagger-ui-react-DwSPXXhl.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-lQrBxfwM.js.map → swagger-ui-react-DwSPXXhl.js.map} +1 -1
- package/studio/web/assets/{textarea-DhLMZ3PA.js → table-DpOjNQHt.js} +2 -10
- package/studio/web/assets/table-DpOjNQHt.js.map +1 -0
- package/studio/web/assets/{tsMode-CrHCRjTK.js → tsMode-Bu51A-VG.js} +2 -2
- package/studio/web/assets/{tsMode-CrHCRjTK.js.map → tsMode-Bu51A-VG.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js → useEntityRestURL-DrV1zMrN.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js.map → useEntityRestURL-DrV1zMrN.js.map} +1 -1
- package/studio/web/index.html +6 -7
- package/upgrade/upgradePrompt.ts +22 -3
- package/utility/environment/environmentManager.ts +152 -0
- package/utility/errors/commonErrors.ts +2 -0
- package/utility/hdbTerms.ts +2 -0
- package/utility/install/installer.ts +37 -0
- package/utility/lmdb/environmentUtility.ts +2 -1
- package/utility/logging/harper_logger.ts +57 -26
- package/studio/web/assets/applications-D03NA7wW.js.map +0 -1
- package/studio/web/assets/index-Bh_CNAHr.js +0 -809
- package/studio/web/assets/index-Bh_CNAHr.js.map +0 -1
- package/studio/web/assets/index-C8Mv9kHV.css +0 -1
- package/studio/web/assets/index.lazy-Dx3MpyDC.js +0 -14
- package/studio/web/assets/notifications-CwKhipK7.js +0 -2
- package/studio/web/assets/pollUnlessForbidden-HHdQZW1N.js.map +0 -1
- package/studio/web/assets/queryClient-CbA8wM7J.js +0 -2
- package/studio/web/assets/queryClient-CbA8wM7J.js.map +0 -1
- package/studio/web/assets/setup-B56Oz1_u.js +0 -3
- package/studio/web/assets/status-BAod7p3o.js +0 -62
- package/studio/web/assets/status-BAod7p3o.js.map +0 -1
- package/studio/web/assets/textarea-DhLMZ3PA.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
1
|
+
import { readFile, realpath } from 'node:fs/promises';
|
|
2
2
|
import { statfs } from 'node:fs/promises';
|
|
3
|
-
import { join } from 'node:path';
|
|
3
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
4
|
import { getWorkerIndex, getWorkerCount } from '../server/threads/manageThreads.js';
|
|
5
5
|
import { logger } from '../utility/logging/logger.ts';
|
|
6
6
|
import { CONFIG_PARAMS } from '../utility/hdbTerms.ts';
|
|
@@ -27,6 +27,56 @@ export type QuotaStatusData = {
|
|
|
27
27
|
updatedAt: number;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Resolves `target` to its real (symlink-free) path. `target` itself may not exist yet (e.g. a
|
|
32
|
+
* table's storage directory that hasn't been created), so walk up to the nearest existing
|
|
33
|
+
* ancestor, resolve that, and rejoin the not-yet-existing remainder lexically — this still
|
|
34
|
+
* catches a symlinked *parent* directory, which is the case that actually matters for quota
|
|
35
|
+
* scoping (leaf directories are created by Harper itself, not symlinked).
|
|
36
|
+
*/
|
|
37
|
+
async function resolveRealPath(target: string): Promise<string> {
|
|
38
|
+
try {
|
|
39
|
+
return await realpath(target);
|
|
40
|
+
} catch {
|
|
41
|
+
const parent = dirname(target);
|
|
42
|
+
if (parent === target) return resolve(target);
|
|
43
|
+
return join(await resolveRealPath(parent), basename(target));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether `target` resolves to a path inside `root`. quota-status.json is scoped to a single
|
|
49
|
+
* ROOTPATH; a database or table configured with its own STORAGE_PATH (or a STORAGE_BLOBPATHS
|
|
50
|
+
* entry) can live on an entirely different volume, and must not be reported against the root's
|
|
51
|
+
* quota. Compares real (symlink-resolved) paths, not just lexically-normalized ones — a `..`-free
|
|
52
|
+
* path can still be a symlink into another volume, and `resolve()` alone wouldn't catch that.
|
|
53
|
+
*/
|
|
54
|
+
async function isPathWithinRoot(root: string, target: string): Promise<boolean> {
|
|
55
|
+
const [resolvedRoot, resolvedTarget] = await Promise.all([resolveRealPath(root), resolveRealPath(target)]);
|
|
56
|
+
const relativePath = relative(resolvedRoot, resolvedTarget);
|
|
57
|
+
if (relativePath === '') return true;
|
|
58
|
+
if (isAbsolute(relativePath)) return false;
|
|
59
|
+
// Check the first segment specifically (`..`), not a `..`-prefix match — a child directory
|
|
60
|
+
// literally named e.g. `..data` (the Kubernetes ConfigMap/Secret atomic-update symlink
|
|
61
|
+
// convention) is not a parent-traversal and must not be rejected as escaping the root.
|
|
62
|
+
const firstSegment = relativePath.split(sep)[0];
|
|
63
|
+
return firstSegment !== '..';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Guards against a syntactically valid but semantically broken quota-status.json (missing or
|
|
68
|
+
* negative usage, non-positive quota, non-finite timestamp) being read as "100% free" or worse.
|
|
69
|
+
*/
|
|
70
|
+
function isValidQuotaStatus(status: QuotaStatusData): boolean {
|
|
71
|
+
return (
|
|
72
|
+
Number.isFinite(status.updatedAt) &&
|
|
73
|
+
Number.isFinite(status.quotaBytes) &&
|
|
74
|
+
status.quotaBytes > 0 &&
|
|
75
|
+
Number.isFinite(status.usedBytes) &&
|
|
76
|
+
status.usedBytes >= 0
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
30
80
|
/**
|
|
31
81
|
* Reads the quota-status.json file written by host-manager.
|
|
32
82
|
* Returns undefined if the file is absent or malformed; does not apply age filtering.
|
|
@@ -70,15 +120,61 @@ export function onStorageReclamation(
|
|
|
70
120
|
}
|
|
71
121
|
let reclamationTimer: NodeJS.Timeout;
|
|
72
122
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
123
|
+
export type StorageSpaceStats = {
|
|
124
|
+
available: number;
|
|
125
|
+
free: number;
|
|
126
|
+
size: number;
|
|
127
|
+
// Whether these numbers reflect a quota (accurate for what Harper can actually write) or
|
|
128
|
+
// raw filesystem-level statfs (can be wildly wrong under a quota-limited data directory).
|
|
129
|
+
basis: 'quota' | 'filesystem';
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get available/free/size storage stats for a path, preferring a fresh quota-status.json (see
|
|
134
|
+
* getQuotaStatus) over statfs. statfs describes the filesystem, which is misleading whenever
|
|
135
|
+
* the data directory is quota-limited on a larger shared volume (#1976).
|
|
136
|
+
*/
|
|
137
|
+
export async function getStorageSpaceStats(path: string): Promise<StorageSpaceStats> {
|
|
138
|
+
const rootPath = envMgr.get(CONFIG_PARAMS.ROOTPATH);
|
|
76
139
|
const status = await getQuotaStatus();
|
|
77
|
-
|
|
78
|
-
|
|
140
|
+
const statusAge = status ? Date.now() - status.updatedAt : undefined;
|
|
141
|
+
if (
|
|
142
|
+
status &&
|
|
143
|
+
isValidQuotaStatus(status) &&
|
|
144
|
+
rootPath &&
|
|
145
|
+
// A negative age (clock skew, or a corrupt/future updatedAt) is not "fresh" — treat it the
|
|
146
|
+
// same as stale rather than trusting quota data indefinitely.
|
|
147
|
+
statusAge >= 0 &&
|
|
148
|
+
statusAge < QUOTA_STATUS_MAX_AGE_MS &&
|
|
149
|
+
// Checked last: it's the only async/IO-bound condition, so short-circuit it behind the
|
|
150
|
+
// cheap synchronous checks above.
|
|
151
|
+
(await isPathWithinRoot(rootPath, path))
|
|
152
|
+
) {
|
|
153
|
+
const available = Math.max(0, status.quotaBytes - status.usedBytes);
|
|
154
|
+
return { available, free: available, size: status.quotaBytes, basis: 'quota' };
|
|
79
155
|
}
|
|
80
156
|
const fsStats = await statfs(path);
|
|
81
|
-
return
|
|
157
|
+
return {
|
|
158
|
+
available: fsStats.bavail * fsStats.bsize,
|
|
159
|
+
free: fsStats.bfree * fsStats.bsize,
|
|
160
|
+
size: fsStats.blocks * fsStats.bsize,
|
|
161
|
+
basis: 'filesystem',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Ratio of available to total size, guarding the 0/0 = NaN case (a zero-size statfs/quota result,
|
|
167
|
+
* e.g. an empty or misconfigured mount) so a full disk still reports a ratio of 0 rather than NaN.
|
|
168
|
+
*/
|
|
169
|
+
export function computeAvailableRatio(available: number, size: number): number {
|
|
170
|
+
return size > 0 ? available / size : 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// If a fresh quota-status.json exists (written by host-manager every ~90s), use quota-based ratio.
|
|
174
|
+
// Otherwise fall back to statfs for the registered path.
|
|
175
|
+
const defaultGetAvailableSpaceRatio = async (path: string): Promise<number> => {
|
|
176
|
+
const { available, size } = await getStorageSpaceStats(path);
|
|
177
|
+
return computeAvailableRatio(available, size);
|
|
82
178
|
};
|
|
83
179
|
let getAvailableSpaceRatio: (path: string) => Promise<number> = defaultGetAvailableSpaceRatio;
|
|
84
180
|
|
|
@@ -232,6 +232,14 @@ function registerWorkerDataProvider(name, provider) {
|
|
|
232
232
|
if (workerDataProviders.get(name) === provider) workerDataProviders.delete(name);
|
|
233
233
|
};
|
|
234
234
|
}
|
|
235
|
+
// Propagate this thread's in-process config overrides (env.setProperty — installer bootstrap or
|
|
236
|
+
// the unit-test harness's per-run isolation) to every worker spawned from here, so a worker's
|
|
237
|
+
// effective config is never silently whatever happens to be installed on disk. Worker-side replay
|
|
238
|
+
// is environmentManager.ts's applyInheritedConfigOverrides(), invoked from initSync(). Registered
|
|
239
|
+
// on every thread (not just main) so a nested worker-of-a-worker also inherits the full chain.
|
|
240
|
+
// setProperty() clones each value as it records it, so this provider cannot hit the log-and-skip
|
|
241
|
+
// path below — which for this one would mean spawning the worker on the on-disk config.
|
|
242
|
+
registerWorkerDataProvider('configOverrides', () => envMgr.getConfigOverrides());
|
|
235
243
|
function collectProvidedWorkerData(options) {
|
|
236
244
|
if (workerDataProviders.size === 0) return undefined;
|
|
237
245
|
let provided;
|
|
@@ -8,6 +8,12 @@ import { join } from 'path';
|
|
|
8
8
|
|
|
9
9
|
const workers = [];
|
|
10
10
|
const workersReady = [];
|
|
11
|
+
// startHTTPThreads() can be called more than once in-process (e.g. a test harness adding more
|
|
12
|
+
// worker threads to an already-running server via addThreads()). The crash-path sweep below must
|
|
13
|
+
// run only on the very first call — a later call happens after real mirrors are already bound, and
|
|
14
|
+
// sweeping the sockets directory then would delete their live files, reintroducing the exact outage
|
|
15
|
+
// this guards against.
|
|
16
|
+
let sweptSocketsDirectory = false;
|
|
11
17
|
|
|
12
18
|
if (isMainThread) {
|
|
13
19
|
process.on('uncaughtException', (error) => {
|
|
@@ -23,6 +29,20 @@ if (isMainThread) {
|
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
export async function startHTTPThreads(threadCount = 2, dynamicThreads?: boolean) {
|
|
32
|
+
// Crash-path defense: a hard crash can skip a worker's exit-time UDS cleanup and leave stale
|
|
33
|
+
// mirror files behind. This runs before any worker below can start (and thus before any mirror
|
|
34
|
+
// can bind), so it can only ever clear files nothing is using yet — never a live mirror. The
|
|
35
|
+
// inode ownership guard in cleanupUdsFiles()/markUdsBindFailed() (see http.ts) is the matching
|
|
36
|
+
// defense for the in-process rolling-restart case, which this sweep does not cover.
|
|
37
|
+
// Lazy dynamic import (not a top-level one), matching server/status/index.ts's own lazy import of
|
|
38
|
+
// http.ts: http.ts's module graph reaches security/auth.ts, whose module-scope table() call needs
|
|
39
|
+
// config already initialized — pulling that graph in at this file's own top level (which
|
|
40
|
+
// bin/run.ts imports before parsing argv / initializing config) breaks startup with "Unable to
|
|
41
|
+
// determine database storage path" before main() ever runs.
|
|
42
|
+
if (isMainThread && !sweptSocketsDirectory) {
|
|
43
|
+
sweptSocketsDirectory = true;
|
|
44
|
+
(await import('../http.ts')).cleanupSocketsDirectory();
|
|
45
|
+
}
|
|
26
46
|
recordHostname().catch((err) => harperLogger.error?.('Error recording hostname for analytics:', err));
|
|
27
47
|
// Drive transaction-log cooling from the main thread (the registry is a
|
|
28
48
|
// process-global singleton; see startTransactionLogCooling). Runs for all
|
|
@@ -275,6 +275,11 @@ function listenOnDomainSocket(port, server) {
|
|
|
275
275
|
}
|
|
276
276
|
function onListening() {
|
|
277
277
|
server.removeListener('error', onError);
|
|
278
|
+
// Record ownership of the inode we just bound, so cleanupUdsFiles()/markUdsBindFailed()
|
|
279
|
+
// (see http.ts) can tell this worker's own file apart from a replacement's later rebind
|
|
280
|
+
// at the same path (see registerUdsCleanupPaths). A no-op for domain sockets that aren't
|
|
281
|
+
// UDS mirrors (e.g. the operations API's primary socket), which were never registered.
|
|
282
|
+
httpComponent.recordUdsBindSuccess(port);
|
|
278
283
|
harperLogger.info('Domain socket listening on ' + port);
|
|
279
284
|
resolve({ port, name: server.name, protocol_name: server.protocol_name });
|
|
280
285
|
}
|
|
@@ -387,6 +392,9 @@ function listenOnPorts() {
|
|
|
387
392
|
callback?.();
|
|
388
393
|
},
|
|
389
394
|
};
|
|
395
|
+
// createUwsServer() only resolves once uWS's own listen_unix() confirms the bind, so
|
|
396
|
+
// ownership is confirmed now (see http.ts's ownership-aware cleanupUdsFiles()).
|
|
397
|
+
if (cfg.socketPath) httpComponent.recordUdsBindSuccess(cfg.socketPath);
|
|
390
398
|
harperLogger.info('uWS listening on ' + (cfg.socketPath ?? cfg.port));
|
|
391
399
|
return { port: key };
|
|
392
400
|
})
|
|
@@ -517,6 +525,9 @@ async function listenOnPortsBun() {
|
|
|
517
525
|
});
|
|
518
526
|
SERVERS[udsPath] = udsServer;
|
|
519
527
|
httpComponent.registerUdsCleanupPaths(udsPath, yamlPath);
|
|
528
|
+
// Bun.serve() above already succeeded (a bind failure throws synchronously into the
|
|
529
|
+
// surrounding catch), so this worker's ownership of udsPath is confirmed now.
|
|
530
|
+
httpComponent.recordUdsBindSuccess(udsPath);
|
|
520
531
|
|
|
521
532
|
const writeMetadata = () => httpComponent.writeUdsMetadata(yamlPath, port, config.pseudoServer);
|
|
522
533
|
config.tlsSelector.ready.then(writeMetadata);
|
package/sqlTranslator/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import * as terms from '../utility/hdbTerms.ts';
|
|
|
17
17
|
import { handleHDBError } from '../utility/errors/hdbError.ts';
|
|
18
18
|
import { HTTP_STATUS_CODES } from '../utility/errors/commonErrors.ts';
|
|
19
19
|
import * as sqlEngineRouter from '../sqlEngine/router.ts';
|
|
20
|
+
import { isOperationAuthorizationBypassed } from '../server/serverHelpers/operationAuthorizationState.ts';
|
|
20
21
|
|
|
21
22
|
//here we call to define and import custom functions to alasql
|
|
22
23
|
alasqlFunctionImporter(alasql);
|
|
@@ -129,7 +130,11 @@ export function processAST(jsonMessage: any, parsedSqlObject: any, callback: any
|
|
|
129
130
|
try {
|
|
130
131
|
let sqlFunction = nullFunction;
|
|
131
132
|
|
|
132
|
-
|
|
133
|
+
// Trusted bypass is dispatch/async-context state (set by chooseOperation's caller via
|
|
134
|
+
// runWithOperationAuthorizationBypass), never body state — jsonMessage.bypass_auth is
|
|
135
|
+
// caller-controlled and is stripped before operation code sees it (see
|
|
136
|
+
// server/serverHelpers/serverHandlers.js and components/mcp/tools/operations.ts).
|
|
137
|
+
if (!isOperationAuthorizationBypassed() && !parsedSqlObject.permissions_checked) {
|
|
133
138
|
let permissionsCheck = checkASTPermissions(jsonMessage, parsedSqlObject);
|
|
134
139
|
if (permissionsCheck && permissionsCheck.length > 0) {
|
|
135
140
|
return callback(UNAUTHORIZED_RESPONSE, permissionsCheck);
|