@hasna/mementos 0.15.2 → 0.16.0
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 +75 -21
- package/dist/audit-contract.d.ts +87 -0
- package/dist/audit-contract.d.ts.map +1 -0
- package/dist/cli/commands/info-context.d.ts.map +1 -1
- package/dist/cli/commands/info-history.d.ts.map +1 -1
- package/dist/cli/commands/io-backup.d.ts.map +1 -1
- package/dist/cli/commands/io-restore.d.ts.map +1 -1
- package/dist/cli/commands/memory-cmd-list.d.ts +25 -0
- package/dist/cli/commands/memory-cmd-list.d.ts.map +1 -1
- package/dist/cli/commands/project.d.ts.map +1 -1
- package/dist/cli/commands/system-session.d.ts.map +1 -1
- package/dist/cli/commands/system-synthesized-profile.d.ts.map +1 -1
- package/dist/cli/index.js +2093 -740
- package/dist/db/api-mode.d.ts +10 -0
- package/dist/db/api-mode.d.ts.map +1 -1
- package/dist/db/api-response-contract.d.ts +23 -0
- package/dist/db/api-response-contract.d.ts.map +1 -0
- package/dist/db/audit.d.ts +34 -45
- package/dist/db/audit.d.ts.map +1 -1
- package/dist/db/database.d.ts +1 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/locks.d.ts.map +1 -1
- package/dist/db/machines.d.ts +50 -13
- package/dist/db/machines.d.ts.map +1 -1
- package/dist/db/memories.d.ts.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/pg-migrate.d.ts.map +1 -1
- package/dist/db/pg-migrations.d.ts.map +1 -1
- package/dist/db/session-jobs.d.ts +2 -0
- package/dist/db/session-jobs.d.ts.map +1 -1
- package/dist/diagnostics/historical-project-registration-receipt.js +215 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1523 -206
- package/dist/lib/built-in-hooks.d.ts.map +1 -1
- package/dist/lib/injection-project.d.ts +10 -0
- package/dist/lib/injection-project.d.ts.map +1 -0
- package/dist/lib/injector.d.ts +1 -0
- package/dist/lib/injector.d.ts.map +1 -1
- package/dist/lib/machine-visibility.d.ts.map +1 -1
- package/dist/lib/memory-lock.d.ts.map +1 -1
- package/dist/lib/profile-synthesizer.d.ts +13 -2
- package/dist/lib/profile-synthesizer.d.ts.map +1 -1
- package/dist/lib/session-queue.d.ts.map +1 -1
- package/dist/lib/session-registry.d.ts +38 -1
- package/dist/lib/session-registry.d.ts.map +1 -1
- package/dist/lib/storage-sync.d.ts +5 -0
- package/dist/lib/storage-sync.d.ts.map +1 -1
- package/dist/lib/synthesis/index.d.ts.map +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +3828 -1776
- package/dist/mcp/profile.d.ts +23 -0
- package/dist/mcp/profile.d.ts.map +1 -0
- package/dist/mcp/tools/lock-tools.d.ts.map +1 -1
- package/dist/mcp/tools/memory-audit.d.ts +1 -1
- package/dist/mcp/tools/memory-audit.d.ts.map +1 -1
- package/dist/mcp/tools/memory-crud.d.ts.map +1 -1
- package/dist/mcp/tools/memory-inject.d.ts.map +1 -1
- package/dist/mcp/tools/project-tools.d.ts.map +1 -1
- package/dist/mcp/tools/session-tools.d.ts.map +1 -1
- package/dist/mcp/tools/tool-registry.d.ts +22 -19
- package/dist/mcp/tools/tool-registry.d.ts.map +1 -1
- package/dist/mcp/tools/utility-tools.d.ts +2 -1
- package/dist/mcp/tools/utility-tools.d.ts.map +1 -1
- package/dist/project-registration.js +276 -20
- package/dist/sdk/index.d.ts +109 -6
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +740 -25
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3195 -1134
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/routes/audit.d.ts +2 -0
- package/dist/server/routes/audit.d.ts.map +1 -0
- package/dist/server/routes/machines.d.ts +2 -0
- package/dist/server/routes/machines.d.ts.map +1 -0
- package/dist/server/routes/system-sessions.d.ts.map +1 -1
- package/dist/server/routes/system-synthesis.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/hasna.contract.json +2 -2
- package/package.json +8 -5
- package/dist/cli/__fixtures__/clean-fallback-stub-server.d.ts +0 -2
- package/dist/cli/__fixtures__/clean-fallback-stub-server.d.ts.map +0 -1
- package/dist/cli/__fixtures__/io-restore-stub-server.d.ts +0 -2
- package/dist/cli/__fixtures__/io-restore-stub-server.d.ts.map +0 -1
- package/dist/db/__fixtures__/fail-closed-stub-server.d.ts +0 -2
- package/dist/db/__fixtures__/fail-closed-stub-server.d.ts.map +0 -1
- package/dist/db/__fixtures__/list-filter-capture-server.d.ts +0 -2
- package/dist/db/__fixtures__/list-filter-capture-server.d.ts.map +0 -1
- package/dist/db/__fixtures__/list-filter-client-runner.d.ts +0 -2
- package/dist/db/__fixtures__/list-filter-client-runner.d.ts.map +0 -1
- package/dist/test-support/memories-page-stub-server.d.ts +0 -2
- package/dist/test-support/memories-page-stub-server.d.ts.map +0 -1
- package/dist/test-support/memories-page-stub.d.ts +0 -28
- package/dist/test-support/memories-page-stub.d.ts.map +0 -1
- package/dist/test-support/pg-sync-stub-worker.d.ts +0 -2
- package/dist/test-support/pg-sync-stub-worker.d.ts.map +0 -1
- package/dist/test-support/preload-local-store.d.ts +0 -2
- package/dist/test-support/preload-local-store.d.ts.map +0 -1
- package/dist/test-support/project-authority-identity.d.ts +0 -8
- package/dist/test-support/project-authority-identity.d.ts.map +0 -1
- package/dist/test-support/store-isolation.d.ts +0 -147
- package/dist/test-support/store-isolation.d.ts.map +0 -1
- package/dist/test-support/strip-ansi.d.ts +0 -3
- package/dist/test-support/strip-ansi.d.ts.map +0 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ToolRegistry } from "./tools/tool-registry.js";
|
|
3
|
+
export declare const MEMENTOS_MCP_PROFILES: readonly ["core", "search", "graph", "automation", "admin", "storage", "hooks", "full"];
|
|
4
|
+
export type MementosMcpProfile = (typeof MEMENTOS_MCP_PROFILES)[number];
|
|
5
|
+
export declare const MEMENTOS_MCP_PROFILE_TOOLS: Record<Exclude<MementosMcpProfile, "full">, readonly string[]>;
|
|
6
|
+
export interface McpProfileSelection {
|
|
7
|
+
raw: string;
|
|
8
|
+
profiles: MementosMcpProfile[];
|
|
9
|
+
unknown: string[];
|
|
10
|
+
full: boolean;
|
|
11
|
+
toolNames: ReadonlySet<string>;
|
|
12
|
+
}
|
|
13
|
+
export declare function resolveMcpProfileValue(argv?: string[], env?: NodeJS.ProcessEnv): string;
|
|
14
|
+
export declare function selectMcpProfile(value: string | undefined): McpProfileSelection;
|
|
15
|
+
export declare function shouldRegisterMcpTool(name: string, selection: McpProfileSelection): boolean;
|
|
16
|
+
export declare function toolCategory(name: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Registration-only adapter: excluded tools are never added to tools/list, while
|
|
19
|
+
* every included tool is captured in the per-server discovery registry.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createProfiledMcpServer(server: McpServer, selection: McpProfileSelection, registry: ToolRegistry): McpServer;
|
|
22
|
+
export declare function legacyResourcesEnabled(selection: McpProfileSelection): boolean;
|
|
23
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/mcp/profile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,eAAO,MAAM,qBAAqB,yFASxB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAqJxE,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAQrG,CAAC;AAQF,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAChC;AASD,wBAAgB,sBAAsB,CACpC,IAAI,GAAE,MAAM,EAAiB,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAgBR;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,mBAAmB,CA8B/E;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAE3F;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiBjD;AAcD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,mBAAmB,EAC9B,QAAQ,EAAE,YAAY,GACrB,SAAS,CAwBX;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAE9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/lock-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"lock-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/lock-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6BpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0NzD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-audit.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/memory-audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-audit.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/memory-audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAuCzE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAuFhE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-crud.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/memory-crud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-crud.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/memory-crud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA+FpE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAub/D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-inject.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/memory-inject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-inject.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/memory-inject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAcpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8UjE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/project-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"project-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/project-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAiCpE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkL5D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/session-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAepE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"session-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/session-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAepE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkL5D"}
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* for tool discovery and documentation.
|
|
5
|
-
*/
|
|
6
|
-
export interface ToolSchema {
|
|
1
|
+
/** Per-server MCP tool discovery registry used by search_tools/describe_tools. */
|
|
2
|
+
export interface ToolParamSchema {
|
|
3
|
+
type: string;
|
|
7
4
|
description: string;
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
required?: boolean;
|
|
6
|
+
enum?: string[];
|
|
7
|
+
items?: {
|
|
10
8
|
type: string;
|
|
11
|
-
description: string;
|
|
12
|
-
required?: boolean;
|
|
13
9
|
enum?: string[];
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface ToolSchema {
|
|
13
|
+
name?: string;
|
|
14
|
+
description: string;
|
|
15
|
+
category: string;
|
|
16
|
+
params: Record<string, ToolParamSchema>;
|
|
19
17
|
example?: string;
|
|
20
18
|
}
|
|
21
19
|
export interface ToolEntry {
|
|
@@ -23,8 +21,13 @@ export interface ToolEntry {
|
|
|
23
21
|
description: string;
|
|
24
22
|
category: string;
|
|
25
23
|
}
|
|
26
|
-
export declare
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
export declare class ToolRegistry {
|
|
25
|
+
private readonly registry;
|
|
26
|
+
registerToolSchemas(schemas: Record<string, ToolSchema>): void;
|
|
27
|
+
registerDiscoveredTool(name: string, description: string, category: string, inputShape?: Record<string, unknown>): void;
|
|
28
|
+
retain(names: ReadonlySet<string>): void;
|
|
29
|
+
getAllToolEntries(): ToolEntry[];
|
|
30
|
+
getToolSchema(name: string): ToolSchema | undefined;
|
|
31
|
+
searchToolEntries(query: string, category?: string): ToolEntry[];
|
|
32
|
+
}
|
|
30
33
|
//# sourceMappingURL=tool-registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/tool-registry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/tool-registry.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAC3C;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuCD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAE1D,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI;IAM9D,sBAAsB,CACpB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,IAAI;IAeP,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI;IAMxC,iBAAiB,IAAI,SAAS,EAAE;IAMhC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAInD,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE;CAOjE"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
|
|
2
|
+
import { ToolRegistry } from "./tool-registry.js";
|
|
3
|
+
export declare function registerUtilityTools(server: McpServer, registry: ToolRegistry): void;
|
|
3
4
|
//# sourceMappingURL=utility-tools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utility-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/utility-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"utility-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/utility-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAiBpE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAoFlD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAqUpF"}
|
|
@@ -1239,6 +1239,8 @@ function getApiConfig(env2 = process.env, options = {}) {
|
|
|
1239
1239
|
function isApiMode() {
|
|
1240
1240
|
if (hasDatabaseUrl())
|
|
1241
1241
|
return false;
|
|
1242
|
+
if (isServerContext())
|
|
1243
|
+
return false;
|
|
1242
1244
|
return getApiConfig() !== null;
|
|
1243
1245
|
}
|
|
1244
1246
|
function assertClientStoreConfigured(env2 = process.env, options = {}) {
|
|
@@ -1285,7 +1287,14 @@ x-api-key: ${cfg.apiKey}
|
|
|
1285
1287
|
let bodyFile;
|
|
1286
1288
|
if (hasBody) {
|
|
1287
1289
|
bodyFile = join3(tmpdir(), `mem-req-${process.pid}-${randomUUID()}.json`);
|
|
1288
|
-
|
|
1290
|
+
try {
|
|
1291
|
+
writeFileSync2(bodyFile, JSON.stringify(body), { mode: 384 });
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
try {
|
|
1294
|
+
unlinkSync(bodyFile);
|
|
1295
|
+
} catch {}
|
|
1296
|
+
throw error;
|
|
1297
|
+
}
|
|
1289
1298
|
args.push("--data-binary", `@${bodyFile}`);
|
|
1290
1299
|
}
|
|
1291
1300
|
args.push(url);
|
|
@@ -1333,7 +1342,7 @@ function apiJson(method, path, body, options) {
|
|
|
1333
1342
|
try {
|
|
1334
1343
|
data = JSON.parse(raw.body);
|
|
1335
1344
|
} catch (e) {
|
|
1336
|
-
throw new ApiRequestError(`mementos cloud ${method} ${path} returned status ${raw.status} with a body that is not valid JSON (${e instanceof Error ? e.message : String(e)}) \u2014 the response is truncated or the server is unhealthy`, raw.status,
|
|
1345
|
+
throw new ApiRequestError(`mementos cloud ${method} ${path} returned status ${raw.status} with a body that is not valid JSON (${e instanceof Error ? e.message : String(e)}) \u2014 the response is truncated or the server is unhealthy`, raw.status, "");
|
|
1337
1346
|
}
|
|
1338
1347
|
} else {
|
|
1339
1348
|
data = undefined;
|
|
@@ -1375,6 +1384,7 @@ function toQuery(params) {
|
|
|
1375
1384
|
var API_URL_ENV_KEYS, API_KEY_ENV_KEYS, DATABASE_URL_ENV_KEYS, DB_PATH_ENV_KEYS, ALLOW_REMOTE_API_IN_TESTS_ENV = "MEMENTOS_ALLOW_REMOTE_API_IN_TESTS", MementosStoreConfigError, ApiRequestError, DEFAULT_TIMEOUT_S = "45";
|
|
1376
1385
|
var init_api_mode = __esm(() => {
|
|
1377
1386
|
init_local_opt_in();
|
|
1387
|
+
init_storage();
|
|
1378
1388
|
API_URL_ENV_KEYS = clientTransportEnvKeys2("mementos").apiUrlKeys;
|
|
1379
1389
|
API_KEY_ENV_KEYS = clientTransportEnvKeys2("mementos").apiKeyKeys;
|
|
1380
1390
|
DATABASE_URL_ENV_KEYS = ["HASNA_MEMENTOS_DATABASE_URL", "MEMENTOS_DATABASE_URL"];
|
|
@@ -2787,6 +2797,220 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (38);
|
|
|
2787
2797
|
`
|
|
2788
2798
|
${sqliteMementosMemoryProjectLinkSchemaSql()}
|
|
2789
2799
|
INSERT OR IGNORE INTO _migrations (id) VALUES (40);
|
|
2800
|
+
`,
|
|
2801
|
+
`
|
|
2802
|
+
DROP TABLE IF EXISTS temp.mementos_m41_machine_preflight;
|
|
2803
|
+
CREATE TEMP TABLE mementos_m41_machine_preflight (
|
|
2804
|
+
id TEXT PRIMARY KEY,
|
|
2805
|
+
name TEXT NOT NULL CHECK(
|
|
2806
|
+
length(name) BETWEEN 1 AND 128
|
|
2807
|
+
AND instr(name, char(0)) = 0
|
|
2808
|
+
AND instr(name, char(1)) = 0
|
|
2809
|
+
AND instr(name, char(2)) = 0
|
|
2810
|
+
AND instr(name, char(3)) = 0
|
|
2811
|
+
AND instr(name, char(4)) = 0
|
|
2812
|
+
AND instr(name, char(5)) = 0
|
|
2813
|
+
AND instr(name, char(6)) = 0
|
|
2814
|
+
AND instr(name, char(7)) = 0
|
|
2815
|
+
AND instr(name, char(8)) = 0
|
|
2816
|
+
AND instr(name, char(9)) = 0
|
|
2817
|
+
AND instr(name, char(10)) = 0
|
|
2818
|
+
AND instr(name, char(11)) = 0
|
|
2819
|
+
AND instr(name, char(12)) = 0
|
|
2820
|
+
AND instr(name, char(13)) = 0
|
|
2821
|
+
AND instr(name, char(14)) = 0
|
|
2822
|
+
AND instr(name, char(15)) = 0
|
|
2823
|
+
AND instr(name, char(16)) = 0
|
|
2824
|
+
AND instr(name, char(17)) = 0
|
|
2825
|
+
AND instr(name, char(18)) = 0
|
|
2826
|
+
AND instr(name, char(19)) = 0
|
|
2827
|
+
AND instr(name, char(20)) = 0
|
|
2828
|
+
AND instr(name, char(21)) = 0
|
|
2829
|
+
AND instr(name, char(22)) = 0
|
|
2830
|
+
AND instr(name, char(23)) = 0
|
|
2831
|
+
AND instr(name, char(24)) = 0
|
|
2832
|
+
AND instr(name, char(25)) = 0
|
|
2833
|
+
AND instr(name, char(26)) = 0
|
|
2834
|
+
AND instr(name, char(27)) = 0
|
|
2835
|
+
AND instr(name, char(28)) = 0
|
|
2836
|
+
AND instr(name, char(29)) = 0
|
|
2837
|
+
AND instr(name, char(30)) = 0
|
|
2838
|
+
AND instr(name, char(31)) = 0
|
|
2839
|
+
AND instr(name, char(127)) = 0
|
|
2840
|
+
),
|
|
2841
|
+
hostname TEXT NOT NULL UNIQUE CHECK(
|
|
2842
|
+
length(hostname) BETWEEN 1 AND 253
|
|
2843
|
+
AND hostname = lower(rtrim(trim(hostname), '.'))
|
|
2844
|
+
AND instr(hostname, ' ') = 0
|
|
2845
|
+
AND instr(hostname, char(9)) = 0
|
|
2846
|
+
AND instr(hostname, char(10)) = 0
|
|
2847
|
+
AND instr(hostname, char(13)) = 0
|
|
2848
|
+
AND instr(hostname, '/') = 0
|
|
2849
|
+
AND instr(hostname, char(92)) = 0
|
|
2850
|
+
),
|
|
2851
|
+
platform TEXT NOT NULL CHECK(
|
|
2852
|
+
length(platform) BETWEEN 1 AND 64
|
|
2853
|
+
AND platform = lower(trim(platform))
|
|
2854
|
+
AND platform NOT GLOB '*[^a-z0-9._-]*'
|
|
2855
|
+
),
|
|
2856
|
+
is_primary INTEGER NOT NULL CHECK(is_primary IN (0, 1)),
|
|
2857
|
+
created_at TEXT NOT NULL CHECK(julianday(created_at) IS NOT NULL),
|
|
2858
|
+
last_seen_at TEXT NOT NULL CHECK(
|
|
2859
|
+
julianday(last_seen_at) IS NOT NULL
|
|
2860
|
+
AND julianday(last_seen_at) >= julianday(created_at)
|
|
2861
|
+
)
|
|
2862
|
+
);
|
|
2863
|
+
INSERT INTO mementos_m41_machine_preflight
|
|
2864
|
+
(id, name, hostname, platform, is_primary, created_at, last_seen_at)
|
|
2865
|
+
SELECT
|
|
2866
|
+
id,
|
|
2867
|
+
name,
|
|
2868
|
+
lower(rtrim(trim(hostname), '.')),
|
|
2869
|
+
platform,
|
|
2870
|
+
is_primary,
|
|
2871
|
+
created_at,
|
|
2872
|
+
last_seen_at
|
|
2873
|
+
FROM machines;
|
|
2874
|
+
|
|
2875
|
+
UPDATE machines
|
|
2876
|
+
SET hostname = (
|
|
2877
|
+
SELECT preflight.hostname
|
|
2878
|
+
FROM mementos_m41_machine_preflight preflight
|
|
2879
|
+
WHERE preflight.id = machines.id
|
|
2880
|
+
);
|
|
2881
|
+
|
|
2882
|
+
DROP TABLE mementos_m41_machine_preflight;
|
|
2883
|
+
DROP TRIGGER IF EXISTS machines_single_primary_insert;
|
|
2884
|
+
DROP TRIGGER IF EXISTS machines_single_primary_update;
|
|
2885
|
+
DROP INDEX IF EXISTS idx_machines_hostname;
|
|
2886
|
+
CREATE UNIQUE INDEX idx_machines_hostname ON machines(hostname);
|
|
2887
|
+
DROP INDEX IF EXISTS idx_machines_single_primary;
|
|
2888
|
+
CREATE UNIQUE INDEX idx_machines_single_primary
|
|
2889
|
+
ON machines(is_primary) WHERE is_primary = 1;
|
|
2890
|
+
|
|
2891
|
+
CREATE TRIGGER IF NOT EXISTS machines_hostname_canonical_insert
|
|
2892
|
+
BEFORE INSERT ON machines
|
|
2893
|
+
WHEN NEW.hostname != lower(rtrim(trim(NEW.hostname), '.'))
|
|
2894
|
+
OR length(NEW.hostname) NOT BETWEEN 1 AND 253
|
|
2895
|
+
OR instr(NEW.hostname, ' ') > 0
|
|
2896
|
+
OR instr(NEW.hostname, char(9)) > 0
|
|
2897
|
+
OR instr(NEW.hostname, char(10)) > 0
|
|
2898
|
+
OR instr(NEW.hostname, char(13)) > 0
|
|
2899
|
+
OR instr(NEW.hostname, '/') > 0
|
|
2900
|
+
OR instr(NEW.hostname, char(92)) > 0
|
|
2901
|
+
BEGIN
|
|
2902
|
+
SELECT RAISE(ABORT, 'machines.hostname must be canonical');
|
|
2903
|
+
END;
|
|
2904
|
+
|
|
2905
|
+
CREATE TRIGGER IF NOT EXISTS machines_hostname_canonical_update
|
|
2906
|
+
BEFORE UPDATE OF hostname ON machines
|
|
2907
|
+
WHEN NEW.hostname != lower(rtrim(trim(NEW.hostname), '.'))
|
|
2908
|
+
OR length(NEW.hostname) NOT BETWEEN 1 AND 253
|
|
2909
|
+
OR instr(NEW.hostname, ' ') > 0
|
|
2910
|
+
OR instr(NEW.hostname, char(9)) > 0
|
|
2911
|
+
OR instr(NEW.hostname, char(10)) > 0
|
|
2912
|
+
OR instr(NEW.hostname, char(13)) > 0
|
|
2913
|
+
OR instr(NEW.hostname, '/') > 0
|
|
2914
|
+
OR instr(NEW.hostname, char(92)) > 0
|
|
2915
|
+
BEGIN
|
|
2916
|
+
SELECT RAISE(ABORT, 'machines.hostname must be canonical');
|
|
2917
|
+
END;
|
|
2918
|
+
|
|
2919
|
+
CREATE TRIGGER IF NOT EXISTS machines_runtime_contract_insert
|
|
2920
|
+
BEFORE INSERT ON machines
|
|
2921
|
+
WHEN length(NEW.name) NOT BETWEEN 1 AND 128
|
|
2922
|
+
OR instr(NEW.name, char(0)) > 0
|
|
2923
|
+
OR instr(NEW.name, char(1)) > 0
|
|
2924
|
+
OR instr(NEW.name, char(2)) > 0
|
|
2925
|
+
OR instr(NEW.name, char(3)) > 0
|
|
2926
|
+
OR instr(NEW.name, char(4)) > 0
|
|
2927
|
+
OR instr(NEW.name, char(5)) > 0
|
|
2928
|
+
OR instr(NEW.name, char(6)) > 0
|
|
2929
|
+
OR instr(NEW.name, char(7)) > 0
|
|
2930
|
+
OR instr(NEW.name, char(8)) > 0
|
|
2931
|
+
OR instr(NEW.name, char(9)) > 0
|
|
2932
|
+
OR instr(NEW.name, char(10)) > 0
|
|
2933
|
+
OR instr(NEW.name, char(11)) > 0
|
|
2934
|
+
OR instr(NEW.name, char(12)) > 0
|
|
2935
|
+
OR instr(NEW.name, char(13)) > 0
|
|
2936
|
+
OR instr(NEW.name, char(14)) > 0
|
|
2937
|
+
OR instr(NEW.name, char(15)) > 0
|
|
2938
|
+
OR instr(NEW.name, char(16)) > 0
|
|
2939
|
+
OR instr(NEW.name, char(17)) > 0
|
|
2940
|
+
OR instr(NEW.name, char(18)) > 0
|
|
2941
|
+
OR instr(NEW.name, char(19)) > 0
|
|
2942
|
+
OR instr(NEW.name, char(20)) > 0
|
|
2943
|
+
OR instr(NEW.name, char(21)) > 0
|
|
2944
|
+
OR instr(NEW.name, char(22)) > 0
|
|
2945
|
+
OR instr(NEW.name, char(23)) > 0
|
|
2946
|
+
OR instr(NEW.name, char(24)) > 0
|
|
2947
|
+
OR instr(NEW.name, char(25)) > 0
|
|
2948
|
+
OR instr(NEW.name, char(26)) > 0
|
|
2949
|
+
OR instr(NEW.name, char(27)) > 0
|
|
2950
|
+
OR instr(NEW.name, char(28)) > 0
|
|
2951
|
+
OR instr(NEW.name, char(29)) > 0
|
|
2952
|
+
OR instr(NEW.name, char(30)) > 0
|
|
2953
|
+
OR instr(NEW.name, char(31)) > 0
|
|
2954
|
+
OR instr(NEW.name, char(127)) > 0
|
|
2955
|
+
OR length(NEW.platform) NOT BETWEEN 1 AND 64
|
|
2956
|
+
OR NEW.platform != lower(trim(NEW.platform))
|
|
2957
|
+
OR NEW.platform GLOB '*[^a-z0-9._-]*'
|
|
2958
|
+
OR NEW.is_primary NOT IN (0, 1)
|
|
2959
|
+
OR julianday(NEW.created_at) IS NULL
|
|
2960
|
+
OR julianday(NEW.last_seen_at) IS NULL
|
|
2961
|
+
OR julianday(NEW.last_seen_at) < julianday(NEW.created_at)
|
|
2962
|
+
BEGIN
|
|
2963
|
+
SELECT RAISE(ABORT, 'machines row violates the persisted runtime contract');
|
|
2964
|
+
END;
|
|
2965
|
+
|
|
2966
|
+
CREATE TRIGGER IF NOT EXISTS machines_runtime_contract_update
|
|
2967
|
+
BEFORE UPDATE OF name, platform, is_primary, created_at, last_seen_at ON machines
|
|
2968
|
+
WHEN length(NEW.name) NOT BETWEEN 1 AND 128
|
|
2969
|
+
OR instr(NEW.name, char(0)) > 0
|
|
2970
|
+
OR instr(NEW.name, char(1)) > 0
|
|
2971
|
+
OR instr(NEW.name, char(2)) > 0
|
|
2972
|
+
OR instr(NEW.name, char(3)) > 0
|
|
2973
|
+
OR instr(NEW.name, char(4)) > 0
|
|
2974
|
+
OR instr(NEW.name, char(5)) > 0
|
|
2975
|
+
OR instr(NEW.name, char(6)) > 0
|
|
2976
|
+
OR instr(NEW.name, char(7)) > 0
|
|
2977
|
+
OR instr(NEW.name, char(8)) > 0
|
|
2978
|
+
OR instr(NEW.name, char(9)) > 0
|
|
2979
|
+
OR instr(NEW.name, char(10)) > 0
|
|
2980
|
+
OR instr(NEW.name, char(11)) > 0
|
|
2981
|
+
OR instr(NEW.name, char(12)) > 0
|
|
2982
|
+
OR instr(NEW.name, char(13)) > 0
|
|
2983
|
+
OR instr(NEW.name, char(14)) > 0
|
|
2984
|
+
OR instr(NEW.name, char(15)) > 0
|
|
2985
|
+
OR instr(NEW.name, char(16)) > 0
|
|
2986
|
+
OR instr(NEW.name, char(17)) > 0
|
|
2987
|
+
OR instr(NEW.name, char(18)) > 0
|
|
2988
|
+
OR instr(NEW.name, char(19)) > 0
|
|
2989
|
+
OR instr(NEW.name, char(20)) > 0
|
|
2990
|
+
OR instr(NEW.name, char(21)) > 0
|
|
2991
|
+
OR instr(NEW.name, char(22)) > 0
|
|
2992
|
+
OR instr(NEW.name, char(23)) > 0
|
|
2993
|
+
OR instr(NEW.name, char(24)) > 0
|
|
2994
|
+
OR instr(NEW.name, char(25)) > 0
|
|
2995
|
+
OR instr(NEW.name, char(26)) > 0
|
|
2996
|
+
OR instr(NEW.name, char(27)) > 0
|
|
2997
|
+
OR instr(NEW.name, char(28)) > 0
|
|
2998
|
+
OR instr(NEW.name, char(29)) > 0
|
|
2999
|
+
OR instr(NEW.name, char(30)) > 0
|
|
3000
|
+
OR instr(NEW.name, char(31)) > 0
|
|
3001
|
+
OR instr(NEW.name, char(127)) > 0
|
|
3002
|
+
OR length(NEW.platform) NOT BETWEEN 1 AND 64
|
|
3003
|
+
OR NEW.platform != lower(trim(NEW.platform))
|
|
3004
|
+
OR NEW.platform GLOB '*[^a-z0-9._-]*'
|
|
3005
|
+
OR NEW.is_primary NOT IN (0, 1)
|
|
3006
|
+
OR julianday(NEW.created_at) IS NULL
|
|
3007
|
+
OR julianday(NEW.last_seen_at) IS NULL
|
|
3008
|
+
OR julianday(NEW.last_seen_at) < julianday(NEW.created_at)
|
|
3009
|
+
BEGIN
|
|
3010
|
+
SELECT RAISE(ABORT, 'machines row violates the persisted runtime contract');
|
|
3011
|
+
END;
|
|
3012
|
+
|
|
3013
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (41);
|
|
2790
3014
|
`
|
|
2791
3015
|
];
|
|
2792
3016
|
});
|
|
@@ -2802,7 +3026,8 @@ __export(exports_database, {
|
|
|
2802
3026
|
getDbPath: () => getDbPath,
|
|
2803
3027
|
getDatabase: () => getDatabase,
|
|
2804
3028
|
escapeLikePrefix: () => escapeLikePrefix,
|
|
2805
|
-
closeDatabase: () => closeDatabase
|
|
3029
|
+
closeDatabase: () => closeDatabase,
|
|
3030
|
+
applyMachineIdentityMigration: () => applyMachineIdentityMigration
|
|
2806
3031
|
});
|
|
2807
3032
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, cpSync } from "fs";
|
|
2808
3033
|
import { dirname, join as join4, resolve as resolve2 } from "path";
|
|
@@ -2930,32 +3155,62 @@ function getDatabase(dbPath) {
|
|
|
2930
3155
|
return _db;
|
|
2931
3156
|
}
|
|
2932
3157
|
function runMigrations(db) {
|
|
3158
|
+
let currentLevel = 0;
|
|
2933
3159
|
try {
|
|
2934
3160
|
const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
for (let i = 0;i < MIGRATIONS.length; i++) {
|
|
2945
|
-
try {
|
|
2946
|
-
applyMigration(db, i);
|
|
2947
|
-
} catch (e) {
|
|
2948
|
-
console.warn(`[mementos] Migration ${i + 1} failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
3161
|
+
currentLevel = result?.max_id ?? 0;
|
|
3162
|
+
} catch {}
|
|
3163
|
+
for (let i = currentLevel;i < MIGRATIONS.length; i++) {
|
|
3164
|
+
try {
|
|
3165
|
+
applyMigration(db, i);
|
|
3166
|
+
} catch (error) {
|
|
3167
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3168
|
+
if (i === 40) {
|
|
3169
|
+
throw new Error(`Mementos migration 41 refused unsafe machine identity data: ${message}`);
|
|
2949
3170
|
}
|
|
3171
|
+
console.warn(`[mementos] Migration ${i + 1} failed: ${message}`);
|
|
2950
3172
|
}
|
|
2951
3173
|
}
|
|
2952
3174
|
}
|
|
2953
3175
|
function applyMigration(db, index) {
|
|
2954
|
-
if (index === 32)
|
|
3176
|
+
if (index === 32)
|
|
2955
3177
|
ensureMachinePrimaryColumn(db);
|
|
3178
|
+
if (index === 40) {
|
|
3179
|
+
applyMachineIdentityMigration(db);
|
|
3180
|
+
return;
|
|
2956
3181
|
}
|
|
2957
3182
|
db.exec(MIGRATIONS[index]);
|
|
2958
3183
|
}
|
|
3184
|
+
function applyMachineIdentityMigration(db) {
|
|
3185
|
+
db.transaction(() => {
|
|
3186
|
+
const rows = db.query("SELECT id, name, hostname, platform, is_primary, created_at, last_seen_at FROM machines").all();
|
|
3187
|
+
const identities = new Map;
|
|
3188
|
+
let primaryCount = 0;
|
|
3189
|
+
for (const row of rows) {
|
|
3190
|
+
const canonical = row.hostname.trim().replace(/\.+$/, "").toLowerCase();
|
|
3191
|
+
const createdAt = new Date(row.created_at).getTime();
|
|
3192
|
+
const lastSeenAt = new Date(row.last_seen_at).getTime();
|
|
3193
|
+
const primaryValueValid = row.is_primary === 0 || row.is_primary === 1 || row.is_primary === false || row.is_primary === true;
|
|
3194
|
+
if (!row.id || !row.name || row.name.length > 128 || /[\u0000-\u001f\u007f]/.test(row.name) || !canonical || canonical.length > 253 || /[\u0000-\u001f\u007f/\\\s]/.test(canonical) || !row.platform || row.platform.length > 64 || row.platform !== row.platform.trim().toLowerCase() || !/^[a-z0-9._-]+$/.test(row.platform) || !primaryValueValid || !Number.isFinite(createdAt) || !Number.isFinite(lastSeenAt) || lastSeenAt < createdAt) {
|
|
3195
|
+
throw new Error(`invalid legacy machine row ${row.id || "(missing id)"}`);
|
|
3196
|
+
}
|
|
3197
|
+
const owner = identities.get(canonical);
|
|
3198
|
+
if (owner && owner !== row.id) {
|
|
3199
|
+
throw new Error(`ambiguous normalized hostname '${canonical}' belongs to both ${owner} and ${row.id}`);
|
|
3200
|
+
}
|
|
3201
|
+
identities.set(canonical, row.id);
|
|
3202
|
+
if (Boolean(row.is_primary))
|
|
3203
|
+
primaryCount += 1;
|
|
3204
|
+
}
|
|
3205
|
+
if (primaryCount > 1) {
|
|
3206
|
+
throw new Error(`legacy machine registry contains ${primaryCount} primary rows`);
|
|
3207
|
+
}
|
|
3208
|
+
db.exec(MIGRATIONS[40]);
|
|
3209
|
+
const receipt = db.query("SELECT id FROM _migrations WHERE id = 41").get();
|
|
3210
|
+
if (receipt?.id !== 41)
|
|
3211
|
+
throw new Error("machine identity migration did not record completion");
|
|
3212
|
+
});
|
|
3213
|
+
}
|
|
2959
3214
|
function ensureMachinePrimaryColumn(db) {
|
|
2960
3215
|
const columns = db.query("PRAGMA table_info(machines)").all();
|
|
2961
3216
|
if (columns.length === 0 || columns.some((column) => column.name === "is_primary")) {
|
|
@@ -5907,7 +6162,7 @@ function buildMemoryListConditions(filter) {
|
|
|
5907
6162
|
conditions.push("status = 'active'");
|
|
5908
6163
|
}
|
|
5909
6164
|
if (filter.project_id) {
|
|
5910
|
-
conditions.push("project_id = ?");
|
|
6165
|
+
conditions.push(filter.include_unassigned_project === true ? "(project_id = ? OR project_id IS NULL)" : "project_id = ?");
|
|
5911
6166
|
params.push(filter.project_id);
|
|
5912
6167
|
}
|
|
5913
6168
|
if (filter.agent_id) {
|
|
@@ -5987,6 +6242,7 @@ function listMemoriesPage(filter, db) {
|
|
|
5987
6242
|
pinned: f.pinned,
|
|
5988
6243
|
agent_id: f.agent_id,
|
|
5989
6244
|
project_id: f.project_id,
|
|
6245
|
+
include_unassigned_project: f.project_id && f.include_unassigned_project === true ? true : undefined,
|
|
5990
6246
|
session_id: f.session_id,
|
|
5991
6247
|
machine_id: f.machine_id,
|
|
5992
6248
|
visible_to_machine_id: f.visible_to_machine_id,
|
|
@@ -6011,7 +6267,7 @@ function listMemoriesPage(filter, db) {
|
|
|
6011
6267
|
if (conditions.length > 0) {
|
|
6012
6268
|
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
6013
6269
|
}
|
|
6014
|
-
sql += " ORDER BY importance DESC, created_at DESC";
|
|
6270
|
+
sql += " ORDER BY importance DESC, created_at DESC, id DESC";
|
|
6015
6271
|
if (f.limit) {
|
|
6016
6272
|
sql += " LIMIT ?";
|
|
6017
6273
|
params.push(f.limit);
|