spora 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account-creator-AABUY2JU.js +498 -0
- package/dist/account-creator-AABUY2JU.js.map +1 -0
- package/dist/chunk-6KCIAMHL.js +62 -0
- package/dist/chunk-6KCIAMHL.js.map +1 -0
- package/dist/chunk-A6R5ZGK6.js +156 -0
- package/dist/chunk-A6R5ZGK6.js.map +1 -0
- package/dist/chunk-B6VI6L4D.js +124 -0
- package/dist/chunk-B6VI6L4D.js.map +1 -0
- package/dist/chunk-FTFTB5Y5.js +622 -0
- package/dist/chunk-FTFTB5Y5.js.map +1 -0
- package/dist/chunk-GMSK775L.js +103 -0
- package/dist/chunk-GMSK775L.js.map +1 -0
- package/dist/chunk-H62HH5ZI.js +56 -0
- package/dist/chunk-H62HH5ZI.js.map +1 -0
- package/dist/chunk-KQ37VL54.js +32 -0
- package/dist/chunk-KQ37VL54.js.map +1 -0
- package/dist/chunk-ML4EMUZC.js +57 -0
- package/dist/chunk-ML4EMUZC.js.map +1 -0
- package/dist/chunk-N5TBL3NY.js +86 -0
- package/dist/chunk-N5TBL3NY.js.map +1 -0
- package/dist/chunk-PNZ3XK2N.js +358 -0
- package/dist/chunk-PNZ3XK2N.js.map +1 -0
- package/dist/chunk-UCCAF2ZO.js +47 -0
- package/dist/chunk-UCCAF2ZO.js.map +1 -0
- package/dist/chunk-V6ZNR2SI.js +105 -0
- package/dist/chunk-V6ZNR2SI.js.map +1 -0
- package/dist/cli.js +654 -0
- package/dist/cli.js.map +1 -0
- package/dist/client-BGLXHLID.js +401 -0
- package/dist/client-BGLXHLID.js.map +1 -0
- package/dist/client-TWYR2IIQ.js +373 -0
- package/dist/client-TWYR2IIQ.js.map +1 -0
- package/dist/colony-JVBCMZTK.js +229 -0
- package/dist/colony-JVBCMZTK.js.map +1 -0
- package/dist/config-5EPXA325.js +14 -0
- package/dist/config-5EPXA325.js.map +1 -0
- package/dist/crypto-HS4CGS4A.js +14 -0
- package/dist/crypto-HS4CGS4A.js.map +1 -0
- package/dist/heartbeat-B2CZKMUF.js +901 -0
- package/dist/heartbeat-B2CZKMUF.js.map +1 -0
- package/dist/identity-6CXRCXJQ.js +26 -0
- package/dist/identity-6CXRCXJQ.js.map +1 -0
- package/dist/init-KXNLBFMG.js +403 -0
- package/dist/init-KXNLBFMG.js.map +1 -0
- package/dist/llm-CUCO24K7.js +16 -0
- package/dist/llm-CUCO24K7.js.map +1 -0
- package/dist/mcp-server.js +773 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/memory-2OI3JXY2.js +26 -0
- package/dist/memory-2OI3JXY2.js.map +1 -0
- package/dist/memory-LPU2I6NI.js +24 -0
- package/dist/memory-LPU2I6NI.js.map +1 -0
- package/dist/paths-Q4TJEOMQ.js +13 -0
- package/dist/paths-Q4TJEOMQ.js.map +1 -0
- package/dist/prompt-builder-VHGZFBL6.js +19 -0
- package/dist/prompt-builder-VHGZFBL6.js.map +1 -0
- package/dist/queue-LNBQWMFX.js +14 -0
- package/dist/queue-LNBQWMFX.js.map +1 -0
- package/dist/web-chat/chat.html +423 -0
- package/dist/web-chat-DHHJTGFZ.js +253 -0
- package/dist/web-chat-DHHJTGFZ.js.map +1 -0
- package/dist/x-client-W5IB7XOM.js +12 -0
- package/dist/x-client-W5IB7XOM.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureDirectories,
|
|
3
|
+
paths
|
|
4
|
+
} from "./chunk-6KCIAMHL.js";
|
|
5
|
+
|
|
6
|
+
// src/colony/memory.ts
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
var COLONY_MEMORY_PATH = join(paths.root, "colony-memory.json");
|
|
10
|
+
function loadColonyMemory() {
|
|
11
|
+
if (!existsSync(COLONY_MEMORY_PATH)) {
|
|
12
|
+
return createFreshMemory();
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const raw = readFileSync(COLONY_MEMORY_PATH, "utf-8");
|
|
16
|
+
return JSON.parse(raw);
|
|
17
|
+
} catch {
|
|
18
|
+
return createFreshMemory();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function saveColonyMemory(memory) {
|
|
22
|
+
ensureDirectories();
|
|
23
|
+
writeFileSync(COLONY_MEMORY_PATH, JSON.stringify(memory, null, 2));
|
|
24
|
+
}
|
|
25
|
+
function createFreshMemory() {
|
|
26
|
+
return {
|
|
27
|
+
lastSynced: "",
|
|
28
|
+
entries: [],
|
|
29
|
+
activePlans: []
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function addColonyEntry(entry) {
|
|
33
|
+
const memory = loadColonyMemory();
|
|
34
|
+
const exists = memory.entries.some(
|
|
35
|
+
(e) => e.handle === entry.handle && e.content === entry.content
|
|
36
|
+
);
|
|
37
|
+
if (!exists) {
|
|
38
|
+
memory.entries.push(entry);
|
|
39
|
+
}
|
|
40
|
+
memory.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
|
|
41
|
+
saveColonyMemory(memory);
|
|
42
|
+
}
|
|
43
|
+
function addOrUpdatePlan(plan) {
|
|
44
|
+
const memory = loadColonyMemory();
|
|
45
|
+
const existing = memory.activePlans.find((p) => p.id === plan.id);
|
|
46
|
+
if (existing) {
|
|
47
|
+
for (const p of plan.participants) {
|
|
48
|
+
if (!existing.participants.includes(p)) {
|
|
49
|
+
existing.participants.push(p);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
memory.activePlans.push(plan);
|
|
54
|
+
}
|
|
55
|
+
saveColonyMemory(memory);
|
|
56
|
+
}
|
|
57
|
+
function addPlanParticipant(planId, handle) {
|
|
58
|
+
const memory = loadColonyMemory();
|
|
59
|
+
const plan = memory.activePlans.find((p) => p.id === planId);
|
|
60
|
+
if (!plan) return false;
|
|
61
|
+
if (plan.participants.includes(handle)) return false;
|
|
62
|
+
plan.participants.push(handle);
|
|
63
|
+
saveColonyMemory(memory);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
function addPlanReport(planId, handle, result) {
|
|
67
|
+
const memory = loadColonyMemory();
|
|
68
|
+
const plan = memory.activePlans.find((p) => p.id === planId);
|
|
69
|
+
if (!plan) return false;
|
|
70
|
+
plan.reports.push({ handle, result, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
71
|
+
saveColonyMemory(memory);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
function getRecentEntries(count = 50) {
|
|
75
|
+
const memory = loadColonyMemory();
|
|
76
|
+
return memory.entries.slice(-count);
|
|
77
|
+
}
|
|
78
|
+
function getActivePlans() {
|
|
79
|
+
const memory = loadColonyMemory();
|
|
80
|
+
return memory.activePlans.filter((p) => p.status === "active");
|
|
81
|
+
}
|
|
82
|
+
function getTodayEntries() {
|
|
83
|
+
const memory = loadColonyMemory();
|
|
84
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
85
|
+
return memory.entries.filter((e) => e.timestamp.startsWith(today));
|
|
86
|
+
}
|
|
87
|
+
function renderColonyBriefing() {
|
|
88
|
+
const memory = loadColonyMemory();
|
|
89
|
+
const lines = [];
|
|
90
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
91
|
+
lines.push(`# Colony Briefing`);
|
|
92
|
+
lines.push(`Last synced: ${memory.lastSynced || "never"}`);
|
|
93
|
+
lines.push(`Total memory: ${memory.entries.length} entries`);
|
|
94
|
+
lines.push("");
|
|
95
|
+
const activePlans = memory.activePlans.filter((p) => p.status === "active");
|
|
96
|
+
if (activePlans.length > 0) {
|
|
97
|
+
lines.push(`## Active Plans (${activePlans.length})`);
|
|
98
|
+
for (const plan of activePlans) {
|
|
99
|
+
lines.push(`- **${plan.description}** (by @${plan.proposedBy})`);
|
|
100
|
+
lines.push(` Participants: ${plan.participants.map((p) => `@${p}`).join(", ")}`);
|
|
101
|
+
if (plan.reports.length > 0) {
|
|
102
|
+
lines.push(` Reports: ${plan.reports.length}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
lines.push("");
|
|
106
|
+
}
|
|
107
|
+
const todayEntries = memory.entries.filter((e) => e.timestamp.startsWith(today));
|
|
108
|
+
if (todayEntries.length > 0) {
|
|
109
|
+
lines.push(`## Today's Activity (${todayEntries.length} entries)`);
|
|
110
|
+
for (const entry of todayEntries.slice(-20)) {
|
|
111
|
+
const label = entry.type === "status" ? "STATUS" : entry.type === "idea" ? "IDEA" : entry.type === "observation" ? "OBS" : "";
|
|
112
|
+
lines.push(`- @${entry.handle} [${label}]: ${entry.content}`);
|
|
113
|
+
}
|
|
114
|
+
lines.push("");
|
|
115
|
+
} else {
|
|
116
|
+
lines.push("## Today's Activity");
|
|
117
|
+
lines.push("No activity yet today. Be the first to post!");
|
|
118
|
+
lines.push("");
|
|
119
|
+
}
|
|
120
|
+
const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0];
|
|
121
|
+
const recentEntries = memory.entries.filter(
|
|
122
|
+
(e) => !e.timestamp.startsWith(today) && e.timestamp >= weekAgo
|
|
123
|
+
);
|
|
124
|
+
if (recentEntries.length > 0) {
|
|
125
|
+
lines.push(`## Recent History (${recentEntries.length} entries, last 7 days)`);
|
|
126
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
127
|
+
for (const entry of recentEntries) {
|
|
128
|
+
const date = entry.timestamp.split("T")[0];
|
|
129
|
+
if (!byDate.has(date)) byDate.set(date, []);
|
|
130
|
+
byDate.get(date).push(entry);
|
|
131
|
+
}
|
|
132
|
+
for (const [date, entries] of [...byDate.entries()].sort().reverse()) {
|
|
133
|
+
const handles = new Set(entries.map((e) => e.handle));
|
|
134
|
+
lines.push(`### ${date} \u2014 ${entries.length} entries from ${handles.size} Spores`);
|
|
135
|
+
for (const entry of entries.slice(-5)) {
|
|
136
|
+
lines.push(`- @${entry.handle}: ${entry.content}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
lines.push("");
|
|
140
|
+
}
|
|
141
|
+
return lines.join("\n");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export {
|
|
145
|
+
loadColonyMemory,
|
|
146
|
+
saveColonyMemory,
|
|
147
|
+
addColonyEntry,
|
|
148
|
+
addOrUpdatePlan,
|
|
149
|
+
addPlanParticipant,
|
|
150
|
+
addPlanReport,
|
|
151
|
+
getRecentEntries,
|
|
152
|
+
getActivePlans,
|
|
153
|
+
getTodayEntries,
|
|
154
|
+
renderColonyBriefing
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=chunk-A6R5ZGK6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/colony/memory.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { paths, ensureDirectories } from \"../utils/paths.js\";\n\n// ========== TYPES ==========\n\nexport interface ColonyEntry {\n handle: string;\n content: string;\n timestamp: string;\n type: \"status\" | \"idea\" | \"observation\" | \"chatter\" | \"plan_report\";\n}\n\nexport interface ColonyPlan {\n id: string;\n proposedBy: string;\n description: string;\n proposedAt: string;\n participants: string[];\n status: \"active\" | \"completed\" | \"expired\";\n reports: Array<{ handle: string; result: string; timestamp: string }>;\n}\n\nexport interface ColonyMemory {\n lastSynced: string;\n entries: ColonyEntry[];\n activePlans: ColonyPlan[];\n}\n\n// ========== PERSISTENCE ==========\n\nconst COLONY_MEMORY_PATH = join(paths.root, \"colony-memory.json\");\n\nexport function loadColonyMemory(): ColonyMemory {\n if (!existsSync(COLONY_MEMORY_PATH)) {\n return createFreshMemory();\n }\n\n try {\n const raw = readFileSync(COLONY_MEMORY_PATH, \"utf-8\");\n return JSON.parse(raw) as ColonyMemory;\n } catch {\n return createFreshMemory();\n }\n}\n\nexport function saveColonyMemory(memory: ColonyMemory): void {\n ensureDirectories();\n writeFileSync(COLONY_MEMORY_PATH, JSON.stringify(memory, null, 2));\n}\n\nfunction createFreshMemory(): ColonyMemory {\n return {\n lastSynced: \"\",\n entries: [],\n activePlans: [],\n };\n}\n\n// ========== WRITE OPERATIONS ==========\n\nexport function addColonyEntry(entry: ColonyEntry): void {\n const memory = loadColonyMemory();\n\n // Dedupe by handle + content\n const exists = memory.entries.some(\n (e) => e.handle === entry.handle && e.content === entry.content\n );\n if (!exists) {\n memory.entries.push(entry);\n }\n\n memory.lastSynced = new Date().toISOString();\n saveColonyMemory(memory);\n}\n\nexport function addOrUpdatePlan(plan: ColonyPlan): void {\n const memory = loadColonyMemory();\n\n const existing = memory.activePlans.find((p) => p.id === plan.id);\n if (existing) {\n // Merge participants\n for (const p of plan.participants) {\n if (!existing.participants.includes(p)) {\n existing.participants.push(p);\n }\n }\n } else {\n memory.activePlans.push(plan);\n }\n\n saveColonyMemory(memory);\n}\n\nexport function addPlanParticipant(planId: string, handle: string): boolean {\n const memory = loadColonyMemory();\n const plan = memory.activePlans.find((p) => p.id === planId);\n if (!plan) return false;\n if (plan.participants.includes(handle)) return false;\n\n plan.participants.push(handle);\n saveColonyMemory(memory);\n return true;\n}\n\nexport function addPlanReport(planId: string, handle: string, result: string): boolean {\n const memory = loadColonyMemory();\n const plan = memory.activePlans.find((p) => p.id === planId);\n if (!plan) return false;\n\n plan.reports.push({ handle, result, timestamp: new Date().toISOString() });\n saveColonyMemory(memory);\n return true;\n}\n\n// ========== READ OPERATIONS ==========\n\nexport function getRecentEntries(count = 50): ColonyEntry[] {\n const memory = loadColonyMemory();\n return memory.entries.slice(-count);\n}\n\nexport function getActivePlans(): ColonyPlan[] {\n const memory = loadColonyMemory();\n return memory.activePlans.filter((p) => p.status === \"active\");\n}\n\nexport function getTodayEntries(): ColonyEntry[] {\n const memory = loadColonyMemory();\n const today = new Date().toISOString().split(\"T\")[0];\n return memory.entries.filter((e) => e.timestamp.startsWith(today));\n}\n\nexport function renderColonyBriefing(): string {\n const memory = loadColonyMemory();\n const lines: string[] = [];\n const today = new Date().toISOString().split(\"T\")[0];\n\n lines.push(`# Colony Briefing`);\n lines.push(`Last synced: ${memory.lastSynced || \"never\"}`);\n lines.push(`Total memory: ${memory.entries.length} entries`);\n lines.push(\"\");\n\n // Active plans\n const activePlans = memory.activePlans.filter((p) => p.status === \"active\");\n if (activePlans.length > 0) {\n lines.push(`## Active Plans (${activePlans.length})`);\n for (const plan of activePlans) {\n lines.push(`- **${plan.description}** (by @${plan.proposedBy})`);\n lines.push(` Participants: ${plan.participants.map((p) => `@${p}`).join(\", \")}`);\n if (plan.reports.length > 0) {\n lines.push(` Reports: ${plan.reports.length}`);\n }\n }\n lines.push(\"\");\n }\n\n // Today's activity\n const todayEntries = memory.entries.filter((e) => e.timestamp.startsWith(today));\n if (todayEntries.length > 0) {\n lines.push(`## Today's Activity (${todayEntries.length} entries)`);\n for (const entry of todayEntries.slice(-20)) {\n const label = entry.type === \"status\" ? \"STATUS\" : entry.type === \"idea\" ? \"IDEA\" : entry.type === \"observation\" ? \"OBS\" : \"\";\n lines.push(`- @${entry.handle} [${label}]: ${entry.content}`);\n }\n lines.push(\"\");\n } else {\n lines.push(\"## Today's Activity\");\n lines.push(\"No activity yet today. Be the first to post!\");\n lines.push(\"\");\n }\n\n // Recent history (last 7 days, excluding today)\n const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().split(\"T\")[0];\n const recentEntries = memory.entries.filter(\n (e) => !e.timestamp.startsWith(today) && e.timestamp >= weekAgo\n );\n if (recentEntries.length > 0) {\n lines.push(`## Recent History (${recentEntries.length} entries, last 7 days)`);\n // Group by date\n const byDate = new Map<string, ColonyEntry[]>();\n for (const entry of recentEntries) {\n const date = entry.timestamp.split(\"T\")[0];\n if (!byDate.has(date)) byDate.set(date, []);\n byDate.get(date)!.push(entry);\n }\n for (const [date, entries] of [...byDate.entries()].sort().reverse()) {\n const handles = new Set(entries.map((e) => e.handle));\n lines.push(`### ${date} — ${entries.length} entries from ${handles.size} Spores`);\n for (const entry of entries.slice(-5)) {\n lines.push(`- @${entry.handle}: ${entry.content}`);\n }\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":";;;;;;AAAA,SAAS,cAAc,eAAe,kBAAkB;AACxD,SAAS,YAAY;AA8BrB,IAAM,qBAAqB,KAAK,MAAM,MAAM,oBAAoB;AAEzD,SAAS,mBAAiC;AAC/C,MAAI,CAAC,WAAW,kBAAkB,GAAG;AACnC,WAAO,kBAAkB;AAAA,EAC3B;AAEA,MAAI;AACF,UAAM,MAAM,aAAa,oBAAoB,OAAO;AACpD,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO,kBAAkB;AAAA,EAC3B;AACF;AAEO,SAAS,iBAAiB,QAA4B;AAC3D,oBAAkB;AAClB,gBAAc,oBAAoB,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AACnE;AAEA,SAAS,oBAAkC;AACzC,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,SAAS,CAAC;AAAA,IACV,aAAa,CAAC;AAAA,EAChB;AACF;AAIO,SAAS,eAAe,OAA0B;AACvD,QAAM,SAAS,iBAAiB;AAGhC,QAAM,SAAS,OAAO,QAAQ;AAAA,IAC5B,CAAC,MAAM,EAAE,WAAW,MAAM,UAAU,EAAE,YAAY,MAAM;AAAA,EAC1D;AACA,MAAI,CAAC,QAAQ;AACX,WAAO,QAAQ,KAAK,KAAK;AAAA,EAC3B;AAEA,SAAO,cAAa,oBAAI,KAAK,GAAE,YAAY;AAC3C,mBAAiB,MAAM;AACzB;AAEO,SAAS,gBAAgB,MAAwB;AACtD,QAAM,SAAS,iBAAiB;AAEhC,QAAM,WAAW,OAAO,YAAY,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE;AAChE,MAAI,UAAU;AAEZ,eAAW,KAAK,KAAK,cAAc;AACjC,UAAI,CAAC,SAAS,aAAa,SAAS,CAAC,GAAG;AACtC,iBAAS,aAAa,KAAK,CAAC;AAAA,MAC9B;AAAA,IACF;AAAA,EACF,OAAO;AACL,WAAO,YAAY,KAAK,IAAI;AAAA,EAC9B;AAEA,mBAAiB,MAAM;AACzB;AAEO,SAAS,mBAAmB,QAAgB,QAAyB;AAC1E,QAAM,SAAS,iBAAiB;AAChC,QAAM,OAAO,OAAO,YAAY,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AAC3D,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,aAAa,SAAS,MAAM,EAAG,QAAO;AAE/C,OAAK,aAAa,KAAK,MAAM;AAC7B,mBAAiB,MAAM;AACvB,SAAO;AACT;AAEO,SAAS,cAAc,QAAgB,QAAgB,QAAyB;AACrF,QAAM,SAAS,iBAAiB;AAChC,QAAM,OAAO,OAAO,YAAY,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AAC3D,MAAI,CAAC,KAAM,QAAO;AAElB,OAAK,QAAQ,KAAK,EAAE,QAAQ,QAAQ,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC;AACzE,mBAAiB,MAAM;AACvB,SAAO;AACT;AAIO,SAAS,iBAAiB,QAAQ,IAAmB;AAC1D,QAAM,SAAS,iBAAiB;AAChC,SAAO,OAAO,QAAQ,MAAM,CAAC,KAAK;AACpC;AAEO,SAAS,iBAA+B;AAC7C,QAAM,SAAS,iBAAiB;AAChC,SAAO,OAAO,YAAY,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ;AAC/D;AAEO,SAAS,kBAAiC;AAC/C,QAAM,SAAS,iBAAiB;AAChC,QAAM,SAAQ,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AACnD,SAAO,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,UAAU,WAAW,KAAK,CAAC;AACnE;AAEO,SAAS,uBAA+B;AAC7C,QAAM,SAAS,iBAAiB;AAChC,QAAM,QAAkB,CAAC;AACzB,QAAM,SAAQ,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AAEnD,QAAM,KAAK,mBAAmB;AAC9B,QAAM,KAAK,gBAAgB,OAAO,cAAc,OAAO,EAAE;AACzD,QAAM,KAAK,iBAAiB,OAAO,QAAQ,MAAM,UAAU;AAC3D,QAAM,KAAK,EAAE;AAGb,QAAM,cAAc,OAAO,YAAY,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ;AAC1E,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,KAAK,oBAAoB,YAAY,MAAM,GAAG;AACpD,eAAW,QAAQ,aAAa;AAC9B,YAAM,KAAK,OAAO,KAAK,WAAW,WAAW,KAAK,UAAU,GAAG;AAC/D,YAAM,KAAK,mBAAmB,KAAK,aAAa,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AAChF,UAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,cAAM,KAAK,cAAc,KAAK,QAAQ,MAAM,EAAE;AAAA,MAChD;AAAA,IACF;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAGA,QAAM,eAAe,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,UAAU,WAAW,KAAK,CAAC;AAC/E,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,KAAK,wBAAwB,aAAa,MAAM,WAAW;AACjE,eAAW,SAAS,aAAa,MAAM,GAAG,GAAG;AAC3C,YAAM,QAAQ,MAAM,SAAS,WAAW,WAAW,MAAM,SAAS,SAAS,SAAS,MAAM,SAAS,gBAAgB,QAAQ;AAC3H,YAAM,KAAK,MAAM,MAAM,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,EAAE;AAAA,IAC9D;AACA,UAAM,KAAK,EAAE;AAAA,EACf,OAAO;AACL,UAAM,KAAK,qBAAqB;AAChC,UAAM,KAAK,8CAA8C;AACzD,UAAM,KAAK,EAAE;AAAA,EACf;AAGA,QAAM,UAAU,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AACzF,QAAM,gBAAgB,OAAO,QAAQ;AAAA,IACnC,CAAC,MAAM,CAAC,EAAE,UAAU,WAAW,KAAK,KAAK,EAAE,aAAa;AAAA,EAC1D;AACA,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,KAAK,sBAAsB,cAAc,MAAM,wBAAwB;AAE7E,UAAM,SAAS,oBAAI,IAA2B;AAC9C,eAAW,SAAS,eAAe;AACjC,YAAM,OAAO,MAAM,UAAU,MAAM,GAAG,EAAE,CAAC;AACzC,UAAI,CAAC,OAAO,IAAI,IAAI,EAAG,QAAO,IAAI,MAAM,CAAC,CAAC;AAC1C,aAAO,IAAI,IAAI,EAAG,KAAK,KAAK;AAAA,IAC9B;AACA,eAAW,CAAC,MAAM,OAAO,KAAK,CAAC,GAAG,OAAO,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,GAAG;AACpE,YAAM,UAAU,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AACpD,YAAM,KAAK,OAAO,IAAI,WAAM,QAAQ,MAAM,iBAAiB,QAAQ,IAAI,SAAS;AAChF,iBAAW,SAAS,QAAQ,MAAM,EAAE,GAAG;AACrC,cAAM,KAAK,MAAM,MAAM,MAAM,KAAK,MAAM,OAAO,EAAE;AAAA,MACnD;AAAA,IACF;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;","names":[]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadConfig
|
|
3
|
+
} from "./chunk-GMSK775L.js";
|
|
4
|
+
import {
|
|
5
|
+
logger
|
|
6
|
+
} from "./chunk-UCCAF2ZO.js";
|
|
7
|
+
import {
|
|
8
|
+
ensureDirectories,
|
|
9
|
+
paths
|
|
10
|
+
} from "./chunk-6KCIAMHL.js";
|
|
11
|
+
|
|
12
|
+
// src/scheduler/queue.ts
|
|
13
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
14
|
+
function loadQueue() {
|
|
15
|
+
if (!existsSync(paths.pendingPosts)) {
|
|
16
|
+
return { entries: [] };
|
|
17
|
+
}
|
|
18
|
+
return JSON.parse(readFileSync(paths.pendingPosts, "utf-8"));
|
|
19
|
+
}
|
|
20
|
+
function saveQueue(data) {
|
|
21
|
+
ensureDirectories();
|
|
22
|
+
writeFileSync(paths.pendingPosts, JSON.stringify(data, null, 2));
|
|
23
|
+
}
|
|
24
|
+
function nextScheduledTime() {
|
|
25
|
+
const config = loadConfig();
|
|
26
|
+
const now = /* @__PURE__ */ new Date();
|
|
27
|
+
const queue = loadQueue();
|
|
28
|
+
const pendingEntries = queue.entries.filter((e) => e.status === "pending");
|
|
29
|
+
let lastScheduled = now;
|
|
30
|
+
if (pendingEntries.length > 0) {
|
|
31
|
+
const latest = new Date(
|
|
32
|
+
pendingEntries.reduce(
|
|
33
|
+
(max, e) => new Date(e.scheduledFor) > new Date(max.scheduledFor) ? e : max
|
|
34
|
+
).scheduledFor
|
|
35
|
+
);
|
|
36
|
+
if (latest > lastScheduled) lastScheduled = latest;
|
|
37
|
+
}
|
|
38
|
+
const intervalMinutes = Math.floor(
|
|
39
|
+
(config.schedule.activeHoursEnd - config.schedule.activeHoursStart) * 60 / config.schedule.postsPerDay
|
|
40
|
+
);
|
|
41
|
+
const next = new Date(lastScheduled.getTime() + intervalMinutes * 60 * 1e3);
|
|
42
|
+
if (next.getHours() >= config.schedule.activeHoursEnd) {
|
|
43
|
+
next.setDate(next.getDate() + 1);
|
|
44
|
+
next.setHours(config.schedule.activeHoursStart, Math.floor(Math.random() * 60), 0, 0);
|
|
45
|
+
}
|
|
46
|
+
if (next.getHours() < config.schedule.activeHoursStart) {
|
|
47
|
+
next.setHours(config.schedule.activeHoursStart, Math.floor(Math.random() * 60), 0, 0);
|
|
48
|
+
}
|
|
49
|
+
return next.toISOString();
|
|
50
|
+
}
|
|
51
|
+
function addToQueue(content, scheduledFor) {
|
|
52
|
+
const queue = loadQueue();
|
|
53
|
+
const entry = {
|
|
54
|
+
id: `post-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
55
|
+
content,
|
|
56
|
+
scheduledFor: scheduledFor ?? nextScheduledTime(),
|
|
57
|
+
status: "pending",
|
|
58
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
59
|
+
};
|
|
60
|
+
queue.entries.push(entry);
|
|
61
|
+
saveQueue(queue);
|
|
62
|
+
logger.info(`Post queued: ${entry.id} scheduled for ${entry.scheduledFor}`);
|
|
63
|
+
return entry;
|
|
64
|
+
}
|
|
65
|
+
async function flushQueue() {
|
|
66
|
+
const queue = loadQueue();
|
|
67
|
+
const now = /* @__PURE__ */ new Date();
|
|
68
|
+
let posted = 0;
|
|
69
|
+
let failed = 0;
|
|
70
|
+
const { getXClient } = await import("./x-client-W5IB7XOM.js");
|
|
71
|
+
const client = await getXClient();
|
|
72
|
+
for (const entry of queue.entries) {
|
|
73
|
+
if (entry.status !== "pending") continue;
|
|
74
|
+
if (new Date(entry.scheduledFor) > now) continue;
|
|
75
|
+
try {
|
|
76
|
+
const result = await client.postTweet(entry.content);
|
|
77
|
+
if (result.success) {
|
|
78
|
+
entry.status = "posted";
|
|
79
|
+
entry.postedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
80
|
+
posted++;
|
|
81
|
+
logger.info(`Posted: ${entry.id}`);
|
|
82
|
+
} else {
|
|
83
|
+
entry.status = "failed";
|
|
84
|
+
entry.error = result.error;
|
|
85
|
+
failed++;
|
|
86
|
+
logger.warn(`Failed to post: ${entry.id} - ${result.error}`);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
entry.status = "failed";
|
|
90
|
+
entry.error = error.message;
|
|
91
|
+
failed++;
|
|
92
|
+
}
|
|
93
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
94
|
+
}
|
|
95
|
+
saveQueue(queue);
|
|
96
|
+
const remaining = queue.entries.filter((e) => e.status === "pending").length;
|
|
97
|
+
return { posted, failed, remaining };
|
|
98
|
+
}
|
|
99
|
+
function showQueue() {
|
|
100
|
+
const queue = loadQueue();
|
|
101
|
+
const pending = queue.entries.filter((e) => e.status === "pending");
|
|
102
|
+
if (pending.length === 0) {
|
|
103
|
+
console.log("Queue is empty.");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
console.log(`
|
|
107
|
+
${pending.length} posts queued:
|
|
108
|
+
`);
|
|
109
|
+
for (const entry of pending.sort(
|
|
110
|
+
(a, b) => new Date(a.scheduledFor).getTime() - new Date(b.scheduledFor).getTime()
|
|
111
|
+
)) {
|
|
112
|
+
const time = new Date(entry.scheduledFor).toLocaleString();
|
|
113
|
+
const preview = entry.content.length > 60 ? entry.content.slice(0, 60) + "..." : entry.content;
|
|
114
|
+
console.log(` [${time}] ${preview}`);
|
|
115
|
+
}
|
|
116
|
+
console.log();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
addToQueue,
|
|
121
|
+
flushQueue,
|
|
122
|
+
showQueue
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=chunk-B6VI6L4D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/scheduler/queue.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { paths, ensureDirectories } from \"../utils/paths.js\";\nimport { loadConfig, saveConfig } from \"../utils/config.js\";\nimport { logger } from \"../utils/logger.js\";\n\nexport interface QueueEntry {\n id: string;\n content: string;\n scheduledFor: string;\n status: \"pending\" | \"posted\" | \"failed\" | \"expired\";\n createdAt: string;\n postedAt?: string;\n error?: string;\n}\n\ninterface QueueData {\n entries: QueueEntry[];\n}\n\nfunction loadQueue(): QueueData {\n if (!existsSync(paths.pendingPosts)) {\n return { entries: [] };\n }\n return JSON.parse(readFileSync(paths.pendingPosts, \"utf-8\")) as QueueData;\n}\n\nfunction saveQueue(data: QueueData): void {\n ensureDirectories();\n writeFileSync(paths.pendingPosts, JSON.stringify(data, null, 2));\n}\n\nfunction nextScheduledTime(): string {\n const config = loadConfig();\n const now = new Date();\n const queue = loadQueue();\n\n // Find the latest scheduled time in the queue\n const pendingEntries = queue.entries.filter((e) => e.status === \"pending\");\n let lastScheduled = now;\n\n if (pendingEntries.length > 0) {\n const latest = new Date(\n pendingEntries.reduce((max, e) =>\n new Date(e.scheduledFor) > new Date(max.scheduledFor) ? e : max\n ).scheduledFor\n );\n if (latest > lastScheduled) lastScheduled = latest;\n }\n\n // Add a random interval within the active hours\n const intervalMinutes = Math.floor(\n ((config.schedule.activeHoursEnd - config.schedule.activeHoursStart) * 60) /\n config.schedule.postsPerDay\n );\n\n const next = new Date(lastScheduled.getTime() + intervalMinutes * 60 * 1000);\n\n // Clamp to active hours\n if (next.getHours() >= config.schedule.activeHoursEnd) {\n next.setDate(next.getDate() + 1);\n next.setHours(config.schedule.activeHoursStart, Math.floor(Math.random() * 60), 0, 0);\n }\n if (next.getHours() < config.schedule.activeHoursStart) {\n next.setHours(config.schedule.activeHoursStart, Math.floor(Math.random() * 60), 0, 0);\n }\n\n return next.toISOString();\n}\n\nexport function addToQueue(content: string, scheduledFor?: string): QueueEntry {\n const queue = loadQueue();\n\n const entry: QueueEntry = {\n id: `post-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,\n content,\n scheduledFor: scheduledFor ?? nextScheduledTime(),\n status: \"pending\",\n createdAt: new Date().toISOString(),\n };\n\n queue.entries.push(entry);\n saveQueue(queue);\n\n logger.info(`Post queued: ${entry.id} scheduled for ${entry.scheduledFor}`);\n return entry;\n}\n\nexport async function flushQueue(): Promise<{\n posted: number;\n failed: number;\n remaining: number;\n}> {\n const queue = loadQueue();\n const now = new Date();\n let posted = 0;\n let failed = 0;\n\n const { getXClient } = await import(\"../x-client/index.js\");\n const client = await getXClient();\n\n for (const entry of queue.entries) {\n if (entry.status !== \"pending\") continue;\n if (new Date(entry.scheduledFor) > now) continue;\n\n try {\n const result = await client.postTweet(entry.content);\n if (result.success) {\n entry.status = \"posted\";\n entry.postedAt = new Date().toISOString();\n posted++;\n logger.info(`Posted: ${entry.id}`);\n } else {\n entry.status = \"failed\";\n entry.error = result.error;\n failed++;\n logger.warn(`Failed to post: ${entry.id} - ${result.error}`);\n }\n } catch (error) {\n entry.status = \"failed\";\n entry.error = (error as Error).message;\n failed++;\n }\n\n // Small delay between posts to avoid rate limits\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n\n saveQueue(queue);\n\n const remaining = queue.entries.filter((e) => e.status === \"pending\").length;\n return { posted, failed, remaining };\n}\n\nexport function showQueue(): void {\n const queue = loadQueue();\n const pending = queue.entries.filter((e) => e.status === \"pending\");\n\n if (pending.length === 0) {\n console.log(\"Queue is empty.\");\n return;\n }\n\n console.log(`\\n${pending.length} posts queued:\\n`);\n for (const entry of pending.sort(\n (a, b) => new Date(a.scheduledFor).getTime() - new Date(b.scheduledFor).getTime()\n )) {\n const time = new Date(entry.scheduledFor).toLocaleString();\n const preview = entry.content.length > 60 ? entry.content.slice(0, 60) + \"...\" : entry.content;\n console.log(` [${time}] ${preview}`);\n }\n console.log();\n}\n"],"mappings":"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","names":[]}
|