nolo-cli 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/agent-runtime/hostAdapter.ts +53 -0
- package/agent-runtime/index.ts +28 -0
- package/agent-runtime/localLoop.ts +62 -0
- package/agent-runtime/runtimeDecision.ts +70 -0
- package/agent-runtime/types.ts +87 -0
- package/agentRunCommand.ts +104 -0
- package/agentRuntimeCommands.ts +139 -22
- package/agentRuntimeLocal.ts +7 -0
- package/ai/agent/_executeModel.ts +118 -0
- package/ai/agent/agentSlice.ts +544 -1
- package/ai/agent/appWorkingMemory.ts +126 -0
- package/ai/agent/avatarUtils.ts +24 -0
- package/ai/agent/buildEditingContext.ts +373 -0
- package/ai/agent/buildSystemPrompt.ts +532 -0
- package/ai/agent/cleanAgentMessages.ts +140 -0
- package/ai/agent/cliChatClient.ts +119 -0
- package/ai/agent/contextCompiler.ts +107 -0
- package/ai/agent/contextLayerContract.ts +44 -0
- package/ai/agent/createAgentSchema.ts +234 -0
- package/ai/agent/executeToolCall.ts +58 -0
- package/ai/agent/fetchAgentContexts.ts +42 -0
- package/ai/agent/generatePrompt.ts +3 -0
- package/ai/agent/getFullChatContextKeys.ts +168 -0
- package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
- package/ai/agent/hooks/useAgentConfig.ts +61 -0
- package/ai/agent/hooks/useAgentDialog.ts +35 -0
- package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
- package/ai/agent/hooks/usePublicAgents.ts +473 -0
- package/ai/agent/persistMessageWithFixedId.ts +37 -0
- package/ai/agent/planSlice.ts +259 -0
- package/ai/agent/referenceUtils.ts +229 -0
- package/ai/agent/runAgentBackground.ts +238 -0
- package/ai/agent/runAgentClientLoop.ts +138 -0
- package/ai/agent/runtimeGuidance.ts +97 -0
- package/ai/agent/runtimeServerBase.ts +37 -0
- package/ai/agent/server/fetchPublicAgents.ts +128 -0
- package/ai/agent/startParallelAgentStreams.ts +424 -0
- package/ai/agent/startupProtocol.ts +53 -0
- package/ai/agent/streamAgentChatTurn.ts +1299 -0
- package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
- package/ai/agent/types.ts +71 -0
- package/ai/agent/utils/imageOutput.ts +39 -0
- package/ai/agent/utils/publicImageAgentMode.ts +26 -0
- package/ai/agent/utils/sortUtils.ts +250 -0
- package/ai/agent/web/referencePickerUtils.ts +146 -0
- package/ai/ai.locale.ts +1083 -0
- package/ai/chat/accumulateToolCallChunks.ts +95 -0
- package/ai/chat/fetchUtils.native.ts +276 -0
- package/ai/chat/fetchUtils.ts +153 -0
- package/ai/chat/inlineImageUrlsForCustomProvider.ts +117 -0
- package/ai/chat/parseApiError.ts +64 -0
- package/ai/chat/parseMultilineSSE.ts +95 -0
- package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +712 -0
- package/ai/chat/sendOpenAIResponseRequest.ts +512 -0
- package/ai/chat/shouldUseServerProxy.ts +18 -0
- package/ai/chat/sseClient.native.ts +91 -0
- package/ai/chat/sseClient.ts +67 -0
- package/ai/chat/streamReader.native.ts +31 -0
- package/ai/chat/streamReader.ts +62 -0
- package/ai/chat/updateTotalUsage.ts +72 -0
- package/ai/context/buildReferenceContext.ts +437 -0
- package/ai/context/calculateContextUsage.ts +133 -0
- package/ai/context/retention.ts +165 -0
- package/ai/context/tokenUtils.ts +78 -0
- package/ai/index.ts +1 -1
- package/ai/llm/agentCapabilities.ts +74 -0
- package/ai/llm/calculateGeminiImageTokens.ts +57 -0
- package/ai/llm/deepinfra.ts +28 -0
- package/ai/llm/fireworks.ts +68 -0
- package/ai/llm/generateRequestBody.ts +165 -0
- package/ai/llm/getModelContextWindow.ts +84 -0
- package/ai/llm/getNoloKey.ts +37 -0
- package/ai/llm/getPricing.ts +232 -0
- package/ai/llm/hooks/useModelPricing.ts +75 -0
- package/ai/llm/imagePricing.ts +66 -0
- package/ai/llm/isResponseAPIModel.ts +13 -0
- package/ai/llm/kimi.ts +18 -0
- package/ai/llm/mimo.ts +71 -0
- package/ai/llm/mistral.ts +22 -0
- package/ai/llm/modelAvatar.ts +427 -0
- package/ai/llm/models.ts +45 -0
- package/ai/llm/openrouterModels.ts +141 -0
- package/ai/llm/providers.ts +307 -0
- package/ai/llm/reasoningModels.ts +28 -0
- package/ai/llm/types.ts +59 -0
- package/ai/llm/usageRequestOptions.ts +59 -0
- package/ai/memory/capture.ts +148 -0
- package/ai/memory/consolidate.ts +104 -0
- package/ai/memory/delete.ts +147 -0
- package/ai/memory/overlay.ts +84 -0
- package/ai/memory/query.ts +38 -0
- package/ai/memory/queryShared.ts +160 -0
- package/ai/memory/rank.ts +105 -0
- package/ai/memory/recentRelationshipRecap.ts +247 -0
- package/ai/memory/remember.ts +167 -0
- package/ai/memory/runtime.ts +76 -0
- package/ai/memory/store.ts +20 -0
- package/ai/memory/storeShared.ts +76 -0
- package/ai/memory/types.ts +46 -0
- package/ai/memory/understanding.ts +349 -0
- package/ai/memory/understandingGreeting.ts +264 -0
- package/ai/messages/type.ts +20 -0
- package/ai/policy/personalizationDialog.ts +333 -0
- package/ai/policy/runtimePolicy.ts +440 -0
- package/ai/policy/selfUpdateFields.ts +48 -0
- package/ai/policy/types.ts +64 -0
- package/ai/skills/referenceRuntime.ts +274 -0
- package/ai/skills/skillDiagnostics.ts +251 -0
- package/ai/skills/skillDocBuilder.ts +139 -0
- package/ai/skills/skillDocProtocol.ts +434 -0
- package/ai/skills/skillReferenceSummary.ts +63 -0
- package/ai/skills/skillSummaryMarker.ts +26 -0
- package/ai/token/calculatePrice.ts +546 -0
- package/ai/token/db.ts +98 -0
- package/ai/token/externalToolCost.ts +321 -0
- package/ai/token/hooks/useRecords.ts +65 -0
- package/ai/token/missingUsageEstimate.ts +42 -0
- package/ai/token/modelUsageQuery.ts +252 -0
- package/ai/token/normalizeUsage.ts +84 -0
- package/ai/token/openaiImageGenerationUsage.ts +56 -0
- package/ai/token/prepareTokenUsageData.ts +88 -0
- package/ai/token/query.ts +88 -0
- package/ai/token/queryUserTokens.ts +59 -0
- package/ai/token/resolveBillingTarget.ts +52 -0
- package/ai/token/saveTokenRecord.ts +53 -0
- package/ai/token/serverDialogProjection.ts +78 -0
- package/ai/token/serverTokenWriter.ts +143 -0
- package/ai/token/stats.ts +21 -0
- package/ai/token/tokenThunks.ts +24 -0
- package/ai/token/types.ts +93 -0
- package/ai/tools/agent/agentTools.ts +176 -0
- package/ai/tools/agent/agentUpdateShared.ts +311 -0
- package/ai/tools/agent/callAgentTool.ts +139 -0
- package/ai/tools/agent/createAgentTool.ts +512 -0
- package/ai/tools/agent/createDialogTool.ts +69 -0
- package/ai/tools/agent/createSkillAgentTool.ts +62 -0
- package/ai/tools/agent/parallelBudget.ts +221 -0
- package/ai/tools/agent/presets/appBuilderPreset.ts +147 -0
- package/ai/tools/agent/runLlmTool.ts +96 -0
- package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
- package/ai/tools/agent/skillAgentArgs.ts +106 -0
- package/ai/tools/agent/skillAgentPreset.ts +89 -0
- package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
- package/ai/tools/agent/updateAgentTool.ts +96 -0
- package/ai/tools/agent/updateSelfTool.ts +113 -0
- package/ai/tools/amazonProductScraperTool.ts +86 -0
- package/ai/tools/apifyActorClient.ts +45 -0
- package/ai/tools/appEditGuard.ts +372 -0
- package/ai/tools/appReadSnapshot.ts +153 -0
- package/ai/tools/appTools.ts +1549 -0
- package/ai/tools/applyEditTool.ts +256 -0
- package/ai/tools/applyLineEditsTool.ts +312 -0
- package/ai/tools/browserTools/click.ts +33 -0
- package/ai/tools/browserTools/closeSession.ts +29 -0
- package/ai/tools/browserTools/common.ts +27 -0
- package/ai/tools/browserTools/openSession.ts +48 -0
- package/ai/tools/browserTools/readContent.ts +38 -0
- package/ai/tools/browserTools/selectOption.ts +46 -0
- package/ai/tools/browserTools/typeText.ts +42 -0
- package/ai/tools/category/createCategoryTool.ts +66 -0
- package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
- package/ai/tools/category/updateContentCategoryTool.ts +75 -0
- package/ai/tools/cfBrowserTools.ts +319 -0
- package/ai/tools/cfSpeechToTextTool.ts +49 -0
- package/ai/tools/checkEnvTool.ts +65 -0
- package/ai/tools/cloudflareCrawlTool.ts +289 -0
- package/ai/tools/codeSearchTool.ts +111 -0
- package/ai/tools/codeTools.ts +101 -0
- package/ai/tools/createDocTool.ts +132 -0
- package/ai/tools/createPlanTool.ts +999 -0
- package/ai/tools/createSkillDocTool.ts +155 -0
- package/ai/tools/createWorkflowTool.ts +154 -0
- package/ai/tools/deepseekOcrTool.ts +34 -0
- package/ai/tools/delayTool.ts +31 -0
- package/ai/tools/deleteSpacesTool.ts +325 -0
- package/ai/tools/deleteSpacesToolModel.ts +159 -0
- package/ai/tools/devReloadUtils.ts +29 -0
- package/ai/tools/dialogMessageSearch.ts +137 -0
- package/ai/tools/doctorSkillTool.ts +72 -0
- package/ai/tools/ecommerceScraperTool.ts +86 -0
- package/ai/tools/emailTools.ts +549 -0
- package/ai/tools/evalSkillTool.ts +92 -0
- package/ai/tools/exaSearchTool.ts +64 -0
- package/ai/tools/execBashTool.ts +379 -0
- package/ai/tools/executeSqlTool.ts +192 -0
- package/ai/tools/fetchWebpageSupport.ts +309 -0
- package/ai/tools/fetchWebpageTool.ts +84 -0
- package/ai/tools/geminiImagePreviewTool.ts +361 -0
- package/ai/tools/generateDocxTool.ts +215 -0
- package/ai/tools/googleSearchScraperTool.ts +106 -0
- package/ai/tools/importDataTool.ts +133 -0
- package/ai/tools/importSkillTool.ts +162 -0
- package/ai/tools/index.ts +1927 -0
- package/ai/tools/listFilesTool.ts +82 -0
- package/ai/tools/listUserSpacesTool.ts +113 -0
- package/ai/tools/modelUsageTools.ts +199 -0
- package/ai/tools/olmOcrTool.ts +34 -0
- package/ai/tools/openaiImageTool.ts +267 -0
- package/ai/tools/prepareTools.ts +23 -0
- package/ai/tools/readDocTool.ts +84 -0
- package/ai/tools/readFileTool.ts +211 -0
- package/ai/tools/readTool.ts +163 -0
- package/ai/tools/readXPostTool.ts +233 -0
- package/ai/tools/rememberMemoryTool.ts +84 -0
- package/ai/tools/remotionVideoTool.ts +151 -0
- package/ai/tools/searchDialogMessagesTool.ts +222 -0
- package/ai/tools/searchRepoTool.ts +115 -0
- package/ai/tools/searchWorkspaceTool.ts +259 -0
- package/ai/tools/skillFollowup.ts +86 -0
- package/ai/tools/surfWeatherTool.ts +169 -0
- package/ai/tools/table/addTableRowTool.ts +217 -0
- package/ai/tools/table/createTableTool.ts +315 -0
- package/ai/tools/table/rowTools.ts +366 -0
- package/ai/tools/table/schemaTools.ts +244 -0
- package/ai/tools/table/shareTableTool.ts +148 -0
- package/ai/tools/table/toolShared.ts +129 -0
- package/ai/tools/toolApiClient.ts +198 -0
- package/ai/tools/toolNameAliases.ts +57 -0
- package/ai/tools/toolResultError.ts +42 -0
- package/ai/tools/toolRunSlice.ts +303 -0
- package/ai/tools/toolSchemaCompatibility.ts +53 -0
- package/ai/tools/toolVisibility.ts +4 -0
- package/ai/tools/types.ts +20 -0
- package/ai/tools/uiAskChoiceTool.ts +104 -0
- package/ai/tools/updateContentTitleTool.ts +84 -0
- package/ai/tools/updateDocTool.ts +105 -0
- package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
- package/ai/tools/whisperTool.ts +77 -0
- package/ai/tools/writeFileTool.ts +210 -0
- package/ai/tools/youtubeScraperTool.ts +116 -0
- package/ai/tools/ziweiChartTool.ts +678 -0
- package/ai/types.ts +55 -0
- package/ai/workflow/workflowExecutor.ts +323 -0
- package/ai/workflow/workflowSlice.ts +73 -0
- package/ai/workflow/workflowTypes.ts +106 -0
- package/client/agentRun.test.ts +240 -0
- package/client/agentRun.ts +182 -19
- package/client/compactDialog.test.ts +238 -0
- package/client/localRuntimeAdapter.test.ts +135 -0
- package/client/localRuntimeAdapter.ts +244 -0
- package/client/profileConfig.test.ts +40 -0
- package/client/streamingOutput.test.ts +22 -0
- package/client/streamingOutput.ts +38 -0
- package/commandRegistry.ts +11 -2
- package/connector-experimental/index.ts +5 -0
- package/database/actions/cacheMergedUserData.ts +64 -0
- package/database/actions/common.ts +242 -0
- package/database/actions/deleteFile.ts +40 -0
- package/database/actions/fetchUserData.ts +16 -0
- package/database/actions/fileContent.ts +125 -0
- package/database/actions/patch.ts +155 -0
- package/database/actions/read.ts +337 -0
- package/database/actions/readAndWait.ts +224 -0
- package/database/actions/readRequestManager.ts +120 -0
- package/database/actions/remove.ts +94 -0
- package/database/actions/replication.ts +366 -0
- package/database/actions/upload.ts +174 -0
- package/database/actions/upsert.ts +56 -0
- package/database/actions/write.ts +126 -0
- package/database/client/db.native.ts +73 -0
- package/database/client/db.ts +51 -0
- package/database/client/fetchUserData.ts +61 -0
- package/database/client/handleError.ts +19 -0
- package/database/client/queryRequest.ts +21 -0
- package/database/config.ts +21 -0
- package/database/dbActionThunks.ts +1 -0
- package/database/dbSlice.ts +149 -0
- package/database/email.ts +42 -0
- package/database/fileRing.ts +51 -0
- package/database/fileSharding.ts +70 -0
- package/database/fileStorage.native.ts +92 -0
- package/database/fileStorage.ts +232 -0
- package/database/fileUrl.ts +34 -0
- package/database/hooks/useUserData.ts +489 -0
- package/database/index.ts +1 -0
- package/database/keys.ts +765 -0
- package/database/queryPrefixes.ts +14 -0
- package/database/requests.ts +443 -0
- package/database/runtimeServerContext.ts +35 -0
- package/database/server/MemoryDB.ts +76 -0
- package/database/server/actorAccess.ts +76 -0
- package/database/server/agentDelegation.ts +124 -0
- package/database/server/coreDataOwnership.ts +13 -0
- package/database/server/coreDataProxy.ts +76 -0
- package/database/server/cybotReadonly.ts +18 -0
- package/database/server/dataHandlers.ts +111 -0
- package/database/server/db.ts +118 -0
- package/database/server/dbPath.ts +20 -0
- package/database/server/delete.ts +499 -0
- package/database/server/emailRepository.ts +1480 -0
- package/database/server/ensureDbOpen.ts +12 -0
- package/database/server/fileRead.ts +337 -0
- package/database/server/fileService.ts +436 -0
- package/database/server/handleTransaction.ts +86 -0
- package/database/server/patch.ts +282 -0
- package/database/server/query.ts +138 -0
- package/database/server/read.ts +325 -0
- package/database/server/resourceAccess.ts +211 -0
- package/database/server/routes.ts +110 -0
- package/database/server/spaceMemberAuthority.ts +67 -0
- package/database/server/upload.ts +159 -0
- package/database/server/write.ts +494 -0
- package/database/server/writeAuthority.ts +133 -0
- package/database/sqliteDb.ts +46 -0
- package/database/table/deleteTable.ts +120 -0
- package/database/tenantPlacement.ts +57 -0
- package/database/tombstones.ts +52 -0
- package/database/userDataLoadDecision.ts +17 -0
- package/database/userDataMerge.ts +95 -0
- package/database/userPreferenceRegister.ts +108 -0
- package/database/utils/dbPath.ts +47 -0
- package/database/utils/ulid.native.ts +6 -0
- package/database/utils/ulid.ts +1 -0
- package/index.ts +37 -19
- package/localRuntimeDb.ts +28 -0
- package/package.json +17 -4
- package/runtimeModeArgs.ts +33 -0
- package/tui/readlineWorkspace.ts +1 -0
- package/tui/session.ts +22 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { AccessAction } from "./resourceAccess";
|
|
2
|
+
import serverDb from "./db";
|
|
3
|
+
|
|
4
|
+
export type AgentDelegation = {
|
|
5
|
+
principalUserId: string;
|
|
6
|
+
agentId: string;
|
|
7
|
+
scopes?: string[];
|
|
8
|
+
spaceIds?: string[];
|
|
9
|
+
resourcePrefixes?: string[];
|
|
10
|
+
expiresAt?: string | number | null;
|
|
11
|
+
revokedAt?: string | number | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const agentDelegationKey = (
|
|
15
|
+
principalUserId: string,
|
|
16
|
+
agentId: string
|
|
17
|
+
) => `agent-delegation-${principalUserId}-${agentId}`;
|
|
18
|
+
|
|
19
|
+
export const scopeForAction = (
|
|
20
|
+
action: AccessAction,
|
|
21
|
+
resource?: { dbKey?: string; record?: any }
|
|
22
|
+
): string => {
|
|
23
|
+
const dbKey = resource?.dbKey || resource?.record?.dbKey || "";
|
|
24
|
+
if (dbKey.startsWith("email-") || resource?.record?.type === "email") {
|
|
25
|
+
switch (action) {
|
|
26
|
+
case "read":
|
|
27
|
+
return "email:read";
|
|
28
|
+
case "write":
|
|
29
|
+
return "email:send";
|
|
30
|
+
case "delete":
|
|
31
|
+
return "email:delete";
|
|
32
|
+
case "manage":
|
|
33
|
+
return "email:manage";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
switch (action) {
|
|
38
|
+
case "read":
|
|
39
|
+
return "db:read";
|
|
40
|
+
case "write":
|
|
41
|
+
return "db:write";
|
|
42
|
+
case "delete":
|
|
43
|
+
return "db:delete";
|
|
44
|
+
case "manage":
|
|
45
|
+
return "db:write";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const normalizeSpaceId = (spaceId?: unknown): string | null => {
|
|
50
|
+
if (typeof spaceId !== "string") return null;
|
|
51
|
+
const trimmed = spaceId.trim();
|
|
52
|
+
if (!trimmed) return null;
|
|
53
|
+
return trimmed.startsWith("space-") ? trimmed.slice("space-".length) : trimmed;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const isPast = (value?: string | number | null): boolean => {
|
|
57
|
+
if (value == null) return false;
|
|
58
|
+
const timestamp =
|
|
59
|
+
typeof value === "number" ? value : new Date(value).getTime();
|
|
60
|
+
return Number.isFinite(timestamp) && Date.now() > timestamp;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export async function loadAgentDelegation(
|
|
64
|
+
principalUserId: string,
|
|
65
|
+
agentId: string
|
|
66
|
+
): Promise<AgentDelegation | null> {
|
|
67
|
+
if (!principalUserId || !agentId) return null;
|
|
68
|
+
try {
|
|
69
|
+
const delegation = await serverDb.get(
|
|
70
|
+
agentDelegationKey(principalUserId, agentId)
|
|
71
|
+
);
|
|
72
|
+
return delegation || null;
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function validateAgentDelegation({
|
|
79
|
+
delegation,
|
|
80
|
+
action,
|
|
81
|
+
dbKey,
|
|
82
|
+
record,
|
|
83
|
+
spaceId,
|
|
84
|
+
}: {
|
|
85
|
+
delegation: AgentDelegation | null;
|
|
86
|
+
action: AccessAction;
|
|
87
|
+
dbKey: string;
|
|
88
|
+
record?: any;
|
|
89
|
+
spaceId?: string | null;
|
|
90
|
+
}): { allowed: boolean; reason?: string } {
|
|
91
|
+
if (!delegation) return { allowed: false, reason: "missing_delegation" };
|
|
92
|
+
if (delegation.revokedAt != null) {
|
|
93
|
+
return { allowed: false, reason: "delegation_revoked" };
|
|
94
|
+
}
|
|
95
|
+
if (isPast(delegation.expiresAt)) {
|
|
96
|
+
return { allowed: false, reason: "delegation_expired" };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const scopes = Array.isArray(delegation.scopes) ? delegation.scopes : [];
|
|
100
|
+
if (!scopes.includes(scopeForAction(action, { dbKey, record }))) {
|
|
101
|
+
return { allowed: false, reason: "delegation_scope_denied" };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const prefixes = Array.isArray(delegation.resourcePrefixes)
|
|
105
|
+
? delegation.resourcePrefixes
|
|
106
|
+
: [];
|
|
107
|
+
const hasPrefixAccess = prefixes.some(
|
|
108
|
+
(prefix) => typeof prefix === "string" && dbKey.startsWith(prefix)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const normalizedSpaceId = normalizeSpaceId(spaceId);
|
|
112
|
+
const spaceIds = Array.isArray(delegation.spaceIds)
|
|
113
|
+
? delegation.spaceIds.map(normalizeSpaceId).filter(Boolean)
|
|
114
|
+
: [];
|
|
115
|
+
const hasSpaceAccess = Boolean(
|
|
116
|
+
normalizedSpaceId && spaceIds.includes(normalizedSpaceId)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (!hasPrefixAccess && !hasSpaceAccess) {
|
|
120
|
+
return { allowed: false, reason: "delegation_resource_denied" };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { allowed: true };
|
|
124
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const CORE_PREFIXES = [
|
|
2
|
+
"space-",
|
|
3
|
+
"space-member-",
|
|
4
|
+
"user-pref-",
|
|
5
|
+
"agent-",
|
|
6
|
+
"agent-pub-",
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
export function classifyCoreDataKey(dbKey: string): { owner: "core" | "overlay" } {
|
|
10
|
+
return CORE_PREFIXES.some((prefix) => dbKey.startsWith(prefix))
|
|
11
|
+
? { owner: "core" }
|
|
12
|
+
: { owner: "overlay" };
|
|
13
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { classifyCoreDataKey } from "./coreDataOwnership";
|
|
2
|
+
|
|
3
|
+
const normalizeOrigin = (value: string | URL | null | undefined): string => {
|
|
4
|
+
try {
|
|
5
|
+
const raw = typeof value === "string" ? value : value?.toString() ?? "";
|
|
6
|
+
return new URL(raw).origin.replace(/\/+$/, "");
|
|
7
|
+
} catch {
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const getDbKeyForOperation = (args: {
|
|
13
|
+
operation: string;
|
|
14
|
+
pathname: string;
|
|
15
|
+
req: { body?: Record<string, any> };
|
|
16
|
+
}): string | null => {
|
|
17
|
+
const { operation, pathname, req } = args;
|
|
18
|
+
switch (operation) {
|
|
19
|
+
case "read":
|
|
20
|
+
case "delete":
|
|
21
|
+
case "patch": {
|
|
22
|
+
const prefix = `/api/v1/db/${operation}/`;
|
|
23
|
+
return decodeURIComponent(pathname.slice(prefix.length));
|
|
24
|
+
}
|
|
25
|
+
case "write":
|
|
26
|
+
return typeof req.body?.customKey === "string" ? req.body.customKey : null;
|
|
27
|
+
default:
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export async function maybeProxyDatabaseRequestToCore(args: {
|
|
33
|
+
req: {
|
|
34
|
+
method?: string;
|
|
35
|
+
url?: string | URL;
|
|
36
|
+
headers?: Headers;
|
|
37
|
+
body?: Record<string, any>;
|
|
38
|
+
};
|
|
39
|
+
pathname: string;
|
|
40
|
+
search: string;
|
|
41
|
+
coreBaseUrl?: string | null;
|
|
42
|
+
fetchImpl?: typeof fetch;
|
|
43
|
+
}): Promise<Response | null> {
|
|
44
|
+
const { req, pathname, search, fetchImpl = fetch } = args;
|
|
45
|
+
const coreBaseUrl = normalizeOrigin(args.coreBaseUrl);
|
|
46
|
+
if (!coreBaseUrl) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const requestOrigin = normalizeOrigin(req.url);
|
|
51
|
+
if (requestOrigin && requestOrigin === coreBaseUrl) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const operation = pathname.slice("/api/v1/db/".length).split("/")[0] ?? "";
|
|
56
|
+
const dbKey = getDbKeyForOperation({ operation, pathname, req });
|
|
57
|
+
if (!dbKey) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (classifyCoreDataKey(dbKey).owner !== "core") {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const headers = new Headers(req.headers ?? {});
|
|
66
|
+
const init: RequestInit = {
|
|
67
|
+
method: req.method ?? "GET",
|
|
68
|
+
headers,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (req.method && !["GET", "HEAD"].includes(req.method.toUpperCase())) {
|
|
72
|
+
init.body = JSON.stringify(req.body ?? {});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return fetchImpl(`${coreBaseUrl}${pathname}${search}`, init);
|
|
76
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DataType } from "create/types";
|
|
2
|
+
|
|
3
|
+
export const CYBOT_READONLY_MESSAGE =
|
|
4
|
+
"Cybot data is read-only and cannot be modified. Please use agent data instead.";
|
|
5
|
+
|
|
6
|
+
export const isCybotKey = (dbKey: unknown): boolean =>
|
|
7
|
+
typeof dbKey === "string" &&
|
|
8
|
+
dbKey.startsWith(`${DataType.CYBOT}-`) &&
|
|
9
|
+
dbKey.length > `${DataType.CYBOT}-`.length;
|
|
10
|
+
|
|
11
|
+
export const isCybotReadOnlyMutation = (config: {
|
|
12
|
+
dbKey?: unknown;
|
|
13
|
+
dataType?: unknown;
|
|
14
|
+
existingType?: unknown;
|
|
15
|
+
}): boolean =>
|
|
16
|
+
isCybotKey(config.dbKey) ||
|
|
17
|
+
config.dataType === DataType.CYBOT ||
|
|
18
|
+
config.existingType === DataType.CYBOT;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { pino } from "pino";
|
|
2
|
+
import serverDb from "./db";
|
|
3
|
+
|
|
4
|
+
const logger = pino({ name: "dataHandlers" });
|
|
5
|
+
|
|
6
|
+
// 依赖注入:扣费函数类型,由调用方传入,避免 database → auth 循环依赖
|
|
7
|
+
type DeductBalanceFn = (userId: string, amount: number, reason: string, txId?: string) => Promise<{ success: boolean; error?: string }>;
|
|
8
|
+
|
|
9
|
+
export const handleToken = async (
|
|
10
|
+
data: any,
|
|
11
|
+
res: any,
|
|
12
|
+
userId?: string,
|
|
13
|
+
customKey?: string,
|
|
14
|
+
actionUserId?: string,
|
|
15
|
+
deductBalance?: DeductBalanceFn,
|
|
16
|
+
) => {
|
|
17
|
+
const isStatsKey = customKey.includes("token-stats");
|
|
18
|
+
await serverDb.put(customKey, data);
|
|
19
|
+
|
|
20
|
+
if (!isStatsKey && data.cost && data.cost > 0 && deductBalance) {
|
|
21
|
+
try {
|
|
22
|
+
//must change
|
|
23
|
+
const txId = `token-${customKey}`;
|
|
24
|
+
logger.info({
|
|
25
|
+
event: "token_deduct_start",
|
|
26
|
+
userId: data.userId,
|
|
27
|
+
cost: data.cost,
|
|
28
|
+
txId,
|
|
29
|
+
});
|
|
30
|
+
const deductResult = await deductBalance(
|
|
31
|
+
data.userId,
|
|
32
|
+
data.cost,
|
|
33
|
+
`Token generation cost: ${customKey}`,
|
|
34
|
+
txId
|
|
35
|
+
);
|
|
36
|
+
logger.info({
|
|
37
|
+
event: "token_deduct_result",
|
|
38
|
+
userId: data.userId,
|
|
39
|
+
cost: data.cost,
|
|
40
|
+
txId,
|
|
41
|
+
deductResult,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!deductResult.success) {
|
|
45
|
+
logger.warn({
|
|
46
|
+
event: "token_deduct_failed",
|
|
47
|
+
error: deductResult.error,
|
|
48
|
+
userId: data.userId,
|
|
49
|
+
cost: data.cost,
|
|
50
|
+
txId,
|
|
51
|
+
});
|
|
52
|
+
// next todo fix it
|
|
53
|
+
return res.status(402).json({
|
|
54
|
+
message: "Token usage recorded but payment failed",
|
|
55
|
+
error: deductResult.error,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.error({
|
|
60
|
+
event: "token_deduct_error",
|
|
61
|
+
error: error.message,
|
|
62
|
+
userId: data.userId,
|
|
63
|
+
dbKey: customKey,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return res.status(500).json({
|
|
67
|
+
message: "Token usage recorded but payment system error",
|
|
68
|
+
error: "Internal server error",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return res.status(200).json({
|
|
74
|
+
message: "Token usage recorded successfully",
|
|
75
|
+
id: customKey,
|
|
76
|
+
dbKey: customKey,
|
|
77
|
+
...data,
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const handleCybot = async (data: any, res: any, customKey: string) => {
|
|
82
|
+
try {
|
|
83
|
+
await serverDb.put(customKey, data);
|
|
84
|
+
const savedData = await serverDb.get(customKey);
|
|
85
|
+
|
|
86
|
+
const isDataMatch = JSON.stringify(data) === JSON.stringify(savedData);
|
|
87
|
+
if (!isDataMatch) {
|
|
88
|
+
logger.error({
|
|
89
|
+
event: "cybot_data_mismatch",
|
|
90
|
+
dbKey: customKey,
|
|
91
|
+
name: data.name,
|
|
92
|
+
});
|
|
93
|
+
throw new Error("Data validation failed");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return res.status(200).json({
|
|
97
|
+
message: "Data written successfully",
|
|
98
|
+
id: customKey,
|
|
99
|
+
dbKey: customKey,
|
|
100
|
+
...data,
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
logger.error({
|
|
104
|
+
event: "cybot_save_failed",
|
|
105
|
+
error: error.message,
|
|
106
|
+
dbKey: customKey,
|
|
107
|
+
name: data.name,
|
|
108
|
+
});
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// 文件路径: packages/database/server/db.ts
|
|
2
|
+
|
|
3
|
+
import { Level } from "level";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import { ensureDbOpen } from "./ensureDbOpen";
|
|
7
|
+
import { resolveServerDbPath } from "./dbPath";
|
|
8
|
+
|
|
9
|
+
const DB_PATH = resolveServerDbPath();
|
|
10
|
+
|
|
11
|
+
// 确保目录存在
|
|
12
|
+
if (!fs.existsSync(path.dirname(DB_PATH))) {
|
|
13
|
+
fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
console.log("数据库配置:");
|
|
17
|
+
console.log("- 当前工作目录:", process.cwd());
|
|
18
|
+
console.log("- 数据库路径:", DB_PATH);
|
|
19
|
+
|
|
20
|
+
// 安全 JSON 编码:解码失败时返回 null 而非抛异常,防止损坏记录导致进程崩溃
|
|
21
|
+
const safeJsonEncoding = {
|
|
22
|
+
name: "safe-json",
|
|
23
|
+
format: "utf8" as const,
|
|
24
|
+
encode: (data: any) => JSON.stringify(data),
|
|
25
|
+
decode: (data: string) => {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(data);
|
|
28
|
+
} catch {
|
|
29
|
+
console.warn("[LevelDB] Corrupted value:", data?.substring?.(0, 60));
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function isServerDbLockError(error: unknown): boolean {
|
|
36
|
+
const queue: unknown[] = [error];
|
|
37
|
+
const seen = new Set<unknown>();
|
|
38
|
+
|
|
39
|
+
while (queue.length > 0) {
|
|
40
|
+
const current = queue.shift();
|
|
41
|
+
if (!current || seen.has(current)) continue;
|
|
42
|
+
seen.add(current);
|
|
43
|
+
|
|
44
|
+
const text = `${(current as any)?.code ?? ""} ${(current as any)?.message ?? current}`;
|
|
45
|
+
if (/LEVEL_LOCKED|Resource temporarily unavailable|\/LOCK|LOCK:/.test(text)) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if ((current as any)?.cause) queue.push((current as any).cause);
|
|
50
|
+
if (Array.isArray((current as any)?.errors)) {
|
|
51
|
+
queue.push(...(current as any).errors);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 单例实例:挂在 globalThis 上,--hot 模式下模块重新求值时复用同一实例,避免 LevelDB 锁冲突
|
|
59
|
+
if (!(globalThis as any).__serverDb) {
|
|
60
|
+
(globalThis as any).__serverDb = new Level<string, any>(DB_PATH, {
|
|
61
|
+
valueEncoding: safeJsonEncoding,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const serverDb: Level<string, any> = (globalThis as any).__serverDb;
|
|
65
|
+
|
|
66
|
+
console.log("- LevelDB实际路径:", serverDb.location);
|
|
67
|
+
|
|
68
|
+
// ✅ 保留默认导出:兼容现有的 import serverDb from "./db"
|
|
69
|
+
export default serverDb;
|
|
70
|
+
|
|
71
|
+
// 具名导出:需要的时候可以引用
|
|
72
|
+
export { serverDb };
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 确保数据库处于 open 状态。
|
|
76
|
+
* - 已 open / opening:直接返回
|
|
77
|
+
* - 其他状态:尝试 open,一旦失败抛错
|
|
78
|
+
*/
|
|
79
|
+
export async function ensureServerDbOpen() {
|
|
80
|
+
const status = (serverDb as any).status;
|
|
81
|
+
if (status === "open") return;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await ensureDbOpen(serverDb);
|
|
85
|
+
if (status !== "opening") {
|
|
86
|
+
console.log("✅ LevelDB 已打开");
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
if (isServerDbLockError(err)) {
|
|
90
|
+
console.error("❌ 打开 LevelDB 失败: 数据库已被其他进程占用");
|
|
91
|
+
} else {
|
|
92
|
+
console.error("❌ 打开 LevelDB 失败:", err);
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 兼容入口调用:启动时显式 open 一次
|
|
100
|
+
*/
|
|
101
|
+
export async function openServerDb() {
|
|
102
|
+
return ensureServerDbOpen();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 优雅关机时关闭 DB
|
|
107
|
+
*/
|
|
108
|
+
export async function closeServerDb() {
|
|
109
|
+
const status = (serverDb as any).status;
|
|
110
|
+
if (status !== "open") return;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
await serverDb.close();
|
|
114
|
+
console.log("✅ LevelDB 已关闭");
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.error("❌ 关闭 LevelDB 失败:", err);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
type EnvLike = Record<string, string | undefined>;
|
|
5
|
+
|
|
6
|
+
export type ResolveServerDbPathOptions = {
|
|
7
|
+
env?: EnvLike;
|
|
8
|
+
homeDir?: string;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function resolveServerDbPath(options: ResolveServerDbPathOptions = {}) {
|
|
13
|
+
const env = options.env ?? process.env;
|
|
14
|
+
return env.NOLO_SERVER_DB_PATH?.trim() || path.join(options.cwd ?? process.cwd(), "data", "leveldb");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resolveNoloHome(options: ResolveServerDbPathOptions = {}) {
|
|
18
|
+
const env = options.env ?? process.env;
|
|
19
|
+
return env.NOLO_HOME?.trim() || path.join(options.homeDir ?? homedir(), ".nolo");
|
|
20
|
+
}
|