@harperfast/harper 5.3.0-beta.3 → 5.3.0-beta.4
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/cliOperations.ts +25 -3
- package/bin/run.ts +18 -0
- package/components/Application.ts +52 -78
- package/components/DESIGN.md +76 -3
- package/components/Scope.ts +10 -4
- package/components/anthropic/index.ts +10 -1
- package/components/bedrock/index.ts +10 -1
- package/components/componentLoader.ts +23 -14
- package/components/mcp/tools/operations.ts +3 -1
- package/components/ollama/index.ts +10 -1
- package/components/openai/index.ts +265 -18
- package/components/operations.js +40 -1
- package/config/configUtils.ts +3 -19
- package/dataLayer/DESIGN.md +29 -5
- package/dataLayer/harperBridge/ResourceBridge.ts +39 -3
- package/dataLayer/restoreMarker.ts +250 -14
- package/dataLayer/schema.ts +2 -7
- package/dataLayer/schemaDescribe.ts +10 -0
- package/dist/bin/cliOperations.js +28 -3
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/run.js +19 -0
- package/dist/bin/run.js.map +1 -1
- package/dist/components/Application.d.ts +8 -2
- package/dist/components/Application.js +43 -69
- package/dist/components/Application.js.map +1 -1
- package/dist/components/Scope.d.ts +1 -1
- package/dist/components/Scope.js +9 -4
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/anthropic/index.d.ts +0 -10
- package/dist/components/anthropic/index.js +9 -1
- package/dist/components/anthropic/index.js.map +1 -1
- package/dist/components/bedrock/index.d.ts +0 -12
- package/dist/components/bedrock/index.js +9 -1
- package/dist/components/bedrock/index.js.map +1 -1
- package/dist/components/componentLoader.js +23 -14
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/mcp/tools/operations.js +1 -1
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/ollama/index.d.ts +0 -13
- package/dist/components/ollama/index.js +9 -1
- package/dist/components/ollama/index.js.map +1 -1
- package/dist/components/openai/index.d.ts +11 -15
- package/dist/components/openai/index.js +224 -12
- package/dist/components/openai/index.js.map +1 -1
- package/dist/components/operations.d.ts +9 -0
- package/dist/components/operations.js +32 -2
- package/dist/components/operations.js.map +1 -1
- package/dist/config/configUtils.d.ts +1 -2
- package/dist/config/configUtils.js +7 -22
- package/dist/config/configUtils.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +29 -2
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/restoreMarker.d.ts +25 -3
- package/dist/dataLayer/restoreMarker.js +213 -13
- package/dist/dataLayer/restoreMarker.js.map +1 -1
- package/dist/dataLayer/schema.js +1 -4
- package/dist/dataLayer/schema.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +10 -0
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +28 -0
- package/dist/resources/DatabaseTransaction.d.ts +21 -1
- package/dist/resources/DatabaseTransaction.js +147 -8
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.js +1 -1
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/Table.d.ts +31 -3
- package/dist/resources/Table.js +397 -79
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/auditStore.js +16 -10
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/blob.d.ts +1 -0
- package/dist/resources/blob.js +14 -5
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/branchDatabase.js +3 -3
- package/dist/resources/branchDatabase.js.map +1 -1
- package/dist/resources/databaseDropPreparation.d.ts +20 -0
- package/dist/resources/databaseDropPreparation.js +106 -0
- package/dist/resources/databaseDropPreparation.js.map +1 -0
- package/dist/resources/databases.d.ts +26 -21
- package/dist/resources/databases.js +1040 -204
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/derivedIndexRegistry.d.ts +23 -0
- package/dist/resources/derivedIndexRegistry.js +92 -0
- package/dist/resources/derivedIndexRegistry.js.map +1 -1
- package/dist/resources/derivedIndexRuntime.d.ts +10 -0
- package/dist/resources/derivedIndexRuntime.js +136 -25
- package/dist/resources/derivedIndexRuntime.js.map +1 -1
- package/dist/resources/derivedIndexes.d.ts +39 -0
- package/dist/resources/derivedIndexes.js +733 -0
- package/dist/resources/derivedIndexes.js.map +1 -0
- package/dist/resources/fullTextSchema.d.ts +8 -0
- package/dist/resources/fullTextSchema.js +42 -0
- package/dist/resources/fullTextSchema.js.map +1 -1
- package/dist/resources/fullTextSchemaLifecycle.d.ts +0 -5
- package/dist/resources/fullTextSchemaLifecycle.js +0 -9
- package/dist/resources/fullTextSchemaLifecycle.js.map +1 -1
- package/dist/resources/graphql.js +101 -9
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/hnswDerivedIndex.d.ts +0 -10
- package/dist/resources/indexes/hnswDerivedIndex.js +0 -230
- package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -1
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.d.ts +3 -0
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js +57 -8
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js.map +1 -1
- package/dist/resources/models/Models.d.ts +26 -3
- package/dist/resources/models/Models.js +256 -13
- package/dist/resources/models/Models.js.map +1 -1
- package/dist/resources/models/TestBackend.d.ts +3 -1
- package/dist/resources/models/TestBackend.js +35 -1
- package/dist/resources/models/TestBackend.js.map +1 -1
- package/dist/resources/models/analyticsTable.d.ts +2 -2
- package/dist/resources/models/analyticsTable.js +4 -2
- package/dist/resources/models/analyticsTable.js.map +1 -1
- package/dist/resources/models/backendHelpers.d.ts +24 -0
- package/dist/resources/models/backendHelpers.js +18 -1
- package/dist/resources/models/backendHelpers.js.map +1 -1
- package/dist/resources/models/backendRegistry.d.ts +10 -21
- package/dist/resources/models/backendRegistry.js +74 -18
- package/dist/resources/models/backendRegistry.js.map +1 -1
- package/dist/resources/models/bootstrap.d.ts +7 -0
- package/dist/resources/models/bootstrap.js +36 -4
- package/dist/resources/models/bootstrap.js.map +1 -1
- package/dist/resources/models/decideHook.d.ts +33 -0
- package/dist/resources/models/decideHook.js +105 -0
- package/dist/resources/models/decideHook.js.map +1 -0
- package/dist/resources/models/decision.d.ts +64 -0
- package/dist/resources/models/decision.js +507 -0
- package/dist/resources/models/decision.js.map +1 -0
- package/dist/resources/models/decisionStore.d.ts +134 -0
- package/dist/resources/models/decisionStore.js +350 -0
- package/dist/resources/models/decisionStore.js.map +1 -0
- package/dist/resources/models/embedHook.d.ts +66 -8
- package/dist/resources/models/embedHook.js +228 -61
- package/dist/resources/models/embedHook.js.map +1 -1
- package/dist/resources/models/generativeDecision.d.ts +58 -0
- package/dist/resources/models/generativeDecision.js +315 -0
- package/dist/resources/models/generativeDecision.js.map +1 -0
- package/dist/resources/models/types.d.ts +232 -4
- package/dist/security/authn/oidc/tokenExchange.js +3 -33
- package/dist/security/authn/oidc/tokenExchange.js.map +1 -1
- package/dist/security/authn/oidc/tokenUseTable.d.ts +9 -0
- package/dist/security/authn/oidc/tokenUseTable.js +28 -0
- package/dist/security/authn/oidc/tokenUseTable.js.map +1 -0
- package/dist/security/certificateVerification/certificateVerificationSource.d.ts +0 -1
- package/dist/security/certificateVerification/certificateVerificationSource.js +3 -3
- package/dist/security/certificateVerification/certificateVerificationSource.js.map +1 -1
- package/dist/security/certificateVerification/crlVerification.js +77 -165
- package/dist/security/certificateVerification/crlVerification.js.map +1 -1
- package/dist/security/certificateVerification/types.d.ts +0 -3
- package/dist/security/certificateVerification/verificationTables.d.ts +19 -0
- package/dist/security/certificateVerification/verificationTables.js +125 -0
- package/dist/security/certificateVerification/verificationTables.js.map +1 -0
- package/dist/security/certificateVerification/verificationUtils.d.ts +1 -1
- package/dist/security/certificateVerification/verificationUtils.js +6 -31
- package/dist/security/certificateVerification/verificationUtils.js.map +1 -1
- package/dist/security/jsLoader.d.ts +5 -0
- package/dist/security/jsLoader.js +8 -0
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/server/Server.d.ts +1 -0
- package/dist/server/Server.js.map +1 -1
- package/dist/server/http.js +2 -2
- package/dist/server/http.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +21 -2
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/jobs/jobOwnership.d.ts +25 -0
- package/dist/server/jobs/jobOwnership.js +127 -0
- package/dist/server/jobs/jobOwnership.js.map +1 -0
- package/dist/server/jobs/jobProcess.js +9 -2
- package/dist/server/jobs/jobProcess.js.map +1 -1
- package/dist/server/jobs/jobs.js +18 -5
- package/dist/server/jobs/jobs.js.map +1 -1
- package/dist/server/loadRootComponents.js +10 -0
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.js +11 -1
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/serverHandlers.js +12 -2
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/threads/itc.d.ts +4 -1
- package/dist/server/threads/itc.js +29 -6
- package/dist/server/threads/itc.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +12 -4
- package/dist/server/threads/manageThreads.js +241 -119
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/threadServer.d.ts +1 -0
- package/dist/server/threads/threadServer.js +15 -1
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/server/threads/windowsProcessTree.d.ts +91 -0
- package/dist/server/threads/windowsProcessTree.js +361 -0
- package/dist/server/threads/windowsProcessTree.js.map +1 -0
- package/dist/server/throttle.d.ts +1 -1
- package/dist/server/throttle.js +8 -1
- package/dist/server/throttle.js.map +1 -1
- package/dist/upgrade/directives/5-3-0.js +53 -46
- package/dist/upgrade/directives/5-3-0.js.map +1 -1
- package/dist/utility/errors/hdbError.d.ts +10 -1
- package/dist/utility/errors/hdbError.js +55 -11
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/fsync.d.ts +2 -0
- package/dist/utility/fsync.js +20 -0
- package/dist/utility/fsync.js.map +1 -0
- package/dist/utility/globalSchema.d.ts +18 -0
- package/dist/utility/hdbTerms.d.ts +3 -0
- package/dist/utility/hdbTerms.js +3 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/signalling.d.ts +7 -1
- package/dist/utility/signalling.js +27 -3
- package/dist/utility/signalling.js.map +1 -1
- package/dist/utility/when.d.ts +1 -0
- package/dist/utility/when.js +21 -0
- package/dist/utility/when.js.map +1 -1
- package/dist/validation/configValidator.js +30 -3
- package/dist/validation/configValidator.js.map +1 -1
- package/index.ts +13 -0
- package/json/systemSchema.json +28 -0
- package/npm-shrinkwrap.json +151 -55
- package/package.json +14 -13
- package/resources/DESIGN.md +80 -100
- package/resources/DatabaseTransaction.ts +157 -10
- package/resources/PrimaryRocksDatabase.ts +2 -2
- package/resources/Table.ts +494 -101
- package/resources/auditStore.ts +18 -10
- package/resources/blob.ts +13 -6
- package/resources/branchDatabase.ts +3 -3
- package/resources/databaseDropPreparation.ts +120 -0
- package/resources/databases.ts +1155 -205
- package/resources/derivedIndexRegistry.ts +122 -0
- package/resources/derivedIndexRuntime.ts +157 -30
- package/resources/derivedIndexes.ts +856 -0
- package/resources/fullTextSchema.ts +55 -0
- package/resources/fullTextSchemaLifecycle.ts +0 -15
- package/resources/graphql.ts +107 -12
- package/resources/indexes/DESIGN.md +7 -0
- package/resources/indexes/hnswDerivedIndex.ts +0 -260
- package/resources/indexes/nativeFullTextDerivedIndexLifecycle.ts +72 -9
- package/resources/models/DESIGN.md +39 -0
- package/resources/models/Models.ts +361 -18
- package/resources/models/TestBackend.ts +56 -1
- package/resources/models/analyticsTable.ts +6 -4
- package/resources/models/backendHelpers.ts +17 -0
- package/resources/models/backendRegistry.ts +76 -21
- package/resources/models/bootstrap.ts +57 -7
- package/resources/models/decideHook.ts +135 -0
- package/resources/models/decision.ts +505 -0
- package/resources/models/decisionStore.ts +419 -0
- package/resources/models/embedHook.ts +272 -55
- package/resources/models/generativeDecision.ts +459 -0
- package/resources/models/types.ts +231 -4
- package/security/DESIGN.md +22 -2
- package/security/authn/oidc/tokenExchange.ts +6 -34
- package/security/authn/oidc/tokenUseTable.ts +27 -0
- package/security/certificateVerification/certificateVerificationSource.ts +2 -3
- package/security/certificateVerification/crlVerification.ts +96 -174
- package/security/certificateVerification/types.ts +0 -3
- package/security/certificateVerification/verificationTables.ts +124 -0
- package/security/certificateVerification/verificationUtils.ts +8 -32
- package/security/jsLoader.ts +9 -1
- package/server/DESIGN.md +27 -1
- package/server/Server.ts +2 -1
- package/server/http.ts +4 -2
- package/server/itc/serverHandlers.js +29 -1
- package/server/jobs/jobOwnership.ts +89 -0
- package/server/jobs/jobProcess.ts +8 -2
- package/server/jobs/jobs.ts +15 -4
- package/server/loadRootComponents.js +11 -0
- package/server/serverHelpers/contentTypes.ts +13 -1
- package/server/serverHelpers/serverHandlers.js +11 -2
- package/server/threads/itc.js +32 -6
- package/server/threads/manageThreads.js +268 -122
- package/server/threads/threadServer.js +13 -1
- package/server/threads/windowsProcessTree.ts +409 -0
- package/server/throttle.ts +11 -1
- package/studio/web/assets/{Chat-CveGbxe6.js → Chat-Bq60AO-g.js} +1 -1
- package/studio/web/assets/{FloatingChat-IpCFgYWL.js → FloatingChat-D4560T0p.js} +3 -3
- package/studio/web/assets/{apiToken-CS8fFTgL.js → apiToken-BEvbFPMc.js} +1 -1
- package/studio/web/assets/{applications-D7F4Jopl.js → applications-CSUVTH4E.js} +1 -1
- package/studio/web/assets/{cssMode-Daw_oLPq.js → cssMode-DJyc9mHj.js} +1 -1
- package/studio/web/assets/{editor-B3Bs1zWX.js → editor-DDIsQfj6.js} +1 -1
- package/studio/web/assets/{html-CDaQwWiE.js → html-g1BJHxLy.js} +1 -1
- package/studio/web/assets/{htmlMode-Cb6emO4U.js → htmlMode-Vh5IglCQ.js} +1 -1
- package/studio/web/assets/{index-B4g2Fu_2.js → index-DGRAzvO4.js} +5 -5
- package/studio/web/assets/{index.lazy-NwhvF1q1.js → index.lazy-vgSic7et.js} +1 -1
- package/studio/web/assets/{javascript-2_86gB9Q.js → javascript-5VIolqf7.js} +1 -1
- package/studio/web/assets/{jsonMode-DX-i86uQ.js → jsonMode-DG4zQraI.js} +1 -1
- package/studio/web/assets/{languageServices-B85WN-Ej.js → languageServices-t5FBpX-i.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-JCWHIsKg.js → lspLanguageFeatures-DgVnNPh-.js} +1 -1
- package/studio/web/assets/{notifications-D9ejzt52.js → notifications-BeWcX04m.js} +1 -1
- package/studio/web/assets/{notifications-B7hjKhai.js → notifications-DUGWrWoO.js} +1 -1
- package/studio/web/assets/{profile-D8B6No3F.js → profile-BvUEDBzW.js} +1 -1
- package/studio/web/assets/{regions-DFazIfTK.js → regions-CB-_2m7M.js} +1 -1
- package/studio/web/assets/{register-CwFLviLF.js → register-1w1Zr3b2.js} +2 -2
- package/studio/web/assets/{setComponentFile-VnJ80SSJ.js → setComponentFile-z4-qJCbA.js} +1 -1
- package/studio/web/assets/{setup-BYQxFy-C.js → setup-CS50uJ4x.js} +2 -2
- package/studio/web/assets/{status-Cn5XjmsL.js → status-CI0wOdOE.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-g9xIh6TX.js → toggleHighContrast-BsAE4jPe.js} +1 -1
- package/studio/web/assets/{tsMode-EC-n0Wqc.js → tsMode-D83Gg3cq.js} +1 -1
- package/studio/web/assets/{typescript-DHmM9JCO.js → typescript-Dfp-GrNq.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-BUdnVSVv.js → useEntityRestURL-BKetlINf.js} +1 -1
- package/studio/web/assets/{workers-BVl9bBB-.js → workers-CCvmDLH3.js} +1 -1
- package/studio/web/assets/{xml-C4_sqOkX.js → xml-06pZp00U.js} +1 -1
- package/studio/web/assets/{yaml-CVkbDqtW.js → yaml-iSnWMVlV.js} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/directives/5-3-0.ts +61 -54
- package/utility/DESIGN.md +5 -1
- package/utility/errors/hdbError.ts +56 -8
- package/utility/fsync.ts +16 -0
- package/utility/hdbTerms.ts +3 -0
- package/utility/signalling.ts +33 -4
- package/utility/when.ts +22 -0
- package/validation/configValidator.ts +36 -7
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
|
+
import * as harperLogger from '../../utility/logging/harper_logger.ts';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Confirms that a spawned Windows process tree is gone, identifying its members by lifetime
|
|
7
|
+
* rather than by PID alone.
|
|
8
|
+
*
|
|
9
|
+
* Windows recycles a freed PID almost immediately, and a process keeps its ParentProcessId after
|
|
10
|
+
* that parent exits. So once the direct child (the tree root) has exited, `ProcessId == rootPid`
|
|
11
|
+
* can be any newer process, and `ParentProcessId == rootPid` can be that newer process's children
|
|
12
|
+
* — a scan keyed on the PID alone reports the tree alive for as long as the recycled PID stays in
|
|
13
|
+
* use, and `taskkill /T` on that PID kills whatever now owns it (harper#2273). A real member's
|
|
14
|
+
* creation time is bounded by its parent's lifetime, which is what `selectWindowsProcessTree`
|
|
15
|
+
* checks: the root is only a member while it is the process we spawned, and a child of `X` was
|
|
16
|
+
* created after `X` was and before `X` exited.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export interface WindowsProcessRecord {
|
|
20
|
+
pid: number;
|
|
21
|
+
ppid: number;
|
|
22
|
+
/** Win32_Process.CreationDate as epoch ms; null when Windows reports none (system processes). */
|
|
23
|
+
created: number | null;
|
|
24
|
+
name?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface WindowsProcessTreeIdentity {
|
|
28
|
+
rootPid: number;
|
|
29
|
+
/** Epoch ms at which the root was already running: after spawn returned, or at group registration. */
|
|
30
|
+
rootKnownAt: number;
|
|
31
|
+
/**
|
|
32
|
+
* How long before `rootKnownAt` the root may have started: the interval measured around the
|
|
33
|
+
* `spawn` call, or `ROOT_SPAWN_ALLOWANCE_MS` when that was not measured. Bounds the children of
|
|
34
|
+
* a root whose own creation time was never observed.
|
|
35
|
+
*/
|
|
36
|
+
rootStartedWithinMs?: number;
|
|
37
|
+
/** The root's own creation time, once a scan has seen it running as ours; the exact child bound. */
|
|
38
|
+
rootCreatedAt?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Epoch ms after which the root was no longer running — Node observed its exit, a forced kill
|
|
41
|
+
* was confirmed, or a scan found its PID gone or recycled. Bounds its children's creation. Left
|
|
42
|
+
* undefined while it runs; `confirmWindowsProcessTreeGone` latches it from its own scans.
|
|
43
|
+
*/
|
|
44
|
+
rootExitedAt?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Every member below the root that a scan has found, by PID with its creation time, and the
|
|
47
|
+
* moment a later scan first found it gone. Kept so a member stays one after the parent that
|
|
48
|
+
* linked it to the root has exited, and so its own children stay bounded by its lifetime.
|
|
49
|
+
* `confirmWindowsProcessTreeGone` maintains it.
|
|
50
|
+
*/
|
|
51
|
+
descendants?: Map<number, WindowsTreeMemberRecord>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface WindowsTreeMemberRecord {
|
|
55
|
+
created: number;
|
|
56
|
+
exitedAt?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface WindowsProcessTreeWaitOptions {
|
|
60
|
+
scan?: () => Promise<WindowsProcessRecord[] | null>;
|
|
61
|
+
kill?: (members: WindowsProcessRecord[], rootPid: number) => Promise<void>;
|
|
62
|
+
now?: () => number;
|
|
63
|
+
warn?: (message: string) => void;
|
|
64
|
+
pollMs?: number;
|
|
65
|
+
maxPollMs?: number;
|
|
66
|
+
/** Names the tree in the unconfirmed-termination warning. */
|
|
67
|
+
label?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Date.now() and Win32_Process.CreationDate read the same system clock; this covers its resolution.
|
|
71
|
+
const CLOCK_SKEW_MS = 50;
|
|
72
|
+
// The root is created inside the `spawn` call, so the interval measured around that call is the
|
|
73
|
+
// exact bound on how much earlier than `rootKnownAt` it may have started. This is the fallback for a
|
|
74
|
+
// registration that carried no such interval: a stall between the two — an event loop pause, never
|
|
75
|
+
// a network hop.
|
|
76
|
+
export const ROOT_SPAWN_ALLOWANCE_MS = 1_000;
|
|
77
|
+
export const WINDOWS_TREE_POLL_MS = 25;
|
|
78
|
+
const WINDOWS_TREE_POLL_MAX_MS = 5_000;
|
|
79
|
+
const WINDOWS_TREE_WARNING_MS = 5_000;
|
|
80
|
+
const WINDOWS_TREE_WARNING_INTERVAL_MS = 60_000;
|
|
81
|
+
// Neither helper below is allowed to suspend the confirmation loop indefinitely. The loop itself is
|
|
82
|
+
// deliberately deadline-free (harper#2076: the preparation lock is never released on an unconfirmed
|
|
83
|
+
// tree), but that policy is about the DECISION, not about a single syscall: a `Get-CimInstance` or
|
|
84
|
+
// `taskkill` that never returns parks the loop before it can warn, so an operator sees a silent
|
|
85
|
+
// wedge instead of the 60 s "could not be queried" message the unknown state already produces.
|
|
86
|
+
// Bounding each invocation converts a hung helper into that same unknown, which keeps waiting.
|
|
87
|
+
const WINDOWS_TREE_HELPER_TIMEOUT_MS = 30_000;
|
|
88
|
+
|
|
89
|
+
// Exit 0 means the query ran and produced a table (possibly empty — an empty table is still a
|
|
90
|
+
// positive result, not "unknown"); exit 2 means the query itself failed (e.g. Get-CimInstance
|
|
91
|
+
// denied or WMI unavailable), which ErrorActionPreference=Stop plus the wrapping try/catch turns
|
|
92
|
+
// into that distinct code instead of a false-empty result, and `queryWindowsProcessTable` reads
|
|
93
|
+
// anything but 0 as "could not read the table" (null), never as "positively found nothing".
|
|
94
|
+
const PROCESS_TABLE_SCRIPT =
|
|
95
|
+
"$ErrorActionPreference = 'Stop'; try { " +
|
|
96
|
+
'$rows = @(Get-CimInstance Win32_Process | ForEach-Object { [pscustomobject]@{ ' +
|
|
97
|
+
'pid = [int]$_.ProcessId; ppid = [int]$_.ParentProcessId; name = [string]$_.Name; ' +
|
|
98
|
+
'created = if ($_.CreationDate) { [DateTimeOffset]::new($_.CreationDate).ToUnixTimeMilliseconds() } else { $null } } }); ' +
|
|
99
|
+
'[Console]::Out.Write((ConvertTo-Json -Compress -InputObject $rows)); exit 0 ' +
|
|
100
|
+
'} catch { exit 2 }';
|
|
101
|
+
|
|
102
|
+
export function queryWindowsProcessTable(
|
|
103
|
+
timeoutMs: number = WINDOWS_TREE_HELPER_TIMEOUT_MS
|
|
104
|
+
): Promise<WindowsProcessRecord[] | null> {
|
|
105
|
+
return new Promise((resolve) => {
|
|
106
|
+
const query = spawn(
|
|
107
|
+
'powershell.exe',
|
|
108
|
+
['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', PROCESS_TABLE_SCRIPT],
|
|
109
|
+
{
|
|
110
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
111
|
+
windowsHide: true,
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
let output = '';
|
|
115
|
+
// utf8 so the decoder buffers a multi-byte character split across a chunk boundary — a
|
|
116
|
+
// process name has arbitrary Unicode — instead of coercing each half's raw bytes separately
|
|
117
|
+
query.stdout.setEncoding('utf8');
|
|
118
|
+
query.stdout.on('data', (chunk) => {
|
|
119
|
+
output += chunk;
|
|
120
|
+
});
|
|
121
|
+
const abandon = setTimeout(() => {
|
|
122
|
+
query.kill();
|
|
123
|
+
resolve(null); // unknown, exactly as an unreadable table: the caller keeps waiting
|
|
124
|
+
}, timeoutMs);
|
|
125
|
+
abandon.unref?.();
|
|
126
|
+
const settle = (value: WindowsProcessRecord[] | null) => {
|
|
127
|
+
clearTimeout(abandon);
|
|
128
|
+
resolve(value);
|
|
129
|
+
};
|
|
130
|
+
query.once('close', (code) => settle(code === 0 ? parseProcessTable(output) : null));
|
|
131
|
+
query.once('error', () => settle(null));
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function parseProcessTable(json: string): WindowsProcessRecord[] | null {
|
|
136
|
+
let rows: unknown;
|
|
137
|
+
try {
|
|
138
|
+
rows = JSON.parse(json.replace(/^\uFEFF/, ''));
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
if (rows === null || typeof rows !== 'object') return null;
|
|
143
|
+
const table: WindowsProcessRecord[] = [];
|
|
144
|
+
for (const row of Array.isArray(rows) ? rows : [rows]) {
|
|
145
|
+
const { pid, ppid, created, name } = (row ?? {}) as Record<string, unknown>;
|
|
146
|
+
if (!Number.isInteger(pid) || !Number.isInteger(ppid)) return null;
|
|
147
|
+
table.push({
|
|
148
|
+
pid: pid as number,
|
|
149
|
+
ppid: ppid as number,
|
|
150
|
+
created: Number.isFinite(created) ? (created as number) : null,
|
|
151
|
+
name: typeof name === 'string' ? name : undefined,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return table;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function findWindowsTreeRoot(
|
|
158
|
+
table: WindowsProcessRecord[],
|
|
159
|
+
identity: WindowsProcessTreeIdentity
|
|
160
|
+
): WindowsProcessRecord | undefined {
|
|
161
|
+
if (identity.rootExitedAt !== undefined) return undefined;
|
|
162
|
+
const root = table.find((process) => process.pid === identity.rootPid);
|
|
163
|
+
if (!root || root.created === null) return undefined;
|
|
164
|
+
// Once a scan has pinned the root's exact creation time, a later row at the same PID is ours only
|
|
165
|
+
// if it matches exactly — same as a remembered descendant (line ~223). Without that pin, a
|
|
166
|
+
// replacement created within the clock-skew window of `rootKnownAt` would otherwise pass the loose
|
|
167
|
+
// upper-bound check below, which only rules out a replacement created too late to be ours, not one
|
|
168
|
+
// created too early (the original having already exited and been recycled inside that window).
|
|
169
|
+
if (identity.rootCreatedAt !== undefined) return root.created === identity.rootCreatedAt ? root : undefined;
|
|
170
|
+
if (root.created <= identity.rootKnownAt + CLOCK_SKEW_MS) return root;
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The processes in `table` that belong to the tree rooted at `identity.rootPid`. The root counts
|
|
176
|
+
* only while it still runs and is the process we spawned (created no later than we first knew
|
|
177
|
+
* it); a child counts only if it was created while its parent lived — after the root's own
|
|
178
|
+
* creation where that was observed, and before its exit — so nothing that merely inherited a
|
|
179
|
+
* recycled PID or ParentProcessId is included.
|
|
180
|
+
*/
|
|
181
|
+
export function selectWindowsProcessTree(
|
|
182
|
+
table: WindowsProcessRecord[],
|
|
183
|
+
identity: WindowsProcessTreeIdentity,
|
|
184
|
+
now: number = Date.now()
|
|
185
|
+
): WindowsProcessRecord[] {
|
|
186
|
+
const members: WindowsProcessRecord[] = [];
|
|
187
|
+
const seen = new Set<number>();
|
|
188
|
+
const root = findWindowsTreeRoot(table, identity);
|
|
189
|
+
// Only reserve the root's PID once it is verified as ours — once the root has exited, a live
|
|
190
|
+
// descendant that Windows hands that freed PID is a legitimate child of some other frontier
|
|
191
|
+
// member, and must still be reachable through the frontier walk below.
|
|
192
|
+
if (root) {
|
|
193
|
+
seen.add(identity.rootPid);
|
|
194
|
+
members.push(root);
|
|
195
|
+
}
|
|
196
|
+
const rootCreatedAt = root?.created ?? identity.rootCreatedAt;
|
|
197
|
+
// A row now holding `pid` that could not possibly be the member we are bounding — created after
|
|
198
|
+
// the latest moment a genuine one could have been — proves the PID was already recycled by then,
|
|
199
|
+
// tighter than a "gone" conclusion that only arrives once a scan stops observing the member at
|
|
200
|
+
// all (for a remembered descendant that can be a whole backed-off poll late; for the root, the
|
|
201
|
+
// scan that first fails to find it still uses this frontier before its exit is stamped —
|
|
202
|
+
// `confirmWindowsProcessTreeGone` below only latches `rootExitedAt` after this call returns).
|
|
203
|
+
// `oursNoLaterThan` for a descendant is its exact known creation time; for the root it is the
|
|
204
|
+
// latest creation `findWindowsTreeRoot` would still have accepted as ours, so a row this cannot
|
|
205
|
+
// exonerate is never treated as a false impostor of a root we simply haven't re-confirmed yet.
|
|
206
|
+
const impostorCreatedAt = (pid: number, oursNoLaterThan: number): number | undefined => {
|
|
207
|
+
const impostor = table.find(
|
|
208
|
+
(process) => process.pid === pid && process.created !== null && process.created > oursNoLaterThan
|
|
209
|
+
);
|
|
210
|
+
return impostor?.created;
|
|
211
|
+
};
|
|
212
|
+
let frontier = [
|
|
213
|
+
{
|
|
214
|
+
pid: identity.rootPid,
|
|
215
|
+
notBefore:
|
|
216
|
+
rootCreatedAt === undefined || rootCreatedAt === null
|
|
217
|
+
? // our own clock against WMI's, so it carries the skew allowance
|
|
218
|
+
identity.rootKnownAt - (identity.rootStartedWithinMs ?? ROOT_SPAWN_ALLOWANCE_MS) - CLOCK_SKEW_MS
|
|
219
|
+
: // WMI's clock against itself: a process created before the one whose PID it names as its
|
|
220
|
+
// parent was created by an earlier holder of that PID, so it is never ours
|
|
221
|
+
rootCreatedAt,
|
|
222
|
+
notAfter: Math.min(
|
|
223
|
+
// this bound is our own clock against WMI's, so it carries the skew allowance
|
|
224
|
+
(identity.rootExitedAt ?? now) + CLOCK_SKEW_MS,
|
|
225
|
+
// this one is WMI's clock against itself: the replacement's creation time is exactly when
|
|
226
|
+
// the PID stopped being ours, so anything it went on to create is a stranger's, not ours
|
|
227
|
+
(impostorCreatedAt(identity.rootPid, rootCreatedAt ?? identity.rootKnownAt + CLOCK_SKEW_MS) ?? Infinity) - 1
|
|
228
|
+
),
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
// A member an earlier scan found is still one — by PID and creation time — after the parent
|
|
232
|
+
// that linked it to the root has exited and left no row to walk through; its children are
|
|
233
|
+
// bounded by its lifetime like any other member's, latched from the scan that first lost it.
|
|
234
|
+
for (const [pid, known] of identity.descendants ?? []) {
|
|
235
|
+
if (seen.has(pid)) continue;
|
|
236
|
+
const live = table.find((process) => process.pid === pid && process.created === known.created);
|
|
237
|
+
if (live) {
|
|
238
|
+
seen.add(pid);
|
|
239
|
+
members.push(live);
|
|
240
|
+
frontier.push({ pid, notBefore: known.created, notAfter: Infinity });
|
|
241
|
+
} else {
|
|
242
|
+
frontier.push({
|
|
243
|
+
pid,
|
|
244
|
+
notBefore: known.created,
|
|
245
|
+
notAfter: Math.min(
|
|
246
|
+
(known.exitedAt ?? now) + CLOCK_SKEW_MS,
|
|
247
|
+
(impostorCreatedAt(pid, known.created) ?? Infinity) - 1
|
|
248
|
+
),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
while (frontier.length > 0) {
|
|
253
|
+
const next: typeof frontier = [];
|
|
254
|
+
for (const parent of frontier) {
|
|
255
|
+
for (const process of table) {
|
|
256
|
+
if (process.ppid !== parent.pid || seen.has(process.pid) || process.created === null) continue;
|
|
257
|
+
if (process.created < parent.notBefore || process.created > parent.notAfter) continue;
|
|
258
|
+
seen.add(process.pid);
|
|
259
|
+
members.push(process);
|
|
260
|
+
next.push({ pid: process.pid, notBefore: process.created, notAfter: Infinity });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
frontier = next;
|
|
264
|
+
}
|
|
265
|
+
return members;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// `CreateProcess` rejects a command line over 8191 characters, and the rejection surfaces as a
|
|
269
|
+
// spawn error this module deliberately absorbs — so a tree with enough orphans would be "killed"
|
|
270
|
+
// by a command that never ran, and the wait would never end. At ~11 characters per `/pid NNNNN`
|
|
271
|
+
// this is an order of magnitude inside the limit.
|
|
272
|
+
const TASKKILL_PIDS_PER_INVOCATION = 200;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* The taskkill argument lists that terminate `members`: the whole tree through the root while the
|
|
276
|
+
* root itself is still ours — never also by descendant PID in the same round, since `/T` frees
|
|
277
|
+
* those PIDs before a second invocation could run — and otherwise every member by its own PID,
|
|
278
|
+
* which is safe only because the root's PID is then no longer ours to `/T`. Per-PID kills are
|
|
279
|
+
* batched, since one command line cannot name an unbounded number of them.
|
|
280
|
+
*/
|
|
281
|
+
export function taskkillInvocation(members: WindowsProcessRecord[], rootPid: number): string[][] | null {
|
|
282
|
+
if (members.length === 0) return null;
|
|
283
|
+
if (members.some((member) => member.pid === rootPid)) return [['/pid', String(rootPid), '/T', '/F']];
|
|
284
|
+
const invocations: string[][] = [];
|
|
285
|
+
for (let i = 0; i < members.length; i += TASKKILL_PIDS_PER_INVOCATION) {
|
|
286
|
+
const batch = members.slice(i, i + TASKKILL_PIDS_PER_INVOCATION);
|
|
287
|
+
invocations.push(['/F', ...batch.flatMap((member) => ['/pid', String(member.pid)])]);
|
|
288
|
+
}
|
|
289
|
+
return invocations;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function runTaskkill(args: string[], timeoutMs: number = WINDOWS_TREE_HELPER_TIMEOUT_MS): Promise<void> {
|
|
293
|
+
return new Promise((resolve) => {
|
|
294
|
+
const taskkill = spawn('taskkill', args, { stdio: 'ignore', windowsHide: true });
|
|
295
|
+
const abandon = setTimeout(() => {
|
|
296
|
+
taskkill.kill();
|
|
297
|
+
resolve(); // the next round reissues it; nothing here reads a kill as proof of anything
|
|
298
|
+
}, timeoutMs);
|
|
299
|
+
abandon.unref?.();
|
|
300
|
+
const settle = () => {
|
|
301
|
+
clearTimeout(abandon);
|
|
302
|
+
resolve();
|
|
303
|
+
};
|
|
304
|
+
taskkill.once('close', settle);
|
|
305
|
+
taskkill.once('error', settle);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function killWindowsProcesses(members: WindowsProcessRecord[], rootPid: number): Promise<void> {
|
|
310
|
+
for (const args of taskkillInvocation(members, rootPid) ?? []) await runTaskkill(args);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function rememberDescendants(
|
|
314
|
+
identity: WindowsProcessTreeIdentity,
|
|
315
|
+
members: WindowsProcessRecord[],
|
|
316
|
+
scannedAt: number,
|
|
317
|
+
root: WindowsProcessRecord | undefined
|
|
318
|
+
) {
|
|
319
|
+
const descendants = (identity.descendants ??= new Map());
|
|
320
|
+
for (const member of members) {
|
|
321
|
+
// Skip only the verified root row itself (its lifetime is tracked separately via
|
|
322
|
+
// rootCreatedAt/rootExitedAt) — not every member sharing its PID number. Once the root has
|
|
323
|
+
// exited, a live descendant that Windows hands that freed PID is a distinct incarnation,
|
|
324
|
+
// identified by its own (pid, created) pair, and must be remembered like any other descendant
|
|
325
|
+
// so a later scan can still find it and its own children.
|
|
326
|
+
if (member === root || member.created === null) continue;
|
|
327
|
+
if (descendants.get(member.pid)?.created !== member.created)
|
|
328
|
+
descendants.set(member.pid, { created: member.created });
|
|
329
|
+
}
|
|
330
|
+
for (const [pid, known] of descendants) {
|
|
331
|
+
if (known.exitedAt !== undefined) continue;
|
|
332
|
+
if (!members.some((member) => member.pid === pid && member.created === known.created)) known.exitedAt = scannedAt;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function describeMembers(members: WindowsProcessRecord[]): string {
|
|
337
|
+
return members
|
|
338
|
+
.map(
|
|
339
|
+
(member) =>
|
|
340
|
+
`${member.pid}${member.name ? ` (${member.name})` : ''} created ${member.created === null ? 'unknown' : new Date(member.created).toISOString()}`
|
|
341
|
+
)
|
|
342
|
+
.join(', ');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Terminate every remaining member of the tree and resolve once a scan positively finds none.
|
|
347
|
+
* There is deliberately no deadline: releasing a component's preparation lock while a descendant
|
|
348
|
+
* can still mutate its directory is less safe than keeping that deployment wedged for operator
|
|
349
|
+
* intervention — but the wait is logged, so a wedge names the process holding it, and the poll
|
|
350
|
+
* backs off so a wedge does not spin the process table query. A scan that cannot read the table
|
|
351
|
+
* (null) is unknown, not gone, and keeps the wait going.
|
|
352
|
+
*/
|
|
353
|
+
export async function confirmWindowsProcessTreeGone(
|
|
354
|
+
identity: WindowsProcessTreeIdentity,
|
|
355
|
+
options: WindowsProcessTreeWaitOptions = {}
|
|
356
|
+
): Promise<void> {
|
|
357
|
+
const scan = options.scan ?? queryWindowsProcessTable;
|
|
358
|
+
const kill = options.kill ?? killWindowsProcesses;
|
|
359
|
+
const now = options.now ?? Date.now;
|
|
360
|
+
const warn = options.warn ?? ((message: string) => harperLogger.warn(message));
|
|
361
|
+
const maxPollMs = options.maxPollMs ?? WINDOWS_TREE_POLL_MAX_MS;
|
|
362
|
+
let pollMs = options.pollMs ?? WINDOWS_TREE_POLL_MS;
|
|
363
|
+
const label = options.label ?? `pid ${identity.rootPid}`;
|
|
364
|
+
const startedAt = now();
|
|
365
|
+
let nextWarningAt = startedAt + WINDOWS_TREE_WARNING_MS;
|
|
366
|
+
for (;;) {
|
|
367
|
+
const table = await scan();
|
|
368
|
+
let members: WindowsProcessRecord[] | null = null;
|
|
369
|
+
let rootIdentityUnknown: WindowsProcessRecord | undefined;
|
|
370
|
+
if (table !== null) {
|
|
371
|
+
const scannedAt = now();
|
|
372
|
+
const root = findWindowsTreeRoot(table, identity);
|
|
373
|
+
rootIdentityUnknown =
|
|
374
|
+
identity.rootExitedAt === undefined
|
|
375
|
+
? table.find((process) => process.pid === identity.rootPid && process.created === null)
|
|
376
|
+
: undefined;
|
|
377
|
+
if (rootIdentityUnknown) {
|
|
378
|
+
members = selectWindowsProcessTree(table, identity, scannedAt).filter(
|
|
379
|
+
(member) => identity.descendants?.get(member.pid)?.created === member.created
|
|
380
|
+
);
|
|
381
|
+
} else {
|
|
382
|
+
if (root && identity.rootCreatedAt === undefined) identity.rootCreatedAt = root.created;
|
|
383
|
+
members = selectWindowsProcessTree(table, identity, scannedAt);
|
|
384
|
+
// The root's PID is reusable the moment it exits, so from the first scan that no longer finds
|
|
385
|
+
// it running as ours, nothing created later can be its child. Stamped after the scan: the
|
|
386
|
+
// snapshot may predate the stamp, and a bound that is late keeps waiting, which is the safe
|
|
387
|
+
// direction. The same goes for every other member.
|
|
388
|
+
if (!root && identity.rootExitedAt === undefined) identity.rootExitedAt = scannedAt;
|
|
389
|
+
rememberDescendants(identity, members, scannedAt, root);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (!rootIdentityUnknown && members?.length === 0) return;
|
|
393
|
+
if (members?.length) await kill(members, identity.rootPid);
|
|
394
|
+
const time = now();
|
|
395
|
+
if (time >= nextWarningAt) {
|
|
396
|
+
warn(
|
|
397
|
+
`Termination of the process tree of ${label} remains unconfirmed after ${Math.round(time - startedAt)}ms: ` +
|
|
398
|
+
(rootIdentityUnknown
|
|
399
|
+
? `root pid ${identity.rootPid} is held by ${rootIdentityUnknown.name ?? 'an unnamed process'} without a creation time`
|
|
400
|
+
: members
|
|
401
|
+
? `live members ${describeMembers(members)}`
|
|
402
|
+
: 'the Windows process table could not be queried')
|
|
403
|
+
);
|
|
404
|
+
nextWarningAt = time + WINDOWS_TREE_WARNING_INTERVAL_MS;
|
|
405
|
+
}
|
|
406
|
+
await delay(pollMs);
|
|
407
|
+
pollMs = Math.min(pollMs * 2, maxPollMs);
|
|
408
|
+
}
|
|
409
|
+
}
|
package/server/throttle.ts
CHANGED
|
@@ -5,6 +5,7 @@ const DEFAULT_MAX_QUEUE_TIME = 20_000; // 20 seconds
|
|
|
5
5
|
let lastWarning = 0;
|
|
6
6
|
const WARNING_INTERVAL = 30000;
|
|
7
7
|
const EVENT_QUEUE_MONITORING_INTERVAL = 3000;
|
|
8
|
+
const LIMIT_EXCEEDED_LOG_INTERVAL = 5000;
|
|
8
9
|
let lastEventQueueCheck = performance.now() + EVENT_QUEUE_MONITORING_INTERVAL;
|
|
9
10
|
let averageEventCycleTime = 0;
|
|
10
11
|
/**
|
|
@@ -16,13 +17,22 @@ let averageEventCycleTime = 0;
|
|
|
16
17
|
export function throttle(
|
|
17
18
|
fn: (...args: any) => any,
|
|
18
19
|
onLimitExceeded?: (...args: any) => any,
|
|
19
|
-
maxQueueTimeLimit = DEFAULT_MAX_QUEUE_TIME
|
|
20
|
+
maxQueueTimeLimit = DEFAULT_MAX_QUEUE_TIME,
|
|
21
|
+
name?: string
|
|
20
22
|
) {
|
|
21
23
|
let queuedCalls: any[];
|
|
24
|
+
let lastLimitExceededLog = -Infinity;
|
|
22
25
|
return function (...args: any[]) {
|
|
23
26
|
if (queuedCalls) {
|
|
24
27
|
// this is an estimate of the time an event will take to process, based on the average event cycle time and the queue depth
|
|
25
28
|
if (queuedCalls.length * averageEventCycleTime > maxQueueTimeLimit) {
|
|
29
|
+
const now = performance.now();
|
|
30
|
+
if (now - lastLimitExceededLog > LIMIT_EXCEEDED_LOG_INTERVAL) {
|
|
31
|
+
lastLimitExceededLog = now;
|
|
32
|
+
logger.warn?.(
|
|
33
|
+
`Rejecting queued calls${name ? ` (${name})` : ''}: ${queuedCalls.length} already queued at ~${Math.round(averageEventCycleTime)}ms per event cycle, an estimated wait past the ${maxQueueTimeLimit}ms limit`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
26
36
|
return onLimitExceeded(...args);
|
|
27
37
|
}
|
|
28
38
|
return new Promise((resolve, reject) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{i as e,t}from"./rolldown-runtime-Dd_uD5pT.js";import{S as n,_ as r,a as i,b as a,c as o,d as s,f as c,g as l,h as u,l as d,m as f,n as p,o as m,p as h,r as g,s as _,t as v,v as y,x as b,y as x}from"./vendor-core-BdkdbUek.js";import{i as S,t as C}from"./button-MFP9n8Ck.js";import{a as ee}from"./vendor-datadog-fCQhBHGk.js";import{r as te}from"./vendor-react-izWchTRL.js";import{J as ne,L as re,k as ie,z as ae}from"./vendor-tanstack-CQ1aRjVa.js";import{n as oe}from"./setSessionStorage-B0bf71m4.js";import{It as se}from"./vendor-ui-aE1tHcW7.js";import{t as w}from"./createLucideIcon-bziYxJli.js";import{n as ce,r as le,t as ue}from"./chevron-up-CFe8x5Bp.js";import{c as de,f as fe,h as pe,i as me,l as he,m as ge,p as _e,r as ve,t as ye,u as be}from"./setComponentFile-
|
|
1
|
+
import{i as e,t}from"./rolldown-runtime-Dd_uD5pT.js";import{S as n,_ as r,a as i,b as a,c as o,d as s,f as c,g as l,h as u,l as d,m as f,n as p,o as m,p as h,r as g,s as _,t as v,v as y,x as b,y as x}from"./vendor-core-BdkdbUek.js";import{i as S,t as C}from"./button-MFP9n8Ck.js";import{a as ee}from"./vendor-datadog-fCQhBHGk.js";import{r as te}from"./vendor-react-izWchTRL.js";import{J as ne,L as re,k as ie,z as ae}from"./vendor-tanstack-CQ1aRjVa.js";import{n as oe}from"./setSessionStorage-B0bf71m4.js";import{It as se}from"./vendor-ui-aE1tHcW7.js";import{t as w}from"./createLucideIcon-bziYxJli.js";import{n as ce,r as le,t as ue}from"./chevron-up-CFe8x5Bp.js";import{c as de,f as fe,h as pe,i as me,l as he,m as ge,p as _e,r as ve,t as ye,u as be}from"./setComponentFile-z4-qJCbA.js";import{t as xe}from"./x-vYntCxm4.js";import{n as Se}from"./setLocalStorage-D_kflv4U.js";import{t as Ce}from"./useLocalStorage-BYoZF60G.js";import{Ar as we,Dr as Te,H as Ee,It as De,Mn as Oe,Mt as ke,Nt as Ae,On as je,Pr as Me,Pt as Ne,Sr as Pe,V as Fe,Z as Ie,_n as Le,_r as Re,c as ze,cr as Be,d as Ve,f as He,fr as Ue,ft as We,in as Ge,kr as Ke,m as qe,mr as Je,ur as Ye,ut as Xe,xr as Ze,yt as Qe}from"./index-DGRAzvO4.js";import{t as $e}from"./useEntityRestURL-BKetlINf.js";import{n as et,t as tt}from"./FloatingChat-D4560T0p.js";import{n as nt}from"./getAnalytics-CjthMQqF.js";var rt={name:`between-horizontal-start`,size:24,node:[[`rect`,{width:`13`,height:`7`,x:`8`,y:`3`,rx:`1`,key:`pkso9a`}],[`path`,{d:`m2 9 3 3-3 3`,key:`1agib5`}],[`rect`,{width:`13`,height:`7`,x:`8`,y:`14`,rx:`1`,key:`1q5fc1`}]],aliases:[`between-horizonal-start`]};rt.node;var it=w(rt),at={name:`book`,size:24,node:[[`path`,{d:`M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20`,key:`k3hazp`}]]};at.node;var ot=w(at),st={name:`chart-area`,size:24,node:[[`path`,{d:`M3 3v16a2 2 0 0 0 2 2h16`,key:`c24i48`}],[`path`,{d:`M7 11.207a.5.5 0 0 1 .146-.353l2-2a.5.5 0 0 1 .708 0l3.292 3.292a.5.5 0 0 0 .708 0l4.292-4.292a.5.5 0 0 1 .854.353V16a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1z`,key:`q0gr47`}]],aliases:[`area-chart`]};st.node;var ct=w(st),lt={name:`circle-x`,size:24,node:[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]],aliases:[`x-circle`]};lt.node;var ut=w(lt),dt={name:`file-pen`,size:24,node:[[`path`,{d:`M12.659 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v9.34`,key:`o6klzx`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10.378 12.622a1 1 0 0 1 3 3.003L8.36 20.637a2 2 0 0 1-.854.506l-2.867.837a.5.5 0 0 1-.62-.62l.836-2.869a2 2 0 0 1 .506-.853z`,key:`zhnas1`}]],aliases:[`file-edit`]};dt.node;var ft=w(dt),pt={name:`logs`,size:24,node:[[`path`,{d:`M3 5h1`,key:`1mv5vm`}],[`path`,{d:`M3 12h1`,key:`lp3yf2`}],[`path`,{d:`M3 19h1`,key:`w6f3n9`}],[`path`,{d:`M8 5h1`,key:`1nxr5w`}],[`path`,{d:`M8 12h1`,key:`1con00`}],[`path`,{d:`M8 19h1`,key:`k7p10e`}],[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}]]};pt.node;var mt=w(pt),ht={name:`message-square-heart`,size:24,node:[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}],[`path`,{d:`M7.5 9.5c0 .687.265 1.383.697 1.844l3.009 3.264a1.14 1.14 0 0 0 .407.314 1 1 0 0 0 .783-.004 1.14 1.14 0 0 0 .398-.31l3.008-3.264A2.77 2.77 0 0 0 16.5 9.5 2.5 2.5 0 0 0 12 8a2.5 2.5 0 0 0-4.5 1.5`,key:`1faxuh`}]]};ht.node;var gt=w(ht),_t={name:`send`,size:24,node:[[`path`,{d:`M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z`,key:`1ffxy3`}],[`path`,{d:`m21.854 2.147-10.94 10.939`,key:`12cjpa`}]]};_t.node;var vt=w(_t),yt={name:`wrench`,size:24,node:[[`path`,{d:`M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z`,key:`1ngwbx`}]]};yt.node;var bt=w(yt);async function xt(){await S.delete(`/Chat/Messages/`)}var T=e(ee(),1),E=te();function St({setMessages:e}){let[t,n]=(0,T.useState)(!1),r=(0,T.useCallback)(async()=>{if(!t){n(!0);try{await xt(),e([])}catch(e){console.error(`Failed to clear chat:`,e)}finally{n(!1)}}},[t,e]);return(0,E.jsxs)(`button`,{type:`button`,className:`clear-chat-button gap-1`,onClick:r,disabled:t,title:`Clear chat`,children:[t?(0,E.jsx)(Ze,{className:`animate-spin`,size:18}):(0,E.jsx)(Ue,{size:18}),`Clear`]})}async function Ct(){let{data:e}=await S.get(`/Chat/Messages/`);return e}var wt=`vercel.ai.error`,Tt=Symbol.for(wt),Et,Dt,D=class e extends (Dt=Error,Et=Tt,Dt){constructor({name:e,message:t,cause:n}){super(t),this[Et]=!0,this.name=e,this.cause=n}static isInstance(t){return e.hasMarker(t,wt)}static hasMarker(e,t){let n=Symbol.for(t);return typeof e==`object`&&!!e&&n in e&&typeof e[n]==`boolean`&&e[n]===!0}},Ot=`AI_APICallError`,kt=`vercel.ai.error.${Ot}`,At=Symbol.for(kt),jt,Mt,Nt=class extends (Mt=D,jt=At,Mt){constructor({message:e,url:t,requestBodyValues:n,statusCode:r,responseHeaders:i,responseBody:a,cause:o,isRetryable:s=r!=null&&(r===408||r===409||r===429||r>=500),data:c}){super({name:Ot,message:e,cause:o}),this[jt]=!0,this.url=t,this.requestBodyValues=n,this.statusCode=r,this.responseHeaders=i,this.responseBody=a,this.isRetryable=s,this.data=c}static isInstance(e){return D.hasMarker(e,kt)}},Pt=`AI_EmptyResponseBodyError`,Ft=`vercel.ai.error.${Pt}`,It=Symbol.for(Ft),Lt,Rt,zt=class extends (Rt=D,Lt=It,Rt){constructor({message:e=`Empty response body`}={}){super({name:Pt,message:e}),this[Lt]=!0}static isInstance(e){return D.hasMarker(e,Ft)}};function Bt(e){return e==null?`unknown error`:typeof e==`string`?e:e instanceof Error?e.toString():JSON.stringify(e)}var Vt=`AI_InvalidArgumentError`,Ht=`vercel.ai.error.${Vt}`,Ut=Symbol.for(Ht),Wt,Gt,Kt=class extends (Gt=D,Wt=Ut,Gt){constructor({message:e,cause:t,argument:n}){super({name:Vt,message:e,cause:t}),this[Wt]=!0,this.argument=n}static isInstance(e){return D.hasMarker(e,Ht)}},qt=`AI_JSONParseError`,Jt=`vercel.ai.error.${qt}`,Yt=Symbol.for(Jt),Xt,Zt,Qt=class extends (Zt=D,Xt=Yt,Zt){constructor({text:e,cause:t}){super({name:qt,message:`JSON parsing failed: Text: ${e}.
|
|
2
2
|
Error message: ${Bt(t)}`,cause:t}),this[Xt]=!0,this.text=e}static isInstance(e){return D.hasMarker(e,Jt)}},$t=`AI_TypeValidationError`,en=`vercel.ai.error.${$t}`,tn=Symbol.for(en),nn,rn,O=class e extends (rn=D,nn=tn,rn){constructor({value:e,cause:t,context:n}){let r=`Type validation failed`;if(n?.field&&(r+=` for ${n.field}`),n?.entityName||n?.entityId){r+=` (`;let e=[];n.entityName&&e.push(n.entityName),n.entityId&&e.push(`id: "${n.entityId}"`),r+=e.join(`, `),r+=`)`}super({name:$t,message:`${r}: Value: ${JSON.stringify(e)}.
|
|
3
3
|
Error message: ${Bt(t)}`,cause:t}),this[nn]=!0,this.value=e,this.context=n}static isInstance(e){return D.hasMarker(e,en)}static wrap({value:t,cause:n,context:r}){return e.isInstance(n)&&n.value===t&&n.context?.field===r?.field&&n.context?.entityName===r?.entityName&&n.context?.entityId===r?.entityId?n:new e({value:t,cause:n,context:r})}},an=`AI_UnsupportedFunctionalityError`,on=`vercel.ai.error.${an}`,sn=Symbol.for(on),cn,ln,un=class extends (ln=D,cn=sn,ln){constructor({functionality:e,message:t=`'${e}' functionality not supported.`}){super({name:an,message:t}),this[cn]=!0,this.functionality=e}static isInstance(e){return D.hasMarker(e,on)}},dn=class extends Error{constructor(e,t){super(e),this.name=`ParseError`,this.type=t.type,this.field=t.field,this.value=t.value,this.line=t.line}},fn=10,pn=13,k=32;function mn(e){}function hn(e){if(typeof e==`function`)throw TypeError("`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?");let{onEvent:t=mn,onError:n=mn,onRetry:r=mn,onComment:i,maxBufferSize:a}=e,o=[],s=0,c=!0,l,u=``,d=0,f,p=!1;function m(e){if(p)throw Error("Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.");if(c&&(c=!1,e.charCodeAt(0)===239&&e.charCodeAt(1)===187&&e.charCodeAt(2)===191&&(e=e.slice(3))),o.length===0){let t=g(e);t!==``&&(o.push(t),s=t.length),h();return}if(e.indexOf(`
|
|
4
4
|
`)===-1&&e.indexOf(`\r`)===-1){o.push(e),s+=e.length,h();return}o.push(e);let t=o.join(``);o.length=0,s=0;let n=g(t);n!==``&&(o.push(n),s=n.length),h()}function h(){a!==void 0&&(s+u.length<=a||(p=!0,o.length=0,s=0,l=void 0,u=``,d=0,f=void 0,n(new dn(`Buffered data exceeded max buffer size of ${a} characters`,{type:`max-buffer-size-exceeded`}))))}function g(e){let n=0;if(e.indexOf(`\r`)===-1){let r=e.indexOf(`
|