Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions packages/connector-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,7 @@ export type {
Checkpoint,
Content,
Env,
FeedAuthBrowserMethod,
FeedAuthEnvField,
FeedAuthEnvKeysMethod,
FeedAuthMethod,
FeedAuthNoneMethod,
FeedAuthOAuthMethod,
FeedAuthSchema,
FeedOptions,
FeedSyncResult,
IFeed,
ParentFeedDefinition,
SearchResult,
SessionState,
} from './types.js';
167 changes: 0 additions & 167 deletions packages/connector-sdk/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { TObject } from '@sinclair/typebox';

/**
* Checkpoint data structure for tracking feed sync state
*/
Expand Down Expand Up @@ -38,12 +36,6 @@ export interface FeedSyncResult {
auth_update?: Record<string, any>;
}

export interface ParentFeedDefinition {
type: string;
options: FeedOptions;
description?: string;
}

/**
* Extracted content from platform
*/
Expand Down Expand Up @@ -73,16 +65,6 @@ export interface Content {
metadata?: Record<string, any>;
}

/**
* Search result from platform search
*/
export interface SearchResult {
url: string; // Link to the resource (company page, app listing, etc.)
title: string; // Name or title of the result
description: string; // Brief description of the result
metadata?: Record<string, any>; // Structured config data (e.g., { subreddit: "spotify", content_type: "posts", requires_parent: "posts" })
}

/**
* Feed options passed from MCP tool
*/
Expand Down Expand Up @@ -175,152 +157,3 @@ export interface Env {
*/
export type SessionState = Record<string, any>;

/**
* Auth field definition for connector environment keys
*/
export interface FeedAuthEnvField {
key: string;
label?: string;
description?: string;
example?: string;
secret?: boolean;
}

export interface FeedAuthNoneMethod {
type: 'none';
}

export interface FeedAuthEnvKeysMethod {
type: 'env_keys';
required?: boolean;
scope?: 'connection' | 'organization';
fields: FeedAuthEnvField[];
description?: string;
}

export interface FeedAuthOAuthMethod {
type: 'oauth';
provider: string;
requiredScopes: string[];
optionalScopes?: string[];
required?: boolean;
scope?: 'connection' | 'organization';
description?: string;
authorizationUrl?: string;
tokenUrl?: string;
userinfoUrl?: string;
authParams?: Record<string, string>;
tokenEndpointAuthMethod?: 'client_secret_post' | 'client_secret_basic' | 'none';
usePkce?: boolean;
loginScopes?: string[];
clientIdKey?: string;
clientSecretKey?: string;
setupInstructions?: string;
loginProvisioning?: {
autoCreateConnection?: boolean;
};
}

export interface FeedAuthBrowserMethod {
type: 'browser';
required?: boolean;
description?: string;
capture?: 'cli';
}

export type FeedAuthMethod =
| FeedAuthNoneMethod
| FeedAuthEnvKeysMethod
| FeedAuthOAuthMethod
| FeedAuthBrowserMethod;

export interface FeedAuthSchema {
methods: FeedAuthMethod[];
}

/**
* Main feed interface
*/
export interface IFeed {
/**
* Unique identifier for this feed type
*/
readonly type: string;

/**
* Human-readable display name for this feed
*/
readonly displayName: string;

/**
* API type: 'api' for HTTP/REST APIs, 'browser' for browser rendering
*/
readonly apiType: 'api' | 'browser';

/**
* Feed mode: 'entity' for platforms with specific pages (repos, subreddits, companies)
* or 'search' for query-based platforms (Hacker News, Twitter search)
*/
readonly feedMode: 'entity' | 'search';

/**
* TypeBox schema for validating feed options
*/
readonly optionsSchema: TObject;

/**
* Default SQL formula to calculate normalized score (0-100)
* Can reference: f.score, f.content_length, f.metadata, f.occurred_at
* Can use window functions like PERCENT_RANK()
* User can override this per-connection via connections.scoring_formula
*/
readonly defaultScoringFormula: string;

/**
* Pull new content from platform
*/
pull(
options: FeedOptions,
checkpoint: Checkpoint | null,
env: Env,
sessionState?: SessionState | null,
updateCheckpointFn?: (checkpoint: Checkpoint) => Promise<void>
): Promise<FeedSyncResult>;

/**
* Validate feed options before saving to database
*/
validateOptions(options: FeedOptions): string | null;

/**
* Get rate limit information for this platform
*/
getRateLimit(): {
requests_per_minute: number;
requests_per_hour?: number;
recommended_interval_ms: number;
};

/**
* Search platform for entities
* Optional method - not all platforms may support search
*/
search?(searchTerm: string, env: Env): Promise<SearchResult[]>;

/**
* Generate a URL for the connection from options
*/
urlFromOptions(options: FeedOptions): string;

/**
* Generate a human-readable display label from options
*/
displayLabelFromOptions(options: FeedOptions): string;

/**
* Return parent feed definitions required to preserve hierarchy.
*/
getParentFeedDefinitions(options: FeedOptions): ParentFeedDefinition[];

readonly authSchema?: FeedAuthSchema;
}
42 changes: 0 additions & 42 deletions packages/core/src/__tests__/errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {
ConfigError,
ErrorCode,
OrchestratorError,
PlatformError,
SessionError,
WorkerError,
WorkspaceError,
} from "../errors";
Expand Down Expand Up @@ -106,46 +104,6 @@ describe("WorkspaceError", () => {
});
});

describe("PlatformError", () => {
test("stores platform and operation", () => {
const err = new PlatformError("slack", "send", "rate limited");
expect(err.name).toBe("PlatformError");
expect(err.platform).toBe("slack");
expect(err.operation).toBe("send");
expect(err.message).toBe("rate limited");
});

test("toJSON includes platform alongside base fields", () => {
const cause = new Error("429");
const err = new PlatformError("slack", "send", "rate limited", cause);
const json = err.toJSON();
expect(json.platform).toBe("slack");
expect(json.name).toBe("PlatformError");
expect(json.message).toBe("rate limited");
expect(json.operation).toBe("send");
expect(json.cause).toBe("429");
});
});

describe("SessionError", () => {
test("stores sessionKey and code", () => {
const err = new SessionError("sess-1", "EXPIRED", "session expired");
expect(err.name).toBe("SessionError");
expect(err.sessionKey).toBe("sess-1");
expect(err.code).toBe("EXPIRED");
expect(err.message).toBe("session expired");
});

test("toJSON includes sessionKey and code", () => {
const err = new SessionError("sess-1", "EXPIRED", "session expired");
const json = err.toJSON();
expect(json.sessionKey).toBe("sess-1");
expect(json.code).toBe("EXPIRED");
expect(json.name).toBe("SessionError");
expect(json.message).toBe("session expired");
});
});

describe("OrchestratorError", () => {
test("stores code, details, shouldRetry default false", () => {
const err = new OrchestratorError(
Expand Down
48 changes: 0 additions & 48 deletions packages/core/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,54 +71,6 @@ export class WorkspaceError extends BaseError {
}
}

/**
* Error class for platform-related operations (Slack, WhatsApp, etc.)
*/
export class PlatformError extends BaseError {
override readonly name = "PlatformError";

constructor(
public platform: string,
operation: string,
message: string,
cause?: Error
) {
super(message, cause);
this.operation = operation;
}

override toJSON(): Record<string, any> {
return {
...super.toJSON(),
platform: this.platform,
};
}
}

/**
* Error class for session-related operations
*/
export class SessionError extends BaseError {
readonly name = "SessionError";

constructor(
public sessionKey: string,
public code: string,
message: string,
cause?: Error
) {
super(message, cause);
}

toJSON(): Record<string, any> {
return {
...super.toJSON(),
sessionKey: this.sessionKey,
code: this.code,
};
}
}

// ErrorCode enum for orchestration operations
export enum ErrorCode {
DATABASE_CONNECTION_FAILED = "DATABASE_CONNECTION_FAILED",
Expand Down
15 changes: 0 additions & 15 deletions packages/core/src/utils/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,6 @@ export function safeJsonParse<T = unknown>(
}
}

/**
* Safely stringify value to JSON
* Returns null on stringify failure instead of throwing
*/
export function safeJsonStringify(value: unknown): string | null {
try {
return JSON.stringify(value);
} catch (error) {
logger.error("JSON stringify failed", {
error: error instanceof Error ? error.message : String(error),
});
return null;
}
}

/**
* Stringify a value to JSON, converting bigint values to numbers (when safe)
* or strings. Use this when serializing query results that may contain bigint columns.
Expand Down
33 changes: 1 addition & 32 deletions packages/openclaw-plugin/openclaw.plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,7 @@
"lobu_search_sdk",
"lobu_query_sdk",
"lobu_query_sql",
"lobu_run_sdk",
"wiki_status",
"wiki_search",
"wiki_get",
"wiki_apply",
"wiki_lint",
"memory_search",
"memory_get"
"lobu_run_sdk"
]
},
"configSchema": {
Expand Down Expand Up @@ -67,30 +60,6 @@
"type": "boolean",
"default": true,
"description": "When true, captures conversation observations as long-term memories after each agent session."
},
"memoryWikiCompat": {
"description": "Spike/compat mode: register OpenClaw memory-wiki style tools (wiki_status, wiki_search, wiki_get, wiki_apply, wiki_lint) backed by existing Lobu MCP tools. Does not write a separate wiki vault.",
"oneOf": [
{ "type": "boolean" },
{
"type": "object",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"default": false
},
"fanoutTimeoutMs": {
"type": "integer",
"minimum": 1000,
"maximum": 90000,
"default": 30000,
"description": "Per-fanout timeout (ms) for SDK-backed wiki calls. Slow paths are dropped and the tool returns partial results with a degraded marker. Defaults to 30000."
}
}
}
],
"default": false
}
}
},
Expand Down
Loading
Loading