feed-the-machine 1.6.1 → 1.7.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/LICENSE +21 -21
- package/README.md +170 -170
- package/bin/brain.py +1340 -0
- package/bin/convert_claude_skills_to_codex.py +490 -0
- package/bin/generate-manifest.mjs +463 -463
- package/bin/harden_codex_skills.py +141 -0
- package/bin/install.mjs +491 -491
- package/bin/migrate-eng-buddy-data.py +875 -0
- package/bin/playbook_engine/__init__.py +1 -0
- package/bin/playbook_engine/conftest.py +8 -0
- package/bin/playbook_engine/extractor.py +33 -0
- package/bin/playbook_engine/manager.py +102 -0
- package/bin/playbook_engine/models.py +84 -0
- package/bin/playbook_engine/registry.py +35 -0
- package/bin/playbook_engine/test_extractor.py +72 -0
- package/bin/playbook_engine/test_integration.py +129 -0
- package/bin/playbook_engine/test_manager.py +85 -0
- package/bin/playbook_engine/test_models.py +166 -0
- package/bin/playbook_engine/test_registry.py +67 -0
- package/bin/playbook_engine/test_tracer.py +86 -0
- package/bin/playbook_engine/tracer.py +93 -0
- package/bin/tasks_db.py +456 -0
- package/docs/HOOKS.md +243 -243
- package/docs/INBOX.md +233 -233
- package/ftm/SKILL.md +125 -122
- package/ftm-audit/SKILL.md +623 -623
- package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -91
- package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -66
- package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -135
- package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -69
- package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -96
- package/ftm-audit/scripts/run-knip.sh +23 -23
- package/ftm-audit.yml +2 -2
- package/ftm-brainstorm/SKILL.md +1003 -498
- package/ftm-brainstorm/evals/evals.json +180 -100
- package/ftm-brainstorm/evals/promptfoo.yaml +109 -109
- package/ftm-brainstorm/references/agent-prompts.md +552 -224
- package/ftm-brainstorm/references/plan-template.md +209 -121
- package/ftm-brainstorm.yml +2 -2
- package/ftm-browse/SKILL.md +454 -454
- package/ftm-browse/daemon/browser-manager.ts +206 -206
- package/ftm-browse/daemon/bun.lock +30 -30
- package/ftm-browse/daemon/cli.ts +347 -347
- package/ftm-browse/daemon/commands.ts +410 -410
- package/ftm-browse/daemon/main.ts +357 -357
- package/ftm-browse/daemon/package.json +17 -17
- package/ftm-browse/daemon/server.ts +189 -189
- package/ftm-browse/daemon/snapshot.ts +519 -519
- package/ftm-browse/daemon/tsconfig.json +22 -22
- package/ftm-browse.yml +4 -4
- package/ftm-capture/SKILL.md +370 -370
- package/ftm-capture.yml +4 -4
- package/ftm-codex-gate/SKILL.md +361 -361
- package/ftm-codex-gate.yml +2 -2
- package/ftm-config/SKILL.md +422 -345
- package/ftm-config.default.yml +125 -82
- package/ftm-config.yml +44 -2
- package/ftm-council/SKILL.md +416 -416
- package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -60
- package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -58
- package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -58
- package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -57
- package/ftm-council/references/protocols/PREREQUISITES.md +47 -47
- package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -46
- package/ftm-council.yml +2 -2
- package/ftm-dashboard/SKILL.md +163 -163
- package/ftm-dashboard.yml +4 -4
- package/ftm-debug/SKILL.md +1037 -1037
- package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -58
- package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -46
- package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -279
- package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -436
- package/ftm-debug/references/protocols/BLACKBOARD.md +86 -86
- package/ftm-debug/references/protocols/EDGE-CASES.md +103 -103
- package/ftm-debug.yml +2 -2
- package/ftm-diagram/SKILL.md +277 -277
- package/ftm-diagram.yml +2 -2
- package/ftm-executor/SKILL.md +777 -777
- package/ftm-executor/references/STYLE-TEMPLATE.md +73 -73
- package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -62
- package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -34
- package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -38
- package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +72 -72
- package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -66
- package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -73
- package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -36
- package/ftm-executor/references/protocols/MODEL-PROFILE.md +59 -59
- package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -66
- package/ftm-executor/runtime/ftm-runtime.mjs +252 -252
- package/ftm-executor/runtime/package.json +8 -8
- package/ftm-executor.yml +2 -2
- package/ftm-git/SKILL.md +441 -441
- package/ftm-git/evals/evals.json +26 -26
- package/ftm-git/evals/promptfoo.yaml +75 -75
- package/ftm-git/hooks/post-commit-experience.sh +92 -92
- package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -104
- package/ftm-git/references/protocols/REMEDIATION.md +139 -139
- package/ftm-git/scripts/pre-commit-secrets.sh +110 -110
- package/ftm-git.yml +2 -2
- package/ftm-inbox/backend/__pycache__/main.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/adapters/_retry.py +64 -64
- package/ftm-inbox/backend/adapters/base.py +230 -230
- package/ftm-inbox/backend/adapters/freshservice.py +104 -104
- package/ftm-inbox/backend/adapters/gmail.py +125 -125
- package/ftm-inbox/backend/adapters/jira.py +136 -136
- package/ftm-inbox/backend/adapters/registry.py +192 -192
- package/ftm-inbox/backend/adapters/slack.py +110 -110
- package/ftm-inbox/backend/db/connection.py +54 -54
- package/ftm-inbox/backend/db/schema.py +78 -78
- package/ftm-inbox/backend/executor/__init__.py +7 -7
- package/ftm-inbox/backend/executor/engine.py +149 -149
- package/ftm-inbox/backend/executor/step_runner.py +98 -98
- package/ftm-inbox/backend/main.py +103 -103
- package/ftm-inbox/backend/models/__init__.py +1 -1
- package/ftm-inbox/backend/models/unified_task.py +36 -36
- package/ftm-inbox/backend/planner/__init__.py +6 -6
- package/ftm-inbox/backend/planner/__pycache__/__init__.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/planner/__pycache__/generator.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/planner/__pycache__/schema.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/planner/generator.py +127 -127
- package/ftm-inbox/backend/planner/schema.py +34 -34
- package/ftm-inbox/backend/requirements.txt +5 -5
- package/ftm-inbox/backend/routes/__pycache__/plan.cpython-314.pyc +0 -0
- package/ftm-inbox/backend/routes/execute.py +186 -186
- package/ftm-inbox/backend/routes/health.py +52 -52
- package/ftm-inbox/backend/routes/inbox.py +68 -68
- package/ftm-inbox/backend/routes/plan.py +271 -271
- package/ftm-inbox/bin/launchagent.mjs +91 -91
- package/ftm-inbox/bin/setup.mjs +188 -188
- package/ftm-inbox/bin/start.sh +10 -10
- package/ftm-inbox/bin/status.sh +17 -17
- package/ftm-inbox/bin/stop.sh +8 -8
- package/ftm-inbox/config.example.yml +55 -55
- package/ftm-inbox/package-lock.json +2898 -2898
- package/ftm-inbox/package.json +26 -26
- package/ftm-inbox/postcss.config.js +6 -6
- package/ftm-inbox/src/app.css +199 -199
- package/ftm-inbox/src/app.html +18 -18
- package/ftm-inbox/src/lib/api.ts +166 -166
- package/ftm-inbox/src/lib/components/ExecutionLog.svelte +81 -81
- package/ftm-inbox/src/lib/components/InboxFeed.svelte +143 -143
- package/ftm-inbox/src/lib/components/PlanStep.svelte +271 -271
- package/ftm-inbox/src/lib/components/PlanView.svelte +206 -206
- package/ftm-inbox/src/lib/components/StreamPanel.svelte +99 -99
- package/ftm-inbox/src/lib/components/TaskCard.svelte +190 -190
- package/ftm-inbox/src/lib/components/ui/EmptyState.svelte +63 -63
- package/ftm-inbox/src/lib/components/ui/KawaiiCard.svelte +86 -86
- package/ftm-inbox/src/lib/components/ui/PillButton.svelte +106 -106
- package/ftm-inbox/src/lib/components/ui/StatusBadge.svelte +67 -67
- package/ftm-inbox/src/lib/components/ui/StreamDrawer.svelte +149 -149
- package/ftm-inbox/src/lib/components/ui/ThemeToggle.svelte +80 -80
- package/ftm-inbox/src/lib/theme.ts +47 -47
- package/ftm-inbox/src/routes/+layout.svelte +76 -76
- package/ftm-inbox/src/routes/+page.svelte +401 -401
- package/ftm-inbox/svelte.config.js +12 -12
- package/ftm-inbox/tailwind.config.ts +63 -63
- package/ftm-inbox/tsconfig.json +13 -13
- package/ftm-inbox/vite.config.ts +6 -6
- package/ftm-intent/SKILL.md +241 -241
- package/ftm-intent.yml +2 -2
- package/ftm-manifest.json +3794 -3794
- package/ftm-map/SKILL.md +291 -291
- package/ftm-map/scripts/db.py +712 -712
- package/ftm-map/scripts/index.py +415 -415
- package/ftm-map/scripts/parser.py +224 -224
- package/ftm-map/scripts/queries/go-tags.scm +20 -20
- package/ftm-map/scripts/queries/javascript-tags.scm +35 -35
- package/ftm-map/scripts/queries/python-tags.scm +31 -31
- package/ftm-map/scripts/queries/ruby-tags.scm +19 -19
- package/ftm-map/scripts/queries/rust-tags.scm +37 -37
- package/ftm-map/scripts/queries/typescript-tags.scm +41 -41
- package/ftm-map/scripts/query.py +301 -301
- package/ftm-map/scripts/ranker.py +377 -377
- package/ftm-map/scripts/requirements.txt +5 -5
- package/ftm-map/scripts/setup-hooks.sh +27 -27
- package/ftm-map/scripts/setup.sh +56 -56
- package/ftm-map/scripts/test_db.py +364 -364
- package/ftm-map/scripts/test_parser.py +174 -174
- package/ftm-map/scripts/test_query.py +183 -183
- package/ftm-map/scripts/test_ranker.py +199 -199
- package/ftm-map/scripts/views.py +591 -591
- package/ftm-map.yml +2 -2
- package/ftm-mind/SKILL.md +201 -1943
- package/ftm-mind/evals/promptfoo.yaml +142 -142
- package/ftm-mind/references/blackboard-protocol.md +110 -0
- package/ftm-mind/references/blackboard-schema.md +328 -328
- package/ftm-mind/references/complexity-guide.md +110 -110
- package/ftm-mind/references/complexity-sizing.md +138 -0
- package/ftm-mind/references/decide-act-protocol.md +172 -0
- package/ftm-mind/references/direct-execution.md +51 -0
- package/ftm-mind/references/environment-discovery.md +77 -0
- package/ftm-mind/references/event-registry.md +319 -319
- package/ftm-mind/references/mcp-inventory.md +300 -296
- package/ftm-mind/references/ops-routing.md +47 -0
- package/ftm-mind/references/orient-protocol.md +234 -0
- package/ftm-mind/references/personality.md +40 -0
- package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -72
- package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -32
- package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -80
- package/ftm-mind/references/reflexion-protocol.md +249 -249
- package/ftm-mind/references/routing/SCENARIOS.md +22 -22
- package/ftm-mind/references/routing-scenarios.md +35 -35
- package/ftm-mind.yml +2 -2
- package/ftm-ops.yml +4 -0
- package/ftm-pause/SKILL.md +395 -395
- package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -186
- package/ftm-pause/references/protocols/VALIDATION.md +80 -80
- package/ftm-pause.yml +2 -2
- package/ftm-researcher/SKILL.md +275 -275
- package/ftm-researcher/evals/agent-diversity.yaml +17 -17
- package/ftm-researcher/evals/synthesis-quality.yaml +12 -12
- package/ftm-researcher/evals/trigger-accuracy.yaml +39 -39
- package/ftm-researcher/references/adaptive-search.md +116 -116
- package/ftm-researcher/references/agent-prompts.md +193 -193
- package/ftm-researcher/references/council-integration.md +193 -193
- package/ftm-researcher/references/output-format.md +203 -203
- package/ftm-researcher/references/synthesis-pipeline.md +165 -165
- package/ftm-researcher/scripts/score_credibility.py +234 -234
- package/ftm-researcher/scripts/validate_research.py +92 -92
- package/ftm-researcher.yml +2 -2
- package/ftm-resume/SKILL.md +518 -518
- package/ftm-resume/references/protocols/VALIDATION.md +172 -172
- package/ftm-resume.yml +2 -2
- package/ftm-retro/SKILL.md +380 -380
- package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -89
- package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -109
- package/ftm-retro.yml +2 -2
- package/ftm-routine/SKILL.md +170 -170
- package/ftm-routine.yml +4 -4
- package/ftm-state/blackboard/capabilities.json +5 -5
- package/ftm-state/blackboard/capabilities.schema.json +27 -27
- package/ftm-state/blackboard/context.json +37 -23
- package/ftm-state/blackboard/experiences/doom-statusline-fix.json +26 -0
- package/ftm-state/blackboard/experiences/hackathon-pages-site.json +26 -0
- package/ftm-state/blackboard/experiences/hindsight-sso-kickoff.json +42 -0
- package/ftm-state/blackboard/experiences/index.json +58 -9
- package/ftm-state/blackboard/experiences/learning-ragnarok-api-access.json +23 -0
- package/ftm-state/blackboard/experiences/nordlayer-members-auto-assign.json +26 -0
- package/ftm-state/blackboard/experiences/saml2aws-stale-session-fix.json +41 -0
- package/ftm-state/blackboard/patterns.json +6 -6
- package/ftm-state/schemas/context.schema.json +130 -130
- package/ftm-state/schemas/experience-index.schema.json +77 -77
- package/ftm-state/schemas/experience.schema.json +78 -78
- package/ftm-state/schemas/patterns.schema.json +44 -44
- package/ftm-upgrade/SKILL.md +194 -194
- package/ftm-upgrade/scripts/check-version.sh +76 -76
- package/ftm-upgrade/scripts/upgrade.sh +143 -143
- package/ftm-upgrade.yml +2 -2
- package/ftm-verify.yml +2 -2
- package/ftm.yml +2 -2
- package/hooks/ftm-auto-log.sh +137 -0
- package/hooks/ftm-blackboard-enforcer.sh +93 -93
- package/hooks/ftm-discovery-reminder.sh +90 -90
- package/hooks/ftm-drafts-gate.sh +61 -61
- package/hooks/ftm-event-logger.mjs +107 -107
- package/hooks/ftm-install-hooks.sh +240 -0
- package/hooks/ftm-learning-capture.sh +117 -0
- package/hooks/ftm-map-autodetect.sh +79 -79
- package/hooks/ftm-pending-sync-check.sh +22 -22
- package/hooks/ftm-plan-gate.sh +92 -92
- package/hooks/ftm-post-commit-trigger.sh +57 -57
- package/hooks/ftm-post-compaction.sh +138 -0
- package/hooks/ftm-pre-compaction.sh +147 -0
- package/hooks/ftm-session-end.sh +52 -0
- package/hooks/ftm-session-snapshot.sh +213 -0
- package/hooks/settings-template.json +81 -81
- package/install.sh +363 -363
- package/package.json +84 -84
- package/uninstall.sh +25 -25
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Generates ~/Library/LaunchAgents/com.ftm.inbox.plist
|
|
5
|
-
* Runs ftm-inbox on login (macOS only).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
9
|
-
import { join } from "path";
|
|
10
|
-
import { homedir, platform } from "os";
|
|
11
|
-
import { execSync } from "child_process";
|
|
12
|
-
import { fileURLToPath } from "url";
|
|
13
|
-
import { dirname } from "path";
|
|
14
|
-
|
|
15
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
-
const __dirname = dirname(__filename);
|
|
17
|
-
|
|
18
|
-
const HOME = homedir();
|
|
19
|
-
const LAUNCH_AGENTS_DIR = join(HOME, "Library", "LaunchAgents");
|
|
20
|
-
const PLIST_PATH = join(LAUNCH_AGENTS_DIR, "com.ftm.inbox.plist");
|
|
21
|
-
const INBOX_CONFIG_DIR = join(HOME, ".claude", "ftm-inbox");
|
|
22
|
-
const START_SCRIPT = join(__dirname, "start.sh");
|
|
23
|
-
|
|
24
|
-
function ensureDir(dir) {
|
|
25
|
-
if (!existsSync(dir)) {
|
|
26
|
-
mkdirSync(dir, { recursive: true });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function buildPlist(startScript, logDir) {
|
|
31
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
32
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
33
|
-
<plist version="1.0">
|
|
34
|
-
<dict>
|
|
35
|
-
<key>Label</key>
|
|
36
|
-
<string>com.ftm.inbox</string>
|
|
37
|
-
|
|
38
|
-
<key>ProgramArguments</key>
|
|
39
|
-
<array>
|
|
40
|
-
<string>/bin/bash</string>
|
|
41
|
-
<string>${startScript}</string>
|
|
42
|
-
</array>
|
|
43
|
-
|
|
44
|
-
<key>RunAtLoad</key>
|
|
45
|
-
<true/>
|
|
46
|
-
|
|
47
|
-
<key>KeepAlive</key>
|
|
48
|
-
<false/>
|
|
49
|
-
|
|
50
|
-
<key>StandardOutPath</key>
|
|
51
|
-
<string>${join(logDir, "launchd-stdout.log")}</string>
|
|
52
|
-
|
|
53
|
-
<key>StandardErrorPath</key>
|
|
54
|
-
<string>${join(logDir, "launchd-stderr.log")}</string>
|
|
55
|
-
|
|
56
|
-
<key>WorkingDirectory</key>
|
|
57
|
-
<string>${dirname(dirname(startScript))}</string>
|
|
58
|
-
</dict>
|
|
59
|
-
</plist>
|
|
60
|
-
`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function main() {
|
|
64
|
-
if (platform() !== "darwin") {
|
|
65
|
-
console.error("LaunchAgent setup is macOS-only.");
|
|
66
|
-
process.exit(1);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const logDir = join(INBOX_CONFIG_DIR, "logs");
|
|
70
|
-
|
|
71
|
-
ensureDir(LAUNCH_AGENTS_DIR);
|
|
72
|
-
ensureDir(logDir);
|
|
73
|
-
|
|
74
|
-
const plistContent = buildPlist(START_SCRIPT, logDir);
|
|
75
|
-
writeFileSync(PLIST_PATH, plistContent, "utf8");
|
|
76
|
-
|
|
77
|
-
console.log("LaunchAgent written to:", PLIST_PATH);
|
|
78
|
-
console.log("Logs will be written to:", logDir);
|
|
79
|
-
|
|
80
|
-
// Load it immediately
|
|
81
|
-
try {
|
|
82
|
-
execSync(`launchctl load "${PLIST_PATH}"`, { stdio: "inherit" });
|
|
83
|
-
console.log("LaunchAgent loaded. ftm-inbox will start on next login.");
|
|
84
|
-
console.log("To start it now: launchctl start com.ftm.inbox");
|
|
85
|
-
} catch (err) {
|
|
86
|
-
console.warn("Could not load LaunchAgent automatically:", err.message);
|
|
87
|
-
console.warn("Run manually: launchctl load", PLIST_PATH);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates ~/Library/LaunchAgents/com.ftm.inbox.plist
|
|
5
|
+
* Runs ftm-inbox on login (macOS only).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { homedir, platform } from "os";
|
|
11
|
+
import { execSync } from "child_process";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
import { dirname } from "path";
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
|
|
18
|
+
const HOME = homedir();
|
|
19
|
+
const LAUNCH_AGENTS_DIR = join(HOME, "Library", "LaunchAgents");
|
|
20
|
+
const PLIST_PATH = join(LAUNCH_AGENTS_DIR, "com.ftm.inbox.plist");
|
|
21
|
+
const INBOX_CONFIG_DIR = join(HOME, ".claude", "ftm-inbox");
|
|
22
|
+
const START_SCRIPT = join(__dirname, "start.sh");
|
|
23
|
+
|
|
24
|
+
function ensureDir(dir) {
|
|
25
|
+
if (!existsSync(dir)) {
|
|
26
|
+
mkdirSync(dir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildPlist(startScript, logDir) {
|
|
31
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
32
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
33
|
+
<plist version="1.0">
|
|
34
|
+
<dict>
|
|
35
|
+
<key>Label</key>
|
|
36
|
+
<string>com.ftm.inbox</string>
|
|
37
|
+
|
|
38
|
+
<key>ProgramArguments</key>
|
|
39
|
+
<array>
|
|
40
|
+
<string>/bin/bash</string>
|
|
41
|
+
<string>${startScript}</string>
|
|
42
|
+
</array>
|
|
43
|
+
|
|
44
|
+
<key>RunAtLoad</key>
|
|
45
|
+
<true/>
|
|
46
|
+
|
|
47
|
+
<key>KeepAlive</key>
|
|
48
|
+
<false/>
|
|
49
|
+
|
|
50
|
+
<key>StandardOutPath</key>
|
|
51
|
+
<string>${join(logDir, "launchd-stdout.log")}</string>
|
|
52
|
+
|
|
53
|
+
<key>StandardErrorPath</key>
|
|
54
|
+
<string>${join(logDir, "launchd-stderr.log")}</string>
|
|
55
|
+
|
|
56
|
+
<key>WorkingDirectory</key>
|
|
57
|
+
<string>${dirname(dirname(startScript))}</string>
|
|
58
|
+
</dict>
|
|
59
|
+
</plist>
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function main() {
|
|
64
|
+
if (platform() !== "darwin") {
|
|
65
|
+
console.error("LaunchAgent setup is macOS-only.");
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const logDir = join(INBOX_CONFIG_DIR, "logs");
|
|
70
|
+
|
|
71
|
+
ensureDir(LAUNCH_AGENTS_DIR);
|
|
72
|
+
ensureDir(logDir);
|
|
73
|
+
|
|
74
|
+
const plistContent = buildPlist(START_SCRIPT, logDir);
|
|
75
|
+
writeFileSync(PLIST_PATH, plistContent, "utf8");
|
|
76
|
+
|
|
77
|
+
console.log("LaunchAgent written to:", PLIST_PATH);
|
|
78
|
+
console.log("Logs will be written to:", logDir);
|
|
79
|
+
|
|
80
|
+
// Load it immediately
|
|
81
|
+
try {
|
|
82
|
+
execSync(`launchctl load "${PLIST_PATH}"`, { stdio: "inherit" });
|
|
83
|
+
console.log("LaunchAgent loaded. ftm-inbox will start on next login.");
|
|
84
|
+
console.log("To start it now: launchctl start com.ftm.inbox");
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.warn("Could not load LaunchAgent automatically:", err.message);
|
|
87
|
+
console.warn("Run manually: launchctl load", PLIST_PATH);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
main();
|
package/ftm-inbox/bin/setup.mjs
CHANGED
|
@@ -1,188 +1,188 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ftm-inbox setup wizard
|
|
5
|
-
* Prompts for credentials and writes ~/.claude/ftm-inbox/config.yml
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { createInterface } from "readline";
|
|
9
|
-
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
10
|
-
import { join } from "path";
|
|
11
|
-
import { homedir } from "os";
|
|
12
|
-
|
|
13
|
-
const HOME = homedir();
|
|
14
|
-
const INBOX_CONFIG_DIR = join(HOME, ".claude", "ftm-inbox");
|
|
15
|
-
const CONFIG_FILE = join(INBOX_CONFIG_DIR, "config.yml");
|
|
16
|
-
|
|
17
|
-
const rl = createInterface({
|
|
18
|
-
input: process.stdin,
|
|
19
|
-
output: process.stdout,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
function ask(question, defaultValue = "") {
|
|
23
|
-
return new Promise((resolve) => {
|
|
24
|
-
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
25
|
-
rl.question(prompt, (answer) => {
|
|
26
|
-
resolve(answer.trim() || defaultValue);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function askSecret(question) {
|
|
32
|
-
return new Promise((resolve) => {
|
|
33
|
-
process.stdout.write(`${question}: `);
|
|
34
|
-
// Note: readline doesn't natively hide input; use as-is for terminal environments
|
|
35
|
-
rl.question("", (answer) => {
|
|
36
|
-
resolve(answer.trim());
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function ensureDir(dir) {
|
|
42
|
-
if (!existsSync(dir)) {
|
|
43
|
-
mkdirSync(dir, { recursive: true });
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function buildConfig(answers) {
|
|
48
|
-
const lines = ["# ftm-inbox configuration", "# Generated by ftm-inbox setup wizard", ""];
|
|
49
|
-
|
|
50
|
-
lines.push("server:");
|
|
51
|
-
lines.push(` port: ${answers.port}`);
|
|
52
|
-
lines.push("");
|
|
53
|
-
|
|
54
|
-
if (answers.jiraBaseUrl || answers.jiraEmail || answers.jiraApiToken) {
|
|
55
|
-
lines.push("adapters:");
|
|
56
|
-
lines.push(" jira:");
|
|
57
|
-
lines.push(` enabled: ${!!(answers.jiraBaseUrl && answers.jiraEmail && answers.jiraApiToken)}`);
|
|
58
|
-
lines.push(` base_url: "${answers.jiraBaseUrl}"`);
|
|
59
|
-
lines.push(` email: "${answers.jiraEmail}"`);
|
|
60
|
-
lines.push(` api_token: "${answers.jiraApiToken}"`);
|
|
61
|
-
lines.push(` poll_interval_seconds: 60`);
|
|
62
|
-
lines.push("");
|
|
63
|
-
} else {
|
|
64
|
-
lines.push("adapters:");
|
|
65
|
-
lines.push(" jira:");
|
|
66
|
-
lines.push(" enabled: false");
|
|
67
|
-
lines.push(` base_url: ""`);
|
|
68
|
-
lines.push(` email: ""`);
|
|
69
|
-
lines.push(` api_token: ""`);
|
|
70
|
-
lines.push(` poll_interval_seconds: 60`);
|
|
71
|
-
lines.push("");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
lines.push(" freshservice:");
|
|
75
|
-
lines.push(` enabled: ${!!(answers.freshserviceDomain && answers.freshserviceApiKey)}`);
|
|
76
|
-
lines.push(` domain: "${answers.freshserviceDomain}"`);
|
|
77
|
-
lines.push(` api_key: "${answers.freshserviceApiKey}"`);
|
|
78
|
-
lines.push(` poll_interval_seconds: 120`);
|
|
79
|
-
lines.push("");
|
|
80
|
-
|
|
81
|
-
lines.push(" slack:");
|
|
82
|
-
lines.push(` enabled: ${!!answers.slackBotToken}`);
|
|
83
|
-
lines.push(` bot_token: "${answers.slackBotToken}"`);
|
|
84
|
-
lines.push(` poll_interval_seconds: 30`);
|
|
85
|
-
lines.push("");
|
|
86
|
-
|
|
87
|
-
lines.push(" gmail:");
|
|
88
|
-
lines.push(` enabled: ${!!answers.gmailCredentialsPath}`);
|
|
89
|
-
lines.push(` credentials_path: "${answers.gmailCredentialsPath}"`);
|
|
90
|
-
lines.push(` poll_interval_seconds: 120`);
|
|
91
|
-
lines.push("");
|
|
92
|
-
|
|
93
|
-
lines.push("database:");
|
|
94
|
-
lines.push(` path: "${join(INBOX_CONFIG_DIR, "inbox.db")}"`);
|
|
95
|
-
lines.push("");
|
|
96
|
-
|
|
97
|
-
lines.push("logging:");
|
|
98
|
-
lines.push(` level: "INFO"`);
|
|
99
|
-
lines.push(` path: "${join(INBOX_CONFIG_DIR, "logs")}"`);
|
|
100
|
-
|
|
101
|
-
return lines.join("\n");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function main() {
|
|
105
|
-
console.log("");
|
|
106
|
-
console.log("ftm-inbox Setup Wizard");
|
|
107
|
-
console.log("======================");
|
|
108
|
-
console.log("This will configure your inbox service credentials.");
|
|
109
|
-
console.log("Credentials are stored in ~/.claude/ftm-inbox/config.yml (gitignored).");
|
|
110
|
-
console.log("");
|
|
111
|
-
|
|
112
|
-
const answers = {};
|
|
113
|
-
|
|
114
|
-
// Port
|
|
115
|
-
answers.port = await ask("Port number", "8042");
|
|
116
|
-
|
|
117
|
-
// Jira
|
|
118
|
-
console.log("");
|
|
119
|
-
console.log("--- Jira (leave blank to skip) ---");
|
|
120
|
-
answers.jiraBaseUrl = await ask("Jira base URL (e.g. https://yourorg.atlassian.net)");
|
|
121
|
-
if (answers.jiraBaseUrl) {
|
|
122
|
-
answers.jiraEmail = await ask("Jira email");
|
|
123
|
-
answers.jiraApiToken = await askSecret("Jira API token");
|
|
124
|
-
} else {
|
|
125
|
-
answers.jiraEmail = "";
|
|
126
|
-
answers.jiraApiToken = "";
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Freshservice
|
|
130
|
-
console.log("");
|
|
131
|
-
console.log("--- Freshservice (leave blank to skip) ---");
|
|
132
|
-
answers.freshserviceDomain = await ask("Freshservice domain (e.g. yourorg.freshservice.com)");
|
|
133
|
-
if (answers.freshserviceDomain) {
|
|
134
|
-
answers.freshserviceApiKey = await askSecret("Freshservice API key");
|
|
135
|
-
} else {
|
|
136
|
-
answers.freshserviceApiKey = "";
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Slack
|
|
140
|
-
console.log("");
|
|
141
|
-
console.log("--- Slack (leave blank to skip) ---");
|
|
142
|
-
answers.slackBotToken = await askSecret("Slack bot token (xoxb-...)");
|
|
143
|
-
|
|
144
|
-
// Gmail
|
|
145
|
-
console.log("");
|
|
146
|
-
console.log("--- Gmail (leave blank to skip) ---");
|
|
147
|
-
answers.gmailCredentialsPath = await ask(
|
|
148
|
-
"Gmail credentials JSON path (e.g. ~/credentials.json)"
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
rl.close();
|
|
152
|
-
|
|
153
|
-
// Validate at least one adapter
|
|
154
|
-
const hasJira = !!(answers.jiraBaseUrl && answers.jiraEmail && answers.jiraApiToken);
|
|
155
|
-
const hasFreshservice = !!(answers.freshserviceDomain && answers.freshserviceApiKey);
|
|
156
|
-
const hasSlack = !!answers.slackBotToken;
|
|
157
|
-
const hasGmail = !!answers.gmailCredentialsPath;
|
|
158
|
-
|
|
159
|
-
if (!hasJira && !hasFreshservice && !hasSlack && !hasGmail) {
|
|
160
|
-
console.log("");
|
|
161
|
-
console.error("ERROR: At least one adapter must have credentials configured.");
|
|
162
|
-
console.error("Re-run setup and provide credentials for at least one service.");
|
|
163
|
-
process.exit(1);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Write config
|
|
167
|
-
ensureDir(INBOX_CONFIG_DIR);
|
|
168
|
-
ensureDir(join(INBOX_CONFIG_DIR, "logs"));
|
|
169
|
-
|
|
170
|
-
const configContent = buildConfig(answers);
|
|
171
|
-
writeFileSync(CONFIG_FILE, configContent, "utf8");
|
|
172
|
-
|
|
173
|
-
console.log("");
|
|
174
|
-
console.log("Configuration written to:", CONFIG_FILE);
|
|
175
|
-
console.log("");
|
|
176
|
-
console.log("Enabled adapters:");
|
|
177
|
-
if (hasJira) console.log(" - Jira");
|
|
178
|
-
if (hasFreshservice) console.log(" - Freshservice");
|
|
179
|
-
if (hasSlack) console.log(" - Slack");
|
|
180
|
-
if (hasGmail) console.log(" - Gmail");
|
|
181
|
-
console.log("");
|
|
182
|
-
console.log("Start the service with: ftm-inbox/bin/start.sh");
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
main().catch((err) => {
|
|
186
|
-
console.error("Setup failed:", err.message);
|
|
187
|
-
process.exit(1);
|
|
188
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ftm-inbox setup wizard
|
|
5
|
+
* Prompts for credentials and writes ~/.claude/ftm-inbox/config.yml
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createInterface } from "readline";
|
|
9
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import { homedir } from "os";
|
|
12
|
+
|
|
13
|
+
const HOME = homedir();
|
|
14
|
+
const INBOX_CONFIG_DIR = join(HOME, ".claude", "ftm-inbox");
|
|
15
|
+
const CONFIG_FILE = join(INBOX_CONFIG_DIR, "config.yml");
|
|
16
|
+
|
|
17
|
+
const rl = createInterface({
|
|
18
|
+
input: process.stdin,
|
|
19
|
+
output: process.stdout,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function ask(question, defaultValue = "") {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
25
|
+
rl.question(prompt, (answer) => {
|
|
26
|
+
resolve(answer.trim() || defaultValue);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function askSecret(question) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
process.stdout.write(`${question}: `);
|
|
34
|
+
// Note: readline doesn't natively hide input; use as-is for terminal environments
|
|
35
|
+
rl.question("", (answer) => {
|
|
36
|
+
resolve(answer.trim());
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ensureDir(dir) {
|
|
42
|
+
if (!existsSync(dir)) {
|
|
43
|
+
mkdirSync(dir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function buildConfig(answers) {
|
|
48
|
+
const lines = ["# ftm-inbox configuration", "# Generated by ftm-inbox setup wizard", ""];
|
|
49
|
+
|
|
50
|
+
lines.push("server:");
|
|
51
|
+
lines.push(` port: ${answers.port}`);
|
|
52
|
+
lines.push("");
|
|
53
|
+
|
|
54
|
+
if (answers.jiraBaseUrl || answers.jiraEmail || answers.jiraApiToken) {
|
|
55
|
+
lines.push("adapters:");
|
|
56
|
+
lines.push(" jira:");
|
|
57
|
+
lines.push(` enabled: ${!!(answers.jiraBaseUrl && answers.jiraEmail && answers.jiraApiToken)}`);
|
|
58
|
+
lines.push(` base_url: "${answers.jiraBaseUrl}"`);
|
|
59
|
+
lines.push(` email: "${answers.jiraEmail}"`);
|
|
60
|
+
lines.push(` api_token: "${answers.jiraApiToken}"`);
|
|
61
|
+
lines.push(` poll_interval_seconds: 60`);
|
|
62
|
+
lines.push("");
|
|
63
|
+
} else {
|
|
64
|
+
lines.push("adapters:");
|
|
65
|
+
lines.push(" jira:");
|
|
66
|
+
lines.push(" enabled: false");
|
|
67
|
+
lines.push(` base_url: ""`);
|
|
68
|
+
lines.push(` email: ""`);
|
|
69
|
+
lines.push(` api_token: ""`);
|
|
70
|
+
lines.push(` poll_interval_seconds: 60`);
|
|
71
|
+
lines.push("");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
lines.push(" freshservice:");
|
|
75
|
+
lines.push(` enabled: ${!!(answers.freshserviceDomain && answers.freshserviceApiKey)}`);
|
|
76
|
+
lines.push(` domain: "${answers.freshserviceDomain}"`);
|
|
77
|
+
lines.push(` api_key: "${answers.freshserviceApiKey}"`);
|
|
78
|
+
lines.push(` poll_interval_seconds: 120`);
|
|
79
|
+
lines.push("");
|
|
80
|
+
|
|
81
|
+
lines.push(" slack:");
|
|
82
|
+
lines.push(` enabled: ${!!answers.slackBotToken}`);
|
|
83
|
+
lines.push(` bot_token: "${answers.slackBotToken}"`);
|
|
84
|
+
lines.push(` poll_interval_seconds: 30`);
|
|
85
|
+
lines.push("");
|
|
86
|
+
|
|
87
|
+
lines.push(" gmail:");
|
|
88
|
+
lines.push(` enabled: ${!!answers.gmailCredentialsPath}`);
|
|
89
|
+
lines.push(` credentials_path: "${answers.gmailCredentialsPath}"`);
|
|
90
|
+
lines.push(` poll_interval_seconds: 120`);
|
|
91
|
+
lines.push("");
|
|
92
|
+
|
|
93
|
+
lines.push("database:");
|
|
94
|
+
lines.push(` path: "${join(INBOX_CONFIG_DIR, "inbox.db")}"`);
|
|
95
|
+
lines.push("");
|
|
96
|
+
|
|
97
|
+
lines.push("logging:");
|
|
98
|
+
lines.push(` level: "INFO"`);
|
|
99
|
+
lines.push(` path: "${join(INBOX_CONFIG_DIR, "logs")}"`);
|
|
100
|
+
|
|
101
|
+
return lines.join("\n");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function main() {
|
|
105
|
+
console.log("");
|
|
106
|
+
console.log("ftm-inbox Setup Wizard");
|
|
107
|
+
console.log("======================");
|
|
108
|
+
console.log("This will configure your inbox service credentials.");
|
|
109
|
+
console.log("Credentials are stored in ~/.claude/ftm-inbox/config.yml (gitignored).");
|
|
110
|
+
console.log("");
|
|
111
|
+
|
|
112
|
+
const answers = {};
|
|
113
|
+
|
|
114
|
+
// Port
|
|
115
|
+
answers.port = await ask("Port number", "8042");
|
|
116
|
+
|
|
117
|
+
// Jira
|
|
118
|
+
console.log("");
|
|
119
|
+
console.log("--- Jira (leave blank to skip) ---");
|
|
120
|
+
answers.jiraBaseUrl = await ask("Jira base URL (e.g. https://yourorg.atlassian.net)");
|
|
121
|
+
if (answers.jiraBaseUrl) {
|
|
122
|
+
answers.jiraEmail = await ask("Jira email");
|
|
123
|
+
answers.jiraApiToken = await askSecret("Jira API token");
|
|
124
|
+
} else {
|
|
125
|
+
answers.jiraEmail = "";
|
|
126
|
+
answers.jiraApiToken = "";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Freshservice
|
|
130
|
+
console.log("");
|
|
131
|
+
console.log("--- Freshservice (leave blank to skip) ---");
|
|
132
|
+
answers.freshserviceDomain = await ask("Freshservice domain (e.g. yourorg.freshservice.com)");
|
|
133
|
+
if (answers.freshserviceDomain) {
|
|
134
|
+
answers.freshserviceApiKey = await askSecret("Freshservice API key");
|
|
135
|
+
} else {
|
|
136
|
+
answers.freshserviceApiKey = "";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Slack
|
|
140
|
+
console.log("");
|
|
141
|
+
console.log("--- Slack (leave blank to skip) ---");
|
|
142
|
+
answers.slackBotToken = await askSecret("Slack bot token (xoxb-...)");
|
|
143
|
+
|
|
144
|
+
// Gmail
|
|
145
|
+
console.log("");
|
|
146
|
+
console.log("--- Gmail (leave blank to skip) ---");
|
|
147
|
+
answers.gmailCredentialsPath = await ask(
|
|
148
|
+
"Gmail credentials JSON path (e.g. ~/credentials.json)"
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
rl.close();
|
|
152
|
+
|
|
153
|
+
// Validate at least one adapter
|
|
154
|
+
const hasJira = !!(answers.jiraBaseUrl && answers.jiraEmail && answers.jiraApiToken);
|
|
155
|
+
const hasFreshservice = !!(answers.freshserviceDomain && answers.freshserviceApiKey);
|
|
156
|
+
const hasSlack = !!answers.slackBotToken;
|
|
157
|
+
const hasGmail = !!answers.gmailCredentialsPath;
|
|
158
|
+
|
|
159
|
+
if (!hasJira && !hasFreshservice && !hasSlack && !hasGmail) {
|
|
160
|
+
console.log("");
|
|
161
|
+
console.error("ERROR: At least one adapter must have credentials configured.");
|
|
162
|
+
console.error("Re-run setup and provide credentials for at least one service.");
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Write config
|
|
167
|
+
ensureDir(INBOX_CONFIG_DIR);
|
|
168
|
+
ensureDir(join(INBOX_CONFIG_DIR, "logs"));
|
|
169
|
+
|
|
170
|
+
const configContent = buildConfig(answers);
|
|
171
|
+
writeFileSync(CONFIG_FILE, configContent, "utf8");
|
|
172
|
+
|
|
173
|
+
console.log("");
|
|
174
|
+
console.log("Configuration written to:", CONFIG_FILE);
|
|
175
|
+
console.log("");
|
|
176
|
+
console.log("Enabled adapters:");
|
|
177
|
+
if (hasJira) console.log(" - Jira");
|
|
178
|
+
if (hasFreshservice) console.log(" - Freshservice");
|
|
179
|
+
if (hasSlack) console.log(" - Slack");
|
|
180
|
+
if (hasGmail) console.log(" - Gmail");
|
|
181
|
+
console.log("");
|
|
182
|
+
console.log("Start the service with: ftm-inbox/bin/start.sh");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
main().catch((err) => {
|
|
186
|
+
console.error("Setup failed:", err.message);
|
|
187
|
+
process.exit(1);
|
|
188
|
+
});
|
package/ftm-inbox/bin/start.sh
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Start ftm-inbox backend + pollers
|
|
3
|
-
cd "$(dirname "$0")/.." || exit
|
|
4
|
-
PORT=${FTM_INBOX_PORT:-8042}
|
|
5
|
-
echo "Starting ftm-inbox on port $PORT..."
|
|
6
|
-
python3 -m uvicorn backend.main:app --host 0.0.0.0 --port $PORT &
|
|
7
|
-
BACKEND_PID=$!
|
|
8
|
-
echo "Backend PID: $BACKEND_PID"
|
|
9
|
-
echo $BACKEND_PID > /tmp/ftm-inbox.pid
|
|
10
|
-
echo "ftm-inbox running. Stop with: ftm-inbox/bin/stop.sh"
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Start ftm-inbox backend + pollers
|
|
3
|
+
cd "$(dirname "$0")/.." || exit
|
|
4
|
+
PORT=${FTM_INBOX_PORT:-8042}
|
|
5
|
+
echo "Starting ftm-inbox on port $PORT..."
|
|
6
|
+
python3 -m uvicorn backend.main:app --host 0.0.0.0 --port $PORT &
|
|
7
|
+
BACKEND_PID=$!
|
|
8
|
+
echo "Backend PID: $BACKEND_PID"
|
|
9
|
+
echo $BACKEND_PID > /tmp/ftm-inbox.pid
|
|
10
|
+
echo "ftm-inbox running. Stop with: ftm-inbox/bin/stop.sh"
|
package/ftm-inbox/bin/status.sh
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
if [ -f /tmp/ftm-inbox.pid ] && kill -0 "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null; then
|
|
3
|
-
echo "ftm-inbox is running (PID: $(cat /tmp/ftm-inbox.pid))"
|
|
4
|
-
# Show last poll times from DB if available
|
|
5
|
-
CONFIG_DIR="$HOME/.claude/ftm-inbox"
|
|
6
|
-
DB_PATH="$CONFIG_DIR/inbox.db"
|
|
7
|
-
if [ -f "$DB_PATH" ] && command -v sqlite3 &>/dev/null; then
|
|
8
|
-
echo ""
|
|
9
|
-
echo "Last poll times:"
|
|
10
|
-
sqlite3 "$DB_PATH" "SELECT adapter, MAX(fetched_at) FROM inbox_items GROUP BY adapter;" 2>/dev/null | \
|
|
11
|
-
while IFS='|' read -r adapter ts; do
|
|
12
|
-
echo " $adapter: $ts"
|
|
13
|
-
done
|
|
14
|
-
fi
|
|
15
|
-
else
|
|
16
|
-
echo "ftm-inbox is not running."
|
|
17
|
-
fi
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
if [ -f /tmp/ftm-inbox.pid ] && kill -0 "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null; then
|
|
3
|
+
echo "ftm-inbox is running (PID: $(cat /tmp/ftm-inbox.pid))"
|
|
4
|
+
# Show last poll times from DB if available
|
|
5
|
+
CONFIG_DIR="$HOME/.claude/ftm-inbox"
|
|
6
|
+
DB_PATH="$CONFIG_DIR/inbox.db"
|
|
7
|
+
if [ -f "$DB_PATH" ] && command -v sqlite3 &>/dev/null; then
|
|
8
|
+
echo ""
|
|
9
|
+
echo "Last poll times:"
|
|
10
|
+
sqlite3 "$DB_PATH" "SELECT adapter, MAX(fetched_at) FROM inbox_items GROUP BY adapter;" 2>/dev/null | \
|
|
11
|
+
while IFS='|' read -r adapter ts; do
|
|
12
|
+
echo " $adapter: $ts"
|
|
13
|
+
done
|
|
14
|
+
fi
|
|
15
|
+
else
|
|
16
|
+
echo "ftm-inbox is not running."
|
|
17
|
+
fi
|
package/ftm-inbox/bin/stop.sh
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
if [ -f /tmp/ftm-inbox.pid ]; then
|
|
3
|
-
kill "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null
|
|
4
|
-
rm /tmp/ftm-inbox.pid
|
|
5
|
-
echo "ftm-inbox stopped."
|
|
6
|
-
else
|
|
7
|
-
echo "No running ftm-inbox found."
|
|
8
|
-
fi
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
if [ -f /tmp/ftm-inbox.pid ]; then
|
|
3
|
+
kill "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null
|
|
4
|
+
rm /tmp/ftm-inbox.pid
|
|
5
|
+
echo "ftm-inbox stopped."
|
|
6
|
+
else
|
|
7
|
+
echo "No running ftm-inbox found."
|
|
8
|
+
fi
|