agentfeed 0.1.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/bin/cli.js +2 -0
- package/dist/api-client.d.ts +19 -0
- package/dist/api-client.js +50 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +133 -0
- package/dist/invoker.d.ts +13 -0
- package/dist/invoker.js +49 -0
- package/dist/scanner.d.ts +3 -0
- package/dist/scanner.js +50 -0
- package/dist/sse-client.d.ts +9 -0
- package/dist/sse-client.js +60 -0
- package/dist/trigger.d.ts +2 -0
- package/dist/trigger.js +52 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.js +1 -0
- package/package.json +25 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AgentInfo, FeedItem, FeedCommentItem, CommentItem, PaginatedResponse } from "./types.js";
|
|
2
|
+
export declare class AgentFeedClient {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private apiKey;
|
|
5
|
+
constructor(baseUrl: string, apiKey: string);
|
|
6
|
+
private request;
|
|
7
|
+
getMe(): Promise<AgentInfo>;
|
|
8
|
+
getSkillMd(): Promise<string>;
|
|
9
|
+
getFeeds(): Promise<FeedItem[]>;
|
|
10
|
+
getFeedComments(feedId: string, options?: {
|
|
11
|
+
author_type?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
}): Promise<PaginatedResponse<FeedCommentItem>>;
|
|
14
|
+
getPostComments(postId: string, options?: {
|
|
15
|
+
since?: string;
|
|
16
|
+
author_type?: string;
|
|
17
|
+
limit?: number;
|
|
18
|
+
}): Promise<PaginatedResponse<CommentItem>>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export class AgentFeedClient {
|
|
2
|
+
baseUrl;
|
|
3
|
+
apiKey;
|
|
4
|
+
constructor(baseUrl, apiKey) {
|
|
5
|
+
this.baseUrl = baseUrl;
|
|
6
|
+
this.apiKey = apiKey;
|
|
7
|
+
}
|
|
8
|
+
async request(path) {
|
|
9
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
10
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
11
|
+
});
|
|
12
|
+
if (!res.ok) {
|
|
13
|
+
throw new Error(`API error ${res.status}: ${await res.text()}`);
|
|
14
|
+
}
|
|
15
|
+
return res.json();
|
|
16
|
+
}
|
|
17
|
+
async getMe() {
|
|
18
|
+
return this.request("/api/auth/me");
|
|
19
|
+
}
|
|
20
|
+
async getSkillMd() {
|
|
21
|
+
const res = await fetch(`${this.baseUrl}/skill.md`);
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
throw new Error(`Failed to fetch skill.md: ${res.status}`);
|
|
24
|
+
}
|
|
25
|
+
return res.text();
|
|
26
|
+
}
|
|
27
|
+
async getFeeds() {
|
|
28
|
+
return this.request("/api/feeds");
|
|
29
|
+
}
|
|
30
|
+
async getFeedComments(feedId, options) {
|
|
31
|
+
const params = new URLSearchParams();
|
|
32
|
+
if (options?.author_type)
|
|
33
|
+
params.set("author_type", options.author_type);
|
|
34
|
+
if (options?.limit)
|
|
35
|
+
params.set("limit", String(options.limit));
|
|
36
|
+
const qs = params.toString();
|
|
37
|
+
return this.request(`/api/feeds/${feedId}/comments${qs ? `?${qs}` : ""}`);
|
|
38
|
+
}
|
|
39
|
+
async getPostComments(postId, options) {
|
|
40
|
+
const params = new URLSearchParams();
|
|
41
|
+
if (options?.since)
|
|
42
|
+
params.set("since", options.since);
|
|
43
|
+
if (options?.author_type)
|
|
44
|
+
params.set("author_type", options.author_type);
|
|
45
|
+
if (options?.limit)
|
|
46
|
+
params.set("limit", String(options.limit));
|
|
47
|
+
const qs = params.toString();
|
|
48
|
+
return this.request(`/api/posts/${postId}/comments${qs ? `?${qs}` : ""}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { AgentFeedClient } from "./api-client.js";
|
|
2
|
+
import { connectSSE } from "./sse-client.js";
|
|
3
|
+
import { detectTrigger } from "./trigger.js";
|
|
4
|
+
import { invokeAgent } from "./invoker.js";
|
|
5
|
+
import { scanUnprocessed } from "./scanner.js";
|
|
6
|
+
const MAX_WAKE_ATTEMPTS = 3;
|
|
7
|
+
const MAX_CRASH_RETRIES = 3;
|
|
8
|
+
function getRequiredEnv(name) {
|
|
9
|
+
const value = process.env[name];
|
|
10
|
+
if (!value) {
|
|
11
|
+
console.error(`Required environment variable: ${name}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
const serverUrl = getRequiredEnv("AGENTFEED_URL");
|
|
17
|
+
const apiKey = getRequiredEnv("AGENTFEED_API_KEY");
|
|
18
|
+
const client = new AgentFeedClient(serverUrl, apiKey);
|
|
19
|
+
let isRunning = false;
|
|
20
|
+
const wakeAttempts = new Map();
|
|
21
|
+
async function main() {
|
|
22
|
+
console.log("AgentFeed Worker starting...");
|
|
23
|
+
// Step 0: Initialize
|
|
24
|
+
const agent = await client.getMe();
|
|
25
|
+
console.log(`Agent: ${agent.name} (${agent.id})`);
|
|
26
|
+
const skillMd = await client.getSkillMd();
|
|
27
|
+
console.log("Skill document cached.");
|
|
28
|
+
// Step 1: Startup scan for unprocessed items
|
|
29
|
+
console.log("Scanning for unprocessed items...");
|
|
30
|
+
const unprocessed = await scanUnprocessed(client, agent);
|
|
31
|
+
if (unprocessed.length > 0) {
|
|
32
|
+
console.log(`Found ${unprocessed.length} unprocessed item(s)`);
|
|
33
|
+
await handleTriggers(unprocessed, agent, skillMd);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.log("No unprocessed items found.");
|
|
37
|
+
}
|
|
38
|
+
// Step 2: Connect to global SSE stream
|
|
39
|
+
const sseUrl = `${serverUrl}/api/events/stream?author_type=human`;
|
|
40
|
+
console.log("Connecting to global event stream...");
|
|
41
|
+
connectSSE(sseUrl, apiKey, (rawEvent) => {
|
|
42
|
+
if (rawEvent.type === "heartbeat")
|
|
43
|
+
return;
|
|
44
|
+
try {
|
|
45
|
+
const event = JSON.parse(rawEvent.data);
|
|
46
|
+
const trigger = detectTrigger(event, agent);
|
|
47
|
+
if (trigger) {
|
|
48
|
+
handleTriggers([trigger], agent, skillMd);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.error("Failed to parse event:", err);
|
|
53
|
+
}
|
|
54
|
+
}, (err) => {
|
|
55
|
+
console.error("SSE error, reconnecting...", err.message);
|
|
56
|
+
});
|
|
57
|
+
console.log("Worker ready. Listening for events...");
|
|
58
|
+
}
|
|
59
|
+
async function handleTriggers(triggers, agent, skillMd) {
|
|
60
|
+
if (isRunning)
|
|
61
|
+
return;
|
|
62
|
+
// Filter by wake attempt limit
|
|
63
|
+
const eligible = triggers.filter((t) => {
|
|
64
|
+
const attempts = wakeAttempts.get(t.eventId) ?? 0;
|
|
65
|
+
if (attempts >= MAX_WAKE_ATTEMPTS) {
|
|
66
|
+
console.log(`Skipping ${t.eventId}: max wake attempts reached`);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
});
|
|
71
|
+
if (eligible.length === 0)
|
|
72
|
+
return;
|
|
73
|
+
isRunning = true;
|
|
74
|
+
const trigger = eligible[0];
|
|
75
|
+
wakeAttempts.set(trigger.eventId, (wakeAttempts.get(trigger.eventId) ?? 0) + 1);
|
|
76
|
+
// Fetch recent context for the prompt
|
|
77
|
+
const recentContext = await fetchContext(trigger);
|
|
78
|
+
console.log(`Waking agent for: ${trigger.triggerType} on ${trigger.postId}`);
|
|
79
|
+
let retries = 0;
|
|
80
|
+
let success = false;
|
|
81
|
+
while (retries < MAX_CRASH_RETRIES) {
|
|
82
|
+
try {
|
|
83
|
+
const result = await invokeAgent({
|
|
84
|
+
agent,
|
|
85
|
+
trigger,
|
|
86
|
+
skillMd,
|
|
87
|
+
apiKey,
|
|
88
|
+
serverUrl,
|
|
89
|
+
recentContext,
|
|
90
|
+
});
|
|
91
|
+
if (result.exitCode === 0) {
|
|
92
|
+
success = true;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
console.error(`Agent exited with code ${result.exitCode}, retry ${retries + 1}/${MAX_CRASH_RETRIES}`);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
console.error("Agent invocation error:", err);
|
|
99
|
+
}
|
|
100
|
+
retries++;
|
|
101
|
+
}
|
|
102
|
+
if (!success) {
|
|
103
|
+
console.error(`Agent failed after ${MAX_CRASH_RETRIES} retries`);
|
|
104
|
+
}
|
|
105
|
+
isRunning = false;
|
|
106
|
+
// Post-completion: re-scan for items that arrived during execution
|
|
107
|
+
try {
|
|
108
|
+
const agent2 = await client.getMe();
|
|
109
|
+
const newUnprocessed = await scanUnprocessed(client, agent2);
|
|
110
|
+
if (newUnprocessed.length > 0) {
|
|
111
|
+
console.log(`Post-completion: ${newUnprocessed.length} unprocessed item(s) found`);
|
|
112
|
+
await handleTriggers(newUnprocessed, agent, skillMd);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
console.error("Post-completion scan error:", err);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async function fetchContext(trigger) {
|
|
120
|
+
try {
|
|
121
|
+
const comments = await client.getPostComments(trigger.postId, { limit: 10 });
|
|
122
|
+
return comments.data
|
|
123
|
+
.map((c) => `[${c.author_type}${c.author_name ? ` (${c.author_name})` : ""}] ${c.content}`)
|
|
124
|
+
.join("\n");
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return "";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
main().catch((err) => {
|
|
131
|
+
console.error("Fatal error:", err);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TriggerContext, AgentInfo } from "./types.js";
|
|
2
|
+
interface InvokeOptions {
|
|
3
|
+
agent: AgentInfo;
|
|
4
|
+
trigger: TriggerContext;
|
|
5
|
+
skillMd: string;
|
|
6
|
+
apiKey: string;
|
|
7
|
+
serverUrl: string;
|
|
8
|
+
recentContext: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function invokeAgent(options: InvokeOptions): Promise<{
|
|
11
|
+
exitCode: number;
|
|
12
|
+
}>;
|
|
13
|
+
export {};
|
package/dist/invoker.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export function invokeAgent(options) {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
const prompt = buildPrompt(options);
|
|
5
|
+
const child = spawn("claude", [
|
|
6
|
+
"-p",
|
|
7
|
+
prompt,
|
|
8
|
+
"--append-system-prompt",
|
|
9
|
+
options.skillMd,
|
|
10
|
+
], {
|
|
11
|
+
env: {
|
|
12
|
+
...process.env,
|
|
13
|
+
AGENTFEED_BASE_URL: `${options.serverUrl}/api`,
|
|
14
|
+
AGENTFEED_API_KEY: options.apiKey,
|
|
15
|
+
},
|
|
16
|
+
stdio: "inherit",
|
|
17
|
+
});
|
|
18
|
+
child.on("error", (err) => {
|
|
19
|
+
if (err.code === "ENOENT") {
|
|
20
|
+
reject(new Error("'claude' command not found. Install Claude Code: https://claude.ai/claude-code"));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
reject(err);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
child.on("close", (code) => {
|
|
27
|
+
resolve({ exitCode: code ?? 1 });
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function buildPrompt(options) {
|
|
32
|
+
const { agent, trigger, recentContext } = options;
|
|
33
|
+
const triggerLabel = trigger.triggerType === "own_post_comment"
|
|
34
|
+
? "Comment on your post"
|
|
35
|
+
: "@mention";
|
|
36
|
+
return `You are ${agent.name}.
|
|
37
|
+
|
|
38
|
+
[Trigger]
|
|
39
|
+
- Type: ${triggerLabel}
|
|
40
|
+
- Author: ${trigger.authorName ?? "unknown"}
|
|
41
|
+
- Feed: ${trigger.feedName || trigger.feedId}
|
|
42
|
+
- Post: ${trigger.postTitle ?? "(untitled)"} (${trigger.postId})
|
|
43
|
+
- Content: ${trigger.content}
|
|
44
|
+
|
|
45
|
+
[Recent Context]
|
|
46
|
+
${recentContext || "(no prior context)"}
|
|
47
|
+
|
|
48
|
+
Respond to all pending comments and posts. Use the AgentFeed API (available via environment variables AGENTFEED_BASE_URL and AGENTFEED_API_KEY) to read context and post responses.`;
|
|
49
|
+
}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export async function scanUnprocessed(client, agent) {
|
|
2
|
+
const triggers = [];
|
|
3
|
+
const feeds = await client.getFeeds();
|
|
4
|
+
for (const feed of feeds) {
|
|
5
|
+
const comments = await client.getFeedComments(feed.id, {
|
|
6
|
+
author_type: "human",
|
|
7
|
+
limit: 50,
|
|
8
|
+
});
|
|
9
|
+
for (const comment of comments.data) {
|
|
10
|
+
let shouldTrigger = false;
|
|
11
|
+
let triggerType = "own_post_comment";
|
|
12
|
+
// Check if this is on an agent-owned post
|
|
13
|
+
if (comment.post_created_by === agent.id) {
|
|
14
|
+
shouldTrigger = true;
|
|
15
|
+
triggerType = "own_post_comment";
|
|
16
|
+
}
|
|
17
|
+
// Check for @mentions
|
|
18
|
+
if (containsMention(comment.content, agent.name)) {
|
|
19
|
+
shouldTrigger = true;
|
|
20
|
+
triggerType = "mention";
|
|
21
|
+
}
|
|
22
|
+
if (!shouldTrigger)
|
|
23
|
+
continue;
|
|
24
|
+
// Check if there's already a bot reply after this comment
|
|
25
|
+
const replies = await client.getPostComments(comment.post_id, {
|
|
26
|
+
since: comment.created_at,
|
|
27
|
+
author_type: "bot",
|
|
28
|
+
limit: 1,
|
|
29
|
+
});
|
|
30
|
+
if (replies.data.length === 0) {
|
|
31
|
+
triggers.push({
|
|
32
|
+
triggerType,
|
|
33
|
+
eventId: comment.id,
|
|
34
|
+
feedId: feed.id,
|
|
35
|
+
feedName: feed.name,
|
|
36
|
+
postId: comment.post_id,
|
|
37
|
+
postTitle: comment.post_title,
|
|
38
|
+
content: comment.content,
|
|
39
|
+
authorName: comment.author_name,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return triggers;
|
|
45
|
+
}
|
|
46
|
+
function containsMention(text, agentName) {
|
|
47
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
48
|
+
const regex = new RegExp(`@${escaped}\\b`, "i");
|
|
49
|
+
return regex.test(text);
|
|
50
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface SSEEvent {
|
|
2
|
+
type: string;
|
|
3
|
+
data: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SSEConnection {
|
|
7
|
+
close: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function connectSSE(url: string, apiKey: string, onEvent: (event: SSEEvent) => void, onError: (error: Error) => void): SSEConnection;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export function connectSSE(url, apiKey, onEvent, onError) {
|
|
2
|
+
let aborted = false;
|
|
3
|
+
const controller = new AbortController();
|
|
4
|
+
const run = async () => {
|
|
5
|
+
while (!aborted) {
|
|
6
|
+
try {
|
|
7
|
+
const res = await fetch(url, {
|
|
8
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
9
|
+
signal: controller.signal,
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok || !res.body) {
|
|
12
|
+
throw new Error(`SSE connect failed: ${res.status}`);
|
|
13
|
+
}
|
|
14
|
+
const reader = res.body.getReader();
|
|
15
|
+
const decoder = new TextDecoder();
|
|
16
|
+
let buffer = "";
|
|
17
|
+
let currentEvent = { type: "message", data: "" };
|
|
18
|
+
while (!aborted) {
|
|
19
|
+
const { done, value } = await reader.read();
|
|
20
|
+
if (done)
|
|
21
|
+
break;
|
|
22
|
+
buffer += decoder.decode(value, { stream: true });
|
|
23
|
+
const lines = buffer.split("\n");
|
|
24
|
+
buffer = lines.pop() ?? "";
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
if (line.startsWith("event: ")) {
|
|
27
|
+
currentEvent.type = line.slice(7);
|
|
28
|
+
}
|
|
29
|
+
else if (line.startsWith("data: ")) {
|
|
30
|
+
currentEvent.data = line.slice(6);
|
|
31
|
+
}
|
|
32
|
+
else if (line.startsWith("id: ")) {
|
|
33
|
+
currentEvent.id = line.slice(4);
|
|
34
|
+
}
|
|
35
|
+
else if (line === "") {
|
|
36
|
+
if (currentEvent.data) {
|
|
37
|
+
onEvent({ ...currentEvent });
|
|
38
|
+
}
|
|
39
|
+
currentEvent = { type: "message", data: "" };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (aborted)
|
|
46
|
+
return;
|
|
47
|
+
onError(err instanceof Error ? err : new Error(String(err)));
|
|
48
|
+
// Reconnect after delay
|
|
49
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
run();
|
|
54
|
+
return {
|
|
55
|
+
close: () => {
|
|
56
|
+
aborted = true;
|
|
57
|
+
controller.abort();
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
package/dist/trigger.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function detectTrigger(event, agent) {
|
|
2
|
+
if (event.type === "comment_created") {
|
|
3
|
+
// Trigger 1: Comment on own post
|
|
4
|
+
if (event.post_created_by === agent.id) {
|
|
5
|
+
return {
|
|
6
|
+
triggerType: "own_post_comment",
|
|
7
|
+
eventId: event.id,
|
|
8
|
+
feedId: event.feed_id,
|
|
9
|
+
feedName: "",
|
|
10
|
+
postId: event.post_id,
|
|
11
|
+
postTitle: event.post_title,
|
|
12
|
+
content: event.content,
|
|
13
|
+
authorName: event.author_name,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// Trigger 2: @mention in comment
|
|
17
|
+
if (containsMention(event.content, agent.name)) {
|
|
18
|
+
return {
|
|
19
|
+
triggerType: "mention",
|
|
20
|
+
eventId: event.id,
|
|
21
|
+
feedId: event.feed_id,
|
|
22
|
+
feedName: "",
|
|
23
|
+
postId: event.post_id,
|
|
24
|
+
postTitle: event.post_title,
|
|
25
|
+
content: event.content,
|
|
26
|
+
authorName: event.author_name,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (event.type === "post_created") {
|
|
31
|
+
// Trigger 2: @mention in post title or content
|
|
32
|
+
const text = [event.title, event.content].filter(Boolean).join(" ");
|
|
33
|
+
if (containsMention(text, agent.name)) {
|
|
34
|
+
return {
|
|
35
|
+
triggerType: "mention",
|
|
36
|
+
eventId: event.id,
|
|
37
|
+
feedId: event.feed_id,
|
|
38
|
+
feedName: event.feed_name,
|
|
39
|
+
postId: event.id,
|
|
40
|
+
postTitle: event.title,
|
|
41
|
+
content: event.content ?? "",
|
|
42
|
+
authorName: event.author_name,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function containsMention(text, agentName) {
|
|
49
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
50
|
+
const regex = new RegExp(`@${escaped}\\b`, "i");
|
|
51
|
+
return regex.test(text);
|
|
52
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export interface AgentInfo {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
}
|
|
6
|
+
export interface GlobalPostEvent {
|
|
7
|
+
type: "post_created";
|
|
8
|
+
id: string;
|
|
9
|
+
feed_id: string;
|
|
10
|
+
feed_name: string;
|
|
11
|
+
title: string | null;
|
|
12
|
+
content: string | null;
|
|
13
|
+
created_by: string | null;
|
|
14
|
+
author_name: string | null;
|
|
15
|
+
created_at: string;
|
|
16
|
+
}
|
|
17
|
+
export interface GlobalCommentEvent {
|
|
18
|
+
type: "comment_created";
|
|
19
|
+
id: string;
|
|
20
|
+
post_id: string;
|
|
21
|
+
feed_id: string;
|
|
22
|
+
content: string;
|
|
23
|
+
author_type: "human" | "bot";
|
|
24
|
+
created_by: string | null;
|
|
25
|
+
author_name: string | null;
|
|
26
|
+
created_at: string;
|
|
27
|
+
post_title: string | null;
|
|
28
|
+
post_created_by: string | null;
|
|
29
|
+
}
|
|
30
|
+
export type GlobalEvent = GlobalPostEvent | GlobalCommentEvent;
|
|
31
|
+
export interface TriggerContext {
|
|
32
|
+
triggerType: "own_post_comment" | "mention";
|
|
33
|
+
eventId: string;
|
|
34
|
+
feedId: string;
|
|
35
|
+
feedName: string;
|
|
36
|
+
postId: string;
|
|
37
|
+
postTitle: string | null;
|
|
38
|
+
content: string;
|
|
39
|
+
authorName: string | null;
|
|
40
|
+
}
|
|
41
|
+
export interface FeedItem {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
}
|
|
45
|
+
export interface FeedCommentItem {
|
|
46
|
+
id: string;
|
|
47
|
+
post_id: string;
|
|
48
|
+
content: string;
|
|
49
|
+
author_type: "human" | "bot";
|
|
50
|
+
created_by: string | null;
|
|
51
|
+
author_name: string | null;
|
|
52
|
+
created_at: string;
|
|
53
|
+
post_title: string | null;
|
|
54
|
+
post_created_by: string | null;
|
|
55
|
+
}
|
|
56
|
+
export interface CommentItem {
|
|
57
|
+
id: string;
|
|
58
|
+
post_id: string;
|
|
59
|
+
content: string;
|
|
60
|
+
author_type: "human" | "bot";
|
|
61
|
+
created_by: string | null;
|
|
62
|
+
author_name: string | null;
|
|
63
|
+
created_at: string;
|
|
64
|
+
}
|
|
65
|
+
export interface PaginatedResponse<T> {
|
|
66
|
+
data: T[];
|
|
67
|
+
next_cursor: string | null;
|
|
68
|
+
has_more: boolean;
|
|
69
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agentfeed",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Worker daemon for AgentFeed - watches feeds and wakes AI agents via claude -p",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agentfeed": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"dist/"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsx src/index.ts"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^22",
|
|
22
|
+
"tsx": "^4",
|
|
23
|
+
"typescript": "^5"
|
|
24
|
+
}
|
|
25
|
+
}
|