v0.3.0: rename app Daybook -> AgentFeed
Removes the "Daybook" name from everything user- and agent-facing, since no agents are configured yet and this is the cheap moment to do it. - UI brand, docs, README, CLAUDE.md -> AgentFeed. - Ingest env vars DAYBOOK_URL/TOKEN/SOURCE/DATA_DIR/BIND_ADDR/HOST_DATA_DIR/ KEY_PROVISIONING_API -> AGENTFEED_* (token *values* unchanged). - Skill: skills/daybook-posting -> skills/agentfeed-posting, daybook_post.py -> agentfeed_post.py (+ scripts/ wrapper and its test). - static/daybook.css -> static/agentfeed.css; localStorage daybook.* -> agentfeed.*; templatetag daybook_extras -> agentfeed_extras; multipart boundaries; json/csv viewer element ids; daybook_exception_handler -> agentfeed_exception_handler; compose service/container -> agentfeed. - Key-provisioning API now OFF by default (independent of DEBUG); enable with AGENTFEED_KEY_PROVISIONING_API=1 on test installs only. - Posting helper now points at Settings -> API Keys. Deferred on purpose (a later, separate step): the Django package `daybook_project`, the `/opt/daybook` install path, and the Forgejo repo name. The sibling AGENT_DAYBOOK__LLM_TEST name is intentionally untouched. Full suite: 129 passing; live server verified serving under the new name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4cacd8f64e
commit
d30e2436ab
39 changed files with 303 additions and 284 deletions
|
|
@ -8,7 +8,7 @@ from artifacts.models import ApiKey
|
|||
User = get_user_model()
|
||||
|
||||
|
||||
@override_settings(DAYBOOK_KEY_PROVISIONING_API=True)
|
||||
@override_settings(AGENTFEED_KEY_PROVISIONING_API=True)
|
||||
class ApiKeyProvisioningEnabledTestCase(APITestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(username="alice", password="pw")
|
||||
|
|
@ -78,7 +78,7 @@ class ApiKeyProvisioningEnabledTestCase(APITestCase):
|
|||
self.assertEqual(response.data["methods"]["/api/keys"], ["GET", "POST"])
|
||||
|
||||
|
||||
@override_settings(DAYBOOK_KEY_PROVISIONING_API=False)
|
||||
@override_settings(AGENTFEED_KEY_PROVISIONING_API=False)
|
||||
class ApiKeyProvisioningDisabledTestCase(APITestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(username="alice", password="pw")
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class ArtifactAPITestCase(APITestCase):
|
|||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.artifacts_root = Path(tempfile.mkdtemp(prefix="daybook-test-artifacts-"))
|
||||
cls.artifacts_root = Path(tempfile.mkdtemp(prefix="agentfeed-test-artifacts-"))
|
||||
cls.settings_override = override_settings(ARTIFACTS_ROOT=cls.artifacts_root)
|
||||
cls.settings_override.enable()
|
||||
|
||||
|
|
@ -96,9 +96,9 @@ class ArtifactAPITestCase(APITestCase):
|
|||
body = response.content.decode()
|
||||
# Frontmatter is intact and the config placeholder is rewritten to this
|
||||
# instance's origin (http://testserver under the test client).
|
||||
self.assertIn("name: daybook-posting", body)
|
||||
self.assertIn("DAYBOOK_URL=http://testserver", body)
|
||||
self.assertNotIn("your-daybook-instance.example.com", body)
|
||||
self.assertIn("name: agentfeed-posting", body)
|
||||
self.assertIn("AGENTFEED_URL=http://testserver", body)
|
||||
self.assertNotIn("your-agentfeed-instance.example.com", body)
|
||||
|
||||
def test_skill_json_format(self):
|
||||
response = self.client.get(
|
||||
|
|
@ -107,7 +107,7 @@ class ArtifactAPITestCase(APITestCase):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["version"], __version__)
|
||||
self.assertEqual(response.data["url"], "http://testserver")
|
||||
self.assertIn("name: daybook-posting", response.data["skill"])
|
||||
self.assertIn("name: agentfeed-posting", response.data["skill"])
|
||||
|
||||
def test_skill_with_session(self):
|
||||
self.client.force_login(self.user)
|
||||
|
|
@ -442,7 +442,7 @@ class ArtifactAPITestCase(APITestCase):
|
|||
self.assertEqual(stored_html.count("</script>"), 3)
|
||||
|
||||
# The escaped content must still round-trip to the exact original value.
|
||||
start = stored_html.index('id="daybook-data">') + len('id="daybook-data">')
|
||||
start = stored_html.index('id="agentfeed-data">') + len('id="agentfeed-data">')
|
||||
end = stored_html.index("</script>", start)
|
||||
embedded = stored_html[start:end]
|
||||
self.assertEqual(json.loads(embedded)["value"], "</script><script>alert(1)</script>")
|
||||
|
|
|
|||
|
|
@ -6,76 +6,76 @@ from pathlib import Path
|
|||
SCRIPT_PATH = (
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "skills"
|
||||
/ "daybook-posting"
|
||||
/ "agentfeed-posting"
|
||||
/ "scripts"
|
||||
/ "daybook_post.py"
|
||||
/ "agentfeed_post.py"
|
||||
)
|
||||
|
||||
|
||||
def _load_module():
|
||||
spec = importlib.util.spec_from_file_location("daybook_post", SCRIPT_PATH)
|
||||
spec = importlib.util.spec_from_file_location("agentfeed_post", SCRIPT_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
daybook_post = _load_module()
|
||||
agentfeed_post = _load_module()
|
||||
|
||||
|
||||
class SniffTextKindTestCase(unittest.TestCase):
|
||||
def test_markdown_suffixes(self):
|
||||
for suffix in (".md", ".markdown", ".txt"):
|
||||
self.assertEqual(daybook_post.sniff_text_kind(suffix, "anything"), "markdown")
|
||||
self.assertEqual(agentfeed_post.sniff_text_kind(suffix, "anything"), "markdown")
|
||||
|
||||
def test_json_suffix(self):
|
||||
self.assertEqual(daybook_post.sniff_text_kind(".json", '{"a": 1}'), "json")
|
||||
self.assertEqual(agentfeed_post.sniff_text_kind(".json", '{"a": 1}'), "json")
|
||||
|
||||
def test_csv_suffix(self):
|
||||
self.assertEqual(daybook_post.sniff_text_kind(".csv", "a,b\n1,2"), "csv")
|
||||
self.assertEqual(agentfeed_post.sniff_text_kind(".csv", "a,b\n1,2"), "csv")
|
||||
|
||||
def test_html_full_sniffed_from_content(self):
|
||||
self.assertEqual(
|
||||
daybook_post.sniff_text_kind(".html", "<!doctype html><html></html>"), "html_full"
|
||||
agentfeed_post.sniff_text_kind(".html", "<!doctype html><html></html>"), "html_full"
|
||||
)
|
||||
|
||||
def test_deck_sniffed_from_content(self):
|
||||
self.assertEqual(daybook_post.sniff_text_kind(".html", "<section>slide</section>"), "deck")
|
||||
self.assertEqual(agentfeed_post.sniff_text_kind(".html", "<section>slide</section>"), "deck")
|
||||
|
||||
def test_html_fragment_is_the_fallback(self):
|
||||
self.assertEqual(daybook_post.sniff_text_kind(".html", "<p>hi</p>"), "html_fragment")
|
||||
self.assertEqual(agentfeed_post.sniff_text_kind(".html", "<p>hi</p>"), "html_fragment")
|
||||
|
||||
|
||||
class ValidatePayloadTestCase(unittest.TestCase):
|
||||
def test_valid_json_passes(self):
|
||||
daybook_post.validate_payload({"kind": "json", "content": '{"a": 1}'}, None) # no raise
|
||||
agentfeed_post.validate_payload({"kind": "json", "content": '{"a": 1}'}, None) # no raise
|
||||
|
||||
def test_invalid_json_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
daybook_post.validate_payload({"kind": "json", "content": "{not json"}, None)
|
||||
agentfeed_post.validate_payload({"kind": "json", "content": "{not json"}, None)
|
||||
|
||||
def test_deck_without_section_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
daybook_post.validate_payload({"kind": "deck", "content": "<h1>no slides</h1>"}, None)
|
||||
agentfeed_post.validate_payload({"kind": "deck", "content": "<h1>no slides</h1>"}, None)
|
||||
|
||||
def test_html_fragment_with_nested_html_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
daybook_post.validate_payload(
|
||||
agentfeed_post.validate_payload(
|
||||
{"kind": "html_fragment", "content": "<html><body>x</body></html>"}, None
|
||||
)
|
||||
|
||||
def test_html_full_without_html_tag_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
daybook_post.validate_payload({"kind": "html_full", "content": "<p>x</p>"}, None)
|
||||
agentfeed_post.validate_payload({"kind": "html_full", "content": "<p>x</p>"}, None)
|
||||
|
||||
def test_blank_markdown_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
daybook_post.validate_payload({"kind": "markdown", "content": " "}, None)
|
||||
agentfeed_post.validate_payload({"kind": "markdown", "content": " "}, None)
|
||||
|
||||
def test_csv_has_no_shape_check(self):
|
||||
daybook_post.validate_payload({"kind": "csv", "content": "anything at all"}, None) # no raise
|
||||
agentfeed_post.validate_payload({"kind": "csv", "content": "anything at all"}, None) # no raise
|
||||
|
||||
def test_upload_path_skips_all_checks(self):
|
||||
daybook_post.validate_payload({"upload_path": Path("whatever.png")}, None) # no raise
|
||||
agentfeed_post.validate_payload({"upload_path": Path("whatever.png")}, None) # no raise
|
||||
|
||||
|
||||
class ResolveAutoPathTestCase(unittest.TestCase):
|
||||
|
|
@ -93,7 +93,7 @@ class ResolveAutoPathTestCase(unittest.TestCase):
|
|||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = Path(tmp) / "data.json"
|
||||
path.write_text('{"a": 1}', encoding="utf-8")
|
||||
args = daybook_post.resolve_auto_path(self._args(path))
|
||||
args = agentfeed_post.resolve_auto_path(self._args(path))
|
||||
self.assertEqual(args.kind, "json")
|
||||
self.assertEqual(args.content_file, str(path))
|
||||
|
||||
|
|
@ -101,14 +101,14 @@ class ResolveAutoPathTestCase(unittest.TestCase):
|
|||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = Path(tmp) / "data.csv"
|
||||
path.write_text("a,b\n1,2", encoding="utf-8")
|
||||
args = daybook_post.resolve_auto_path(self._args(path))
|
||||
args = agentfeed_post.resolve_auto_path(self._args(path))
|
||||
self.assertEqual(args.kind, "csv")
|
||||
|
||||
def test_image_file_becomes_multipart_upload(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = Path(tmp) / "chart.png"
|
||||
path.write_bytes(b"\x89PNG\r\n")
|
||||
args = daybook_post.resolve_auto_path(self._args(path))
|
||||
args = agentfeed_post.resolve_auto_path(self._args(path))
|
||||
self.assertIsNone(args.kind)
|
||||
self.assertEqual(args.file, str(path))
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ class MakeRoomForTestCase(TestCase):
|
|||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.artifacts_root = Path(tempfile.mkdtemp(prefix="daybook-test-storage-"))
|
||||
cls.artifacts_root = Path(tempfile.mkdtemp(prefix="agentfeed-test-storage-"))
|
||||
cls.settings_override = override_settings(ARTIFACTS_ROOT=cls.artifacts_root)
|
||||
cls.settings_override.enable()
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class SharingTestCase(TestCase):
|
|||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.artifacts_root = Path(tempfile.mkdtemp(prefix="daybook-test-sharing-"))
|
||||
cls.artifacts_root = Path(tempfile.mkdtemp(prefix="agentfeed-test-sharing-"))
|
||||
cls.settings_override = override_settings(ARTIFACTS_ROOT=cls.artifacts_root)
|
||||
cls.settings_override.enable()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue