popilot 0.6.0 → 0.8.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.mjs +204 -2
- package/lib/doctor.mjs +38 -1
- package/lib/hydrate.mjs +15 -0
- package/lib/scaffold.mjs +5 -0
- package/lib/setup-wizard.mjs +35 -2
- package/package.json +1 -1
- package/scaffold/.context/project.yaml.example +19 -0
- package/scaffold/mcp-notification-server/package.json +18 -0
- package/scaffold/mcp-notification-server/src/index.ts +275 -0
- package/scaffold/mcp-notification-server/src/turso-client.ts +142 -0
- package/scaffold/mcp-notification-server/tsconfig.json +14 -0
- package/scaffold/mcp-pm/package.json +19 -0
- package/scaffold/mcp-pm/src/api-client.ts +69 -0
- package/scaffold/mcp-pm/src/index.ts +660 -0
- package/scaffold/mcp-pm/tsconfig.json +14 -0
- package/scaffold/pm-api/package.json +21 -0
- package/scaffold/pm-api/sql/001-memo-v2.sql +49 -0
- package/scaffold/pm-api/sql/002-notifications.sql +18 -0
- package/scaffold/pm-api/sql/003-content.sql +66 -0
- package/scaffold/pm-api/sql/004-agent-events.sql +21 -0
- package/scaffold/pm-api/sql/005-epic-sprint-decoupling.sql +6 -0
- package/scaffold/pm-api/sql/schema-core.sql +331 -0
- package/scaffold/pm-api/sql/schema-docs.sql +25 -0
- package/scaffold/pm-api/sql/schema-meetings.sql +17 -0
- package/scaffold/pm-api/sql/schema-rewards.sql +16 -0
- package/scaffold/pm-api/src/auth.ts +28 -0
- package/scaffold/pm-api/src/blockchain/adapter.ts +20 -0
- package/scaffold/pm-api/src/blockchain/tron.ts +62 -0
- package/scaffold/pm-api/src/db/adapter.ts +36 -0
- package/scaffold/pm-api/src/db/turso.ts +147 -0
- package/scaffold/pm-api/src/index.ts +114 -0
- package/scaffold/pm-api/src/mcp-tools/dashboard.ts +40 -0
- package/scaffold/pm-api/src/mcp-tools/epic.ts +67 -0
- package/scaffold/pm-api/src/mcp-tools/event.ts +89 -0
- package/scaffold/pm-api/src/mcp-tools/index.ts +11 -0
- package/scaffold/pm-api/src/mcp-tools/initiative.ts +51 -0
- package/scaffold/pm-api/src/mcp-tools/memo.ts +164 -0
- package/scaffold/pm-api/src/mcp-tools/notification.ts +37 -0
- package/scaffold/pm-api/src/mcp-tools/retro.ts +183 -0
- package/scaffold/pm-api/src/mcp-tools/sprint.ts +204 -0
- package/scaffold/pm-api/src/mcp-tools/standup.ts +136 -0
- package/scaffold/pm-api/src/mcp-tools/story.ts +230 -0
- package/scaffold/pm-api/src/mcp-tools/task.ts +187 -0
- package/scaffold/pm-api/src/mcp-tools/utils.ts +83 -0
- package/scaffold/pm-api/src/mcp.ts +871 -0
- package/scaffold/pm-api/src/nudge.ts +283 -0
- package/scaffold/pm-api/src/routes/auth.ts +32 -0
- package/scaffold/pm-api/src/routes/v2-activity.ts +27 -0
- package/scaffold/pm-api/src/routes/v2-admin.ts +165 -0
- package/scaffold/pm-api/src/routes/v2-dashboard.ts +189 -0
- package/scaffold/pm-api/src/routes/v2-docs.ts +34 -0
- package/scaffold/pm-api/src/routes/v2-initiatives.ts +118 -0
- package/scaffold/pm-api/src/routes/v2-kickoff.ts +265 -0
- package/scaffold/pm-api/src/routes/v2-meetings.ts +324 -0
- package/scaffold/pm-api/src/routes/v2-memos.ts +257 -0
- package/scaffold/pm-api/src/routes/v2-nav.ts +260 -0
- package/scaffold/pm-api/src/routes/v2-notifications.ts +79 -0
- package/scaffold/pm-api/src/routes/v2-page-content.ts +35 -0
- package/scaffold/pm-api/src/routes/v2-pm.ts +380 -0
- package/scaffold/pm-api/src/routes/v2-policy.ts +58 -0
- package/scaffold/pm-api/src/routes/v2-retro.ts +221 -0
- package/scaffold/pm-api/src/routes/v2-rewards.ts +132 -0
- package/scaffold/pm-api/src/routes/v2-scenarios.ts +48 -0
- package/scaffold/pm-api/src/routes/v2-search.ts +32 -0
- package/scaffold/pm-api/src/routes/v2-standup.ts +127 -0
- package/scaffold/pm-api/src/routes/v2-user.ts +38 -0
- package/scaffold/pm-api/src/types.ts +11 -0
- package/scaffold/pm-api/src/utils/activity.ts +22 -0
- package/scaffold/pm-api/src/utils/admin.ts +9 -0
- package/scaffold/pm-api/src/utils/agent-notify.ts +62 -0
- package/scaffold/pm-api/src/utils/assignee.ts +69 -0
- package/scaffold/pm-api/src/utils/db.ts +45 -0
- package/scaffold/pm-api/src/utils/initiative.ts +23 -0
- package/scaffold/pm-api/src/utils/retro-link.ts +32 -0
- package/scaffold/pm-api/src/utils/sprint-lifecycle.ts +96 -0
- package/scaffold/pm-api/tsconfig.json +15 -0
- package/scaffold/pm-api/wrangler.toml.hbs +11 -0
- package/scaffold/spec-site/package-lock.json +892 -0
- package/scaffold/spec-site/package.json +15 -1
- package/scaffold/spec-site/src/api/types.ts +6 -0
- package/scaffold/spec-site/src/components/AppHeader.vue +429 -55
- package/scaffold/spec-site/src/components/AuthGate.vue +117 -0
- package/scaffold/spec-site/src/components/BurndownChart.vue +78 -0
- package/scaffold/spec-site/src/components/DocComments.vue +137 -0
- package/scaffold/spec-site/src/components/DocEditor.vue +118 -0
- package/scaffold/spec-site/src/components/DocExportBar.vue +110 -0
- package/scaffold/spec-site/src/components/DocsSidebar.vue +309 -0
- package/scaffold/spec-site/src/components/EmptyState.vue +30 -0
- package/scaffold/spec-site/src/components/ErrorBanner.vue +38 -0
- package/scaffold/spec-site/src/components/Icon.vue +58 -0
- package/scaffold/spec-site/src/components/MemberSelect.vue +48 -0
- package/scaffold/spec-site/src/components/MemoChecklist.vue +88 -0
- package/scaffold/spec-site/src/components/MemoGraph.vue +75 -0
- package/scaffold/spec-site/src/components/MemoItem.vue +353 -0
- package/scaffold/spec-site/src/components/MemoRelations.vue +101 -0
- package/scaffold/spec-site/src/components/MemoTimeline.vue +53 -0
- package/scaffold/spec-site/src/components/MentionInput.vue +174 -0
- package/scaffold/spec-site/src/components/NotificationDropdown.vue +116 -0
- package/scaffold/spec-site/src/components/PriorityBadge.vue +23 -0
- package/scaffold/spec-site/src/components/SearchModal.vue +102 -0
- package/scaffold/spec-site/src/components/SlashCommand.ts +123 -0
- package/scaffold/spec-site/src/components/StateDisplay.vue +54 -0
- package/scaffold/spec-site/src/components/TreeNode.vue +82 -0
- package/scaffold/spec-site/src/components/UserAvatar.vue +24 -0
- package/scaffold/spec-site/src/components/VelocityChart.vue +77 -0
- package/scaffold/spec-site/src/composables/navTypes.ts +3 -0
- package/scaffold/spec-site/src/composables/pmTypes.ts +15 -2
- package/scaffold/spec-site/src/composables/useBottomSheet.ts +103 -0
- package/scaffold/spec-site/src/composables/useDashboard.ts +221 -0
- package/scaffold/spec-site/src/composables/useMediaQuery.ts +28 -0
- package/scaffold/spec-site/src/composables/useMemo.ts +39 -0
- package/scaffold/spec-site/src/composables/useNotification.ts +200 -0
- package/scaffold/spec-site/src/composables/usePmStore.ts +48 -1
- package/scaffold/spec-site/src/composables/useRetro.ts +6 -0
- package/scaffold/spec-site/src/composables/useStandup.ts +201 -0
- package/scaffold/spec-site/src/composables/useTheme.ts +37 -0
- package/scaffold/spec-site/src/composables/useTurso.ts +17 -0
- package/scaffold/spec-site/src/composables/useUser.ts +19 -1
- package/scaffold/spec-site/src/composables/useViewport.ts +26 -0
- package/scaffold/spec-site/src/features.ts +108 -0
- package/scaffold/spec-site/src/mockup/ComponentPalette.vue +61 -0
- package/scaffold/spec-site/src/mockup/MockupCanvas.vue +459 -0
- package/scaffold/spec-site/src/mockup/PropertyPanel.vue +217 -0
- package/scaffold/spec-site/src/mockup/componentCatalog.ts +68 -0
- package/scaffold/spec-site/src/mockup/useScenarios.ts +67 -0
- package/scaffold/spec-site/src/pages/AdminPage.vue +299 -0
- package/scaffold/spec-site/src/pages/DashboardPage.vue +650 -0
- package/scaffold/spec-site/src/pages/DocsEditor.vue +119 -0
- package/scaffold/spec-site/src/pages/DocsHub.vue +157 -0
- package/scaffold/spec-site/src/pages/DocsPage.vue +444 -0
- package/scaffold/spec-site/src/pages/InboxPage.vue +156 -0
- package/scaffold/spec-site/src/pages/MeetingsPage.vue +294 -0
- package/scaffold/spec-site/src/pages/MemosPage.vue +857 -0
- package/scaffold/spec-site/src/pages/MockupEditorPage.vue +611 -0
- package/scaffold/spec-site/src/pages/MockupListPage.vue +121 -0
- package/scaffold/spec-site/src/pages/MockupViewerPage.vue +199 -0
- package/scaffold/spec-site/src/pages/MyPage.vue +343 -0
- package/scaffold/spec-site/src/pages/NotificationSettingsPage.vue +59 -0
- package/scaffold/spec-site/src/pages/RewardsPage.vue +266 -0
- package/scaffold/spec-site/src/pages/SprintAdmin.vue +521 -0
- package/scaffold/spec-site/src/pages/SprintTimeline.vue +159 -0
- package/scaffold/spec-site/src/pages/board/BoardAdmin.vue +422 -0
- package/scaffold/spec-site/src/pages/board/BoardEpicSection.vue +54 -0
- package/scaffold/spec-site/src/pages/board/BoardPage.vue +884 -0
- package/scaffold/spec-site/src/pages/board/BoardStoryCard.vue +67 -0
- package/scaffold/spec-site/src/pages/board/BoardTaskItem.vue +52 -0
- package/scaffold/spec-site/src/pages/board/KanbanBoard.vue +93 -0
- package/scaffold/spec-site/src/pages/board/MyTasksPage.vue +202 -0
- package/scaffold/spec-site/src/pages/board/SprintClose.vue +167 -0
- package/scaffold/spec-site/src/pages/board/SprintColumn.vue +49 -0
- package/scaffold/spec-site/src/pages/board/SprintKickoff.vue +389 -0
- package/scaffold/spec-site/src/pages/board/StatusBadge.vue +52 -0
- package/scaffold/spec-site/src/pages/board/StoryDetailPanel.vue +495 -0
- package/scaffold/spec-site/src/pages/board/TaskCard.vue +42 -0
- package/scaffold/spec-site/src/pages/retro/RetroCard.vue +36 -2
- package/scaffold/spec-site/src/pages/retro/RetroHeader.vue +82 -66
- package/scaffold/spec-site/src/pages/retro/RetroPage.vue +47 -18
- package/scaffold/spec-site/src/pages/standup/StandupEntryCard.vue +551 -0
- package/scaffold/spec-site/src/pages/standup/StandupForm.vue +68 -0
- package/scaffold/spec-site/src/pages/standup/StandupList.vue +71 -0
- package/scaffold/spec-site/src/pages/standup/StandupPage.vue +225 -0
- package/scaffold/spec-site/src/router.ts +141 -0
- package/scaffold/spec-site/src/styles/buttons.css +124 -0
- package/scaffold/spec-site/src/utils/parseMentions.ts +56 -0
- package/scaffold/spec-site/src/utils/timezone.ts +18 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
-- Memo v2 migration — authentication, user activity, memos, replies
|
|
2
|
+
|
|
3
|
+
-- 1. Auth tokens
|
|
4
|
+
CREATE TABLE IF NOT EXISTS auth_tokens (
|
|
5
|
+
token TEXT PRIMARY KEY,
|
|
6
|
+
user_name TEXT NOT NULL,
|
|
7
|
+
user_email TEXT,
|
|
8
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
9
|
+
expires_at TIMESTAMP,
|
|
10
|
+
is_active INTEGER DEFAULT 1
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
-- 2. User activity tracking
|
|
14
|
+
CREATE TABLE IF NOT EXISTS user_activity (
|
|
15
|
+
user_name TEXT PRIMARY KEY,
|
|
16
|
+
last_seen_at TIMESTAMP,
|
|
17
|
+
last_memo_seen TIMESTAMP,
|
|
18
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
-- 3. Memos v2
|
|
22
|
+
CREATE TABLE IF NOT EXISTS memos_v2 (
|
|
23
|
+
id INTEGER PRIMARY KEY,
|
|
24
|
+
page_id TEXT NOT NULL,
|
|
25
|
+
content TEXT NOT NULL,
|
|
26
|
+
memo_type TEXT DEFAULT 'memo',
|
|
27
|
+
status TEXT DEFAULT 'open',
|
|
28
|
+
created_by TEXT NOT NULL,
|
|
29
|
+
assigned_to TEXT,
|
|
30
|
+
resolved_by TEXT,
|
|
31
|
+
resolved_at TIMESTAMP,
|
|
32
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
33
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_memos_v2_page ON memos_v2(page_id);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_memos_v2_assigned ON memos_v2(assigned_to, status);
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_memos_v2_type ON memos_v2(memo_type, status);
|
|
39
|
+
|
|
40
|
+
-- 4. Memo replies
|
|
41
|
+
CREATE TABLE IF NOT EXISTS memo_replies (
|
|
42
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
43
|
+
memo_id INTEGER NOT NULL,
|
|
44
|
+
content TEXT NOT NULL,
|
|
45
|
+
created_by TEXT NOT NULL,
|
|
46
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_memo_replies_memo ON memo_replies(memo_id);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- Notifications table for per-user alert management
|
|
2
|
+
|
|
3
|
+
CREATE TABLE IF NOT EXISTS notifications (
|
|
4
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
+
user_name TEXT NOT NULL,
|
|
6
|
+
type TEXT NOT NULL DEFAULT 'memo_assigned',
|
|
7
|
+
title TEXT NOT NULL,
|
|
8
|
+
body TEXT,
|
|
9
|
+
source_type TEXT NOT NULL DEFAULT 'memo',
|
|
10
|
+
source_id INTEGER NOT NULL,
|
|
11
|
+
page_id TEXT NOT NULL,
|
|
12
|
+
actor TEXT NOT NULL,
|
|
13
|
+
is_read INTEGER DEFAULT 0,
|
|
14
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_user_unread ON notifications(user_name, is_read, created_at DESC);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_source ON notifications(source_type, source_id);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
-- Spec-site content schema — rules, scenarios, areas, versions, wireframe meta
|
|
2
|
+
|
|
3
|
+
-- 1. Rules (flat, individually queryable)
|
|
4
|
+
CREATE TABLE IF NOT EXISTS spec_rules (
|
|
5
|
+
id TEXT NOT NULL,
|
|
6
|
+
page_id TEXT NOT NULL,
|
|
7
|
+
rule_group TEXT NOT NULL,
|
|
8
|
+
category TEXT NOT NULL,
|
|
9
|
+
name TEXT NOT NULL,
|
|
10
|
+
condition TEXT NOT NULL,
|
|
11
|
+
severity TEXT NOT NULL,
|
|
12
|
+
home_message TEXT NOT NULL DEFAULT '',
|
|
13
|
+
action TEXT NOT NULL DEFAULT '',
|
|
14
|
+
data_source TEXT NOT NULL DEFAULT '',
|
|
15
|
+
impl_status TEXT NOT NULL DEFAULT 'logic-needed',
|
|
16
|
+
impl_note TEXT,
|
|
17
|
+
action_route TEXT,
|
|
18
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
19
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
20
|
+
PRIMARY KEY (page_id, id)
|
|
21
|
+
);
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_spec_rules_page ON spec_rules(page_id);
|
|
23
|
+
|
|
24
|
+
-- 2. Scenarios
|
|
25
|
+
CREATE TABLE IF NOT EXISTS spec_scenarios (
|
|
26
|
+
page_id TEXT NOT NULL,
|
|
27
|
+
scenario_id TEXT NOT NULL,
|
|
28
|
+
label TEXT NOT NULL,
|
|
29
|
+
data_json TEXT NOT NULL,
|
|
30
|
+
is_default INTEGER DEFAULT 0,
|
|
31
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
32
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
33
|
+
PRIMARY KEY (page_id, scenario_id)
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
-- 3. Spec areas
|
|
37
|
+
CREATE TABLE IF NOT EXISTS spec_areas (
|
|
38
|
+
page_id TEXT NOT NULL,
|
|
39
|
+
area_id TEXT NOT NULL,
|
|
40
|
+
label TEXT NOT NULL,
|
|
41
|
+
short_label TEXT NOT NULL,
|
|
42
|
+
rule_count INTEGER NOT NULL DEFAULT 0,
|
|
43
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
44
|
+
PRIMARY KEY (page_id, area_id)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- 4. Page versions
|
|
48
|
+
CREATE TABLE IF NOT EXISTS spec_versions (
|
|
49
|
+
page_id TEXT PRIMARY KEY,
|
|
50
|
+
version TEXT NOT NULL,
|
|
51
|
+
last_updated TEXT NOT NULL,
|
|
52
|
+
sprint TEXT NOT NULL,
|
|
53
|
+
status TEXT NOT NULL DEFAULT 'draft',
|
|
54
|
+
changelog TEXT NOT NULL DEFAULT '[]',
|
|
55
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
-- 5. Wireframe metadata
|
|
59
|
+
CREATE TABLE IF NOT EXISTS spec_wireframe_meta (
|
|
60
|
+
page_id TEXT NOT NULL,
|
|
61
|
+
sprint TEXT NOT NULL,
|
|
62
|
+
default_scenario_id TEXT NOT NULL,
|
|
63
|
+
spec_title TEXT NOT NULL,
|
|
64
|
+
route_title TEXT NOT NULL,
|
|
65
|
+
PRIMARY KEY (page_id, sprint)
|
|
66
|
+
);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Agent events table for push-based inter-agent communication
|
|
2
|
+
|
|
3
|
+
CREATE TABLE IF NOT EXISTS agent_events (
|
|
4
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
+
event_type TEXT NOT NULL,
|
|
6
|
+
source_agent TEXT NOT NULL,
|
|
7
|
+
target_agent TEXT NOT NULL,
|
|
8
|
+
target_user TEXT NOT NULL,
|
|
9
|
+
payload TEXT NOT NULL,
|
|
10
|
+
status TEXT DEFAULT 'pending',
|
|
11
|
+
delivered_at TIMESTAMP,
|
|
12
|
+
acked_at TIMESTAMP,
|
|
13
|
+
expires_at TIMESTAMP,
|
|
14
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_events_target
|
|
18
|
+
ON agent_events(target_user, status, created_at DESC);
|
|
19
|
+
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_events_type
|
|
21
|
+
ON agent_events(event_type, status);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- Epic-Sprint decoupling migration
|
|
2
|
+
-- Purpose: Make pm_epics the SSOT, remove nav_epics dependency
|
|
3
|
+
-- Epics are global; stories move between sprints.
|
|
4
|
+
|
|
5
|
+
ALTER TABLE pm_epics ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0;
|
|
6
|
+
ALTER TABLE pm_epics ADD COLUMN origin_sprint TEXT;
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
-- PM API Core Schema
|
|
2
|
+
-- Run this SQL to initialize the database
|
|
3
|
+
|
|
4
|
+
-- Authentication
|
|
5
|
+
CREATE TABLE IF NOT EXISTS auth_tokens (
|
|
6
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
+
user_name TEXT NOT NULL,
|
|
8
|
+
token TEXT NOT NULL UNIQUE,
|
|
9
|
+
is_active INTEGER DEFAULT 1,
|
|
10
|
+
expires_at TEXT,
|
|
11
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
12
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
-- Team Members
|
|
16
|
+
CREATE TABLE IF NOT EXISTS members (
|
|
17
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
+
display_name TEXT NOT NULL UNIQUE,
|
|
19
|
+
role TEXT DEFAULT 'member',
|
|
20
|
+
is_active INTEGER DEFAULT 1,
|
|
21
|
+
webhook_url TEXT,
|
|
22
|
+
wallet_address TEXT,
|
|
23
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
24
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
-- Sprints
|
|
28
|
+
CREATE TABLE IF NOT EXISTS nav_sprints (
|
|
29
|
+
id TEXT PRIMARY KEY,
|
|
30
|
+
title TEXT NOT NULL,
|
|
31
|
+
theme TEXT,
|
|
32
|
+
status TEXT DEFAULT 'planning',
|
|
33
|
+
active INTEGER DEFAULT 0,
|
|
34
|
+
start_date TEXT,
|
|
35
|
+
end_date TEXT,
|
|
36
|
+
velocity INTEGER,
|
|
37
|
+
team_size INTEGER,
|
|
38
|
+
sort_order INTEGER DEFAULT 0,
|
|
39
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
40
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
-- Sprint Members
|
|
44
|
+
CREATE TABLE IF NOT EXISTS sprint_members (
|
|
45
|
+
sprint_id TEXT NOT NULL,
|
|
46
|
+
member_id INTEGER NOT NULL,
|
|
47
|
+
working_days INTEGER DEFAULT 0,
|
|
48
|
+
PRIMARY KEY (sprint_id, member_id),
|
|
49
|
+
FOREIGN KEY (sprint_id) REFERENCES nav_sprints(id),
|
|
50
|
+
FOREIGN KEY (member_id) REFERENCES members(id)
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
-- Member Absences
|
|
54
|
+
CREATE TABLE IF NOT EXISTS member_absences (
|
|
55
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
56
|
+
sprint_id TEXT NOT NULL,
|
|
57
|
+
member_id INTEGER NOT NULL,
|
|
58
|
+
absence_date TEXT NOT NULL,
|
|
59
|
+
reason TEXT,
|
|
60
|
+
UNIQUE(sprint_id, member_id, absence_date),
|
|
61
|
+
FOREIGN KEY (sprint_id) REFERENCES nav_sprints(id),
|
|
62
|
+
FOREIGN KEY (member_id) REFERENCES members(id)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
-- Epics
|
|
66
|
+
CREATE TABLE IF NOT EXISTS pm_epics (
|
|
67
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
68
|
+
title TEXT NOT NULL,
|
|
69
|
+
description TEXT,
|
|
70
|
+
owner TEXT,
|
|
71
|
+
status TEXT DEFAULT 'active',
|
|
72
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
73
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
-- Stories
|
|
77
|
+
CREATE TABLE IF NOT EXISTS pm_stories (
|
|
78
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
79
|
+
epic_id INTEGER,
|
|
80
|
+
epic_uid TEXT,
|
|
81
|
+
sprint TEXT,
|
|
82
|
+
title TEXT NOT NULL,
|
|
83
|
+
description TEXT,
|
|
84
|
+
acceptance_criteria TEXT,
|
|
85
|
+
assignee TEXT,
|
|
86
|
+
assignee_id INTEGER,
|
|
87
|
+
status TEXT DEFAULT 'backlog',
|
|
88
|
+
priority TEXT DEFAULT 'medium',
|
|
89
|
+
area TEXT DEFAULT 'FE',
|
|
90
|
+
story_points INTEGER,
|
|
91
|
+
figma_url TEXT,
|
|
92
|
+
sort_order INTEGER DEFAULT 0,
|
|
93
|
+
start_date TEXT,
|
|
94
|
+
due_date TEXT,
|
|
95
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
96
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
97
|
+
FOREIGN KEY (epic_id) REFERENCES pm_epics(id),
|
|
98
|
+
FOREIGN KEY (sprint) REFERENCES nav_sprints(id),
|
|
99
|
+
FOREIGN KEY (assignee_id) REFERENCES members(id)
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
-- Tasks
|
|
103
|
+
CREATE TABLE IF NOT EXISTS pm_tasks (
|
|
104
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
105
|
+
story_id INTEGER NOT NULL,
|
|
106
|
+
title TEXT NOT NULL,
|
|
107
|
+
assignee TEXT,
|
|
108
|
+
assignee_id INTEGER,
|
|
109
|
+
status TEXT DEFAULT 'todo',
|
|
110
|
+
description TEXT,
|
|
111
|
+
story_points INTEGER,
|
|
112
|
+
sort_order INTEGER DEFAULT 0,
|
|
113
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
114
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
115
|
+
FOREIGN KEY (story_id) REFERENCES pm_stories(id),
|
|
116
|
+
FOREIGN KEY (assignee_id) REFERENCES members(id)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
-- Standup Entries
|
|
120
|
+
CREATE TABLE IF NOT EXISTS pm_standup_entries (
|
|
121
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
122
|
+
sprint TEXT NOT NULL,
|
|
123
|
+
user_name TEXT NOT NULL,
|
|
124
|
+
entry_date TEXT NOT NULL,
|
|
125
|
+
done_text TEXT,
|
|
126
|
+
plan_text TEXT,
|
|
127
|
+
plan_story_ids TEXT,
|
|
128
|
+
blockers TEXT,
|
|
129
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
130
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
131
|
+
UNIQUE(sprint, user_name, entry_date)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
-- Standup Feedback
|
|
135
|
+
CREATE TABLE IF NOT EXISTS pm_standup_feedback (
|
|
136
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
137
|
+
standup_entry_id INTEGER NOT NULL,
|
|
138
|
+
sprint TEXT NOT NULL,
|
|
139
|
+
target_user TEXT NOT NULL,
|
|
140
|
+
feedback_by TEXT NOT NULL,
|
|
141
|
+
feedback_text TEXT NOT NULL,
|
|
142
|
+
review_type TEXT DEFAULT 'comment',
|
|
143
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
144
|
+
FOREIGN KEY (standup_entry_id) REFERENCES pm_standup_entries(id)
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
-- Memos v2
|
|
148
|
+
CREATE TABLE IF NOT EXISTS memos_v2 (
|
|
149
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
150
|
+
page_id TEXT DEFAULT 'home',
|
|
151
|
+
content TEXT NOT NULL,
|
|
152
|
+
memo_type TEXT DEFAULT 'memo',
|
|
153
|
+
title TEXT,
|
|
154
|
+
created_by TEXT NOT NULL,
|
|
155
|
+
created_by_id INTEGER,
|
|
156
|
+
assigned_to TEXT,
|
|
157
|
+
assigned_to_id INTEGER,
|
|
158
|
+
status TEXT DEFAULT 'open',
|
|
159
|
+
resolved_by TEXT,
|
|
160
|
+
resolved_at TEXT,
|
|
161
|
+
related_decisions TEXT,
|
|
162
|
+
review_required INTEGER DEFAULT 0,
|
|
163
|
+
supersedes_id INTEGER,
|
|
164
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
165
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
-- Memo Replies
|
|
169
|
+
CREATE TABLE IF NOT EXISTS memo_replies (
|
|
170
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
171
|
+
memo_id INTEGER NOT NULL,
|
|
172
|
+
content TEXT NOT NULL,
|
|
173
|
+
created_by TEXT NOT NULL,
|
|
174
|
+
review_type TEXT DEFAULT 'comment',
|
|
175
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
176
|
+
FOREIGN KEY (memo_id) REFERENCES memos_v2(id)
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
-- Notifications
|
|
180
|
+
CREATE TABLE IF NOT EXISTS notifications (
|
|
181
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
182
|
+
user_name TEXT NOT NULL,
|
|
183
|
+
type TEXT NOT NULL,
|
|
184
|
+
title TEXT NOT NULL,
|
|
185
|
+
body TEXT,
|
|
186
|
+
source_type TEXT,
|
|
187
|
+
source_id TEXT,
|
|
188
|
+
page_id TEXT,
|
|
189
|
+
actor TEXT,
|
|
190
|
+
is_read INTEGER DEFAULT 0,
|
|
191
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
-- Initiatives
|
|
195
|
+
CREATE TABLE IF NOT EXISTS initiatives (
|
|
196
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
197
|
+
title TEXT NOT NULL,
|
|
198
|
+
content TEXT NOT NULL,
|
|
199
|
+
author TEXT NOT NULL,
|
|
200
|
+
decider TEXT,
|
|
201
|
+
status TEXT DEFAULT 'pending',
|
|
202
|
+
decision_note TEXT,
|
|
203
|
+
decided_at TEXT,
|
|
204
|
+
source_context TEXT,
|
|
205
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
206
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
-- Retro Sessions
|
|
210
|
+
CREATE TABLE IF NOT EXISTS retro_sessions (
|
|
211
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
212
|
+
sprint TEXT NOT NULL UNIQUE,
|
|
213
|
+
title TEXT DEFAULT 'Sprint Retrospective',
|
|
214
|
+
phase TEXT DEFAULT 'collect',
|
|
215
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
216
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
-- Retro Items
|
|
220
|
+
CREATE TABLE IF NOT EXISTS retro_items (
|
|
221
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
222
|
+
session_id INTEGER NOT NULL,
|
|
223
|
+
category TEXT NOT NULL,
|
|
224
|
+
content TEXT NOT NULL,
|
|
225
|
+
author TEXT NOT NULL,
|
|
226
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
227
|
+
FOREIGN KEY (session_id) REFERENCES retro_sessions(id)
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
-- Retro Votes
|
|
231
|
+
CREATE TABLE IF NOT EXISTS retro_votes (
|
|
232
|
+
item_id INTEGER NOT NULL,
|
|
233
|
+
voter TEXT NOT NULL,
|
|
234
|
+
PRIMARY KEY (item_id, voter),
|
|
235
|
+
FOREIGN KEY (item_id) REFERENCES retro_items(id)
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
-- Retro Actions
|
|
239
|
+
CREATE TABLE IF NOT EXISTS retro_actions (
|
|
240
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
241
|
+
session_id INTEGER NOT NULL,
|
|
242
|
+
content TEXT NOT NULL,
|
|
243
|
+
assignee TEXT,
|
|
244
|
+
status TEXT DEFAULT 'todo',
|
|
245
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
246
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
247
|
+
FOREIGN KEY (session_id) REFERENCES retro_sessions(id)
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
-- Agent Events (Push-Native)
|
|
251
|
+
CREATE TABLE IF NOT EXISTS agent_events (
|
|
252
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
253
|
+
event_type TEXT NOT NULL,
|
|
254
|
+
source_agent TEXT NOT NULL,
|
|
255
|
+
target_agent TEXT NOT NULL,
|
|
256
|
+
target_user TEXT NOT NULL,
|
|
257
|
+
payload TEXT NOT NULL,
|
|
258
|
+
status TEXT DEFAULT 'pending',
|
|
259
|
+
expires_at TEXT,
|
|
260
|
+
delivered_at TEXT,
|
|
261
|
+
acked_at TEXT,
|
|
262
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
-- Activity Log
|
|
266
|
+
CREATE TABLE IF NOT EXISTS activity_log (
|
|
267
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
268
|
+
actor TEXT NOT NULL,
|
|
269
|
+
action_type TEXT NOT NULL,
|
|
270
|
+
target_type TEXT NOT NULL,
|
|
271
|
+
target_id TEXT NOT NULL,
|
|
272
|
+
target_title TEXT,
|
|
273
|
+
metadata TEXT,
|
|
274
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
-- Nudge Log
|
|
278
|
+
CREATE TABLE IF NOT EXISTS nudge_log (
|
|
279
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
280
|
+
rule_id TEXT NOT NULL,
|
|
281
|
+
title TEXT NOT NULL,
|
|
282
|
+
body TEXT,
|
|
283
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
-- Navigation Pages (for spec-site wireframes/screens)
|
|
287
|
+
CREATE TABLE IF NOT EXISTS nav_pages (
|
|
288
|
+
id TEXT PRIMARY KEY,
|
|
289
|
+
title TEXT NOT NULL,
|
|
290
|
+
icon TEXT,
|
|
291
|
+
sort_order INTEGER DEFAULT 0,
|
|
292
|
+
parent_id TEXT,
|
|
293
|
+
is_visible INTEGER DEFAULT 1,
|
|
294
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
-- Page Content (spec-site page sections)
|
|
298
|
+
CREATE TABLE IF NOT EXISTS page_content (
|
|
299
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
300
|
+
page_id TEXT NOT NULL,
|
|
301
|
+
sprint TEXT,
|
|
302
|
+
section_type TEXT DEFAULT 'content',
|
|
303
|
+
content TEXT,
|
|
304
|
+
sort_order INTEGER DEFAULT 0,
|
|
305
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
306
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
307
|
+
FOREIGN KEY (page_id) REFERENCES nav_pages(id)
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
-- Scenarios (spec-site scenario definitions)
|
|
311
|
+
CREATE TABLE IF NOT EXISTS scenarios (
|
|
312
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
313
|
+
page_id TEXT NOT NULL,
|
|
314
|
+
sprint TEXT,
|
|
315
|
+
name TEXT NOT NULL,
|
|
316
|
+
description TEXT,
|
|
317
|
+
is_default INTEGER DEFAULT 0,
|
|
318
|
+
sort_order INTEGER DEFAULT 0,
|
|
319
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
320
|
+
FOREIGN KEY (page_id) REFERENCES nav_pages(id)
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
-- Story Comments
|
|
324
|
+
CREATE TABLE IF NOT EXISTS story_comments (
|
|
325
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
326
|
+
story_id INTEGER NOT NULL,
|
|
327
|
+
content TEXT NOT NULL,
|
|
328
|
+
created_by TEXT NOT NULL,
|
|
329
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
330
|
+
FOREIGN KEY (story_id) REFERENCES pm_stories(id)
|
|
331
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Docs Module (Optional)
|
|
2
|
+
-- Enable via project.yaml: operations.pm_api.features.docs = true
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS docs (
|
|
5
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
|
+
title TEXT NOT NULL,
|
|
7
|
+
slug TEXT UNIQUE,
|
|
8
|
+
content TEXT,
|
|
9
|
+
doc_type TEXT DEFAULT 'general',
|
|
10
|
+
category TEXT,
|
|
11
|
+
author TEXT NOT NULL,
|
|
12
|
+
is_published INTEGER DEFAULT 0,
|
|
13
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
14
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS doc_versions (
|
|
18
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19
|
+
doc_id INTEGER NOT NULL,
|
|
20
|
+
content TEXT NOT NULL,
|
|
21
|
+
version INTEGER NOT NULL,
|
|
22
|
+
created_by TEXT NOT NULL,
|
|
23
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
24
|
+
FOREIGN KEY (doc_id) REFERENCES docs(id)
|
|
25
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- Meetings Module (Optional)
|
|
2
|
+
-- Enable via project.yaml: operations.pm_api.features.meetings = true
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS meetings (
|
|
5
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
|
+
sprint TEXT,
|
|
7
|
+
title TEXT NOT NULL,
|
|
8
|
+
meeting_type TEXT DEFAULT 'general',
|
|
9
|
+
meeting_date TEXT NOT NULL,
|
|
10
|
+
attendees TEXT,
|
|
11
|
+
agenda TEXT,
|
|
12
|
+
notes TEXT,
|
|
13
|
+
action_items TEXT,
|
|
14
|
+
created_by TEXT NOT NULL,
|
|
15
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
16
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
17
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- Rewards Module (Optional)
|
|
2
|
+
-- Enable via project.yaml: operations.pm_api.features.rewards = true
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS rewards (
|
|
5
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
|
+
member_name TEXT NOT NULL,
|
|
7
|
+
type TEXT NOT NULL CHECK (type IN ('reward', 'penalty')),
|
|
8
|
+
amount REAL NOT NULL,
|
|
9
|
+
reason TEXT NOT NULL,
|
|
10
|
+
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'paid', 'cancelled')),
|
|
11
|
+
issued_by TEXT NOT NULL,
|
|
12
|
+
tx_hash TEXT,
|
|
13
|
+
paid_at TEXT,
|
|
14
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
15
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
16
|
+
);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Context, Next } from 'hono'
|
|
2
|
+
import type { AppEnv } from './types.js'
|
|
3
|
+
import { query } from './db/adapter.js'
|
|
4
|
+
|
|
5
|
+
export async function authMiddleware(c: Context<AppEnv>, next: Next) {
|
|
6
|
+
const header = c.req.header('Authorization')
|
|
7
|
+
if (!header?.startsWith('Bearer ')) {
|
|
8
|
+
return c.json({ error: 'Missing or invalid Authorization header' }, 401)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const token = header.slice(7)
|
|
12
|
+
const result = await query<{ user_name: string }>(
|
|
13
|
+
`SELECT user_name FROM auth_tokens
|
|
14
|
+
WHERE token = ? AND is_active = 1
|
|
15
|
+
AND (expires_at IS NULL OR expires_at > datetime('now'))`,
|
|
16
|
+
[token],
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
if (result.error) {
|
|
20
|
+
return c.json({ error: 'Auth verification failed' }, 500)
|
|
21
|
+
}
|
|
22
|
+
if (result.rows.length === 0) {
|
|
23
|
+
return c.json({ error: 'Invalid or expired token' }, 401)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
c.set('userName', result.rows[0].user_name)
|
|
27
|
+
await next()
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blockchain adapter interface — provider-agnostic balance query layer.
|
|
3
|
+
* Implementations: TronAdapter, (future: EthAdapter, SolanaAdapter)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface BlockchainAdapter {
|
|
7
|
+
getBalance(address: string): Promise<{ native: number; token: number }>
|
|
8
|
+
getTokenName(): string
|
|
9
|
+
getNativeName(): string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let _adapter: BlockchainAdapter | null = null
|
|
13
|
+
|
|
14
|
+
export function setBlockchainAdapter(adapter: BlockchainAdapter) {
|
|
15
|
+
_adapter = adapter
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getBlockchainAdapter(): BlockchainAdapter | null {
|
|
19
|
+
return _adapter
|
|
20
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { BlockchainAdapter } from './adapter.js'
|
|
2
|
+
|
|
3
|
+
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
|
4
|
+
|
|
5
|
+
function base58ToHex(addr: string): string {
|
|
6
|
+
let num = 0n
|
|
7
|
+
for (const c of addr) {
|
|
8
|
+
const idx = ALPHABET.indexOf(c)
|
|
9
|
+
if (idx < 0) throw new Error(`Invalid base58 char: ${c}`)
|
|
10
|
+
num = num * 58n + BigInt(idx)
|
|
11
|
+
}
|
|
12
|
+
const hex = num.toString(16).padStart(50, '0')
|
|
13
|
+
return hex.slice(0, 42)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class TronAdapter implements BlockchainAdapter {
|
|
17
|
+
private contractAddress: string
|
|
18
|
+
private tokenName: string
|
|
19
|
+
private tokenDecimals: number
|
|
20
|
+
|
|
21
|
+
constructor(opts: { contractAddress: string; tokenName: string; tokenDecimals?: number }) {
|
|
22
|
+
this.contractAddress = opts.contractAddress
|
|
23
|
+
this.tokenName = opts.tokenName
|
|
24
|
+
this.tokenDecimals = opts.tokenDecimals ?? 8
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getTokenName(): string { return this.tokenName }
|
|
28
|
+
getNativeName(): string { return 'TRX' }
|
|
29
|
+
|
|
30
|
+
async getBalance(address: string): Promise<{ native: number; token: number }> {
|
|
31
|
+
try {
|
|
32
|
+
const addrHex = base58ToHex(address)
|
|
33
|
+
const contractHex = base58ToHex(this.contractAddress)
|
|
34
|
+
|
|
35
|
+
const [trxRes, balanceRes] = await Promise.all([
|
|
36
|
+
fetch(`https://api.trongrid.io/v1/accounts/${address}`),
|
|
37
|
+
fetch('https://api.trongrid.io/wallet/triggerconstantcontract', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { 'Content-Type': 'application/json' },
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
owner_address: addrHex,
|
|
42
|
+
contract_address: contractHex,
|
|
43
|
+
function_selector: 'balanceOf(address)',
|
|
44
|
+
parameter: addrHex.slice(2).padStart(64, '0'),
|
|
45
|
+
}),
|
|
46
|
+
}),
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
const trxData = await trxRes.json() as any
|
|
50
|
+
const balanceData = await balanceRes.json() as any
|
|
51
|
+
|
|
52
|
+
const native = (trxData.data?.[0]?.balance ?? 0) / 1_000_000
|
|
53
|
+
const token = balanceData.constant_result?.[0]
|
|
54
|
+
? parseInt(balanceData.constant_result[0], 16) / 10 ** this.tokenDecimals
|
|
55
|
+
: 0
|
|
56
|
+
|
|
57
|
+
return { native, token }
|
|
58
|
+
} catch {
|
|
59
|
+
return { native: 0, token: 0 }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|