We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Addons: `running` is invoked twice.
##### Steps to reproduce the problem:

1. Write an addon that implements `running()`
2. Running is invoked twice on startup.

Addons: `running` is invoked twice.
##### Steps to reproduce the problem:

1. Write an addon that implements `running()`
2. Running is invoked twice on startup.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mitmproxy/addons/script.py`
Content:
```
1 import asyncio
2 import os
3 import importlib.util
4 import importlib.machinery
5 import sys
6 import types
7 import typing
8 import traceback
9 
10 from mitmproxy import addonmanager, hooks
11 from mitmproxy import exceptions
12 from mitmproxy import flow
13 from mitmproxy import command
14 from mitmproxy import eventsequence
15 from mitmproxy import ctx
16 import mitmproxy.types as mtypes
17 
18 
19 def load_script(path: str) -> typing.Optional[types.ModuleType]:
20     fullname = "__mitmproxy_script__.{}".format(
21         os.path.splitext(os.path.basename(path))[0]
22     )
23     # the fullname is not unique among scripts, so if there already is an existing script with said
24     # fullname, remove it.
25     sys.modules.pop(fullname, None)
26     oldpath = sys.path
27     sys.path.insert(0, os.path.dirname(path))
28     m = None
29     try:
30         loader = importlib.machinery.SourceFileLoader(fullname, path)
31         spec = importlib.util.spec_from_loader(fullname, loader=loader)
32         assert spec
33         m = importlib.util.module_from_spec(spec)
34         loader.exec_module(m)
35         if not getattr(m, "name", None):
36             m.name = path  # type: ignore
37     except Exception as e:
38         script_error_handler(path, e, msg=str(e))
39     finally:
40         sys.path[:] = oldpath
41         return m
42 
43 
44 def script_error_handler(path, exc, msg="", tb=False):
45     """
46         Handles all the user's script errors with
47         an optional traceback
48     """
49     exception = type(exc).__name__
50     if msg:
51         exception = msg
52     lineno = ""
53     if hasattr(exc, "lineno"):
54         lineno = str(exc.lineno)
55     log_msg = f"in script {path}:{lineno} {exception}"
56     if tb:
57         etype, value, tback = sys.exc_info()
58         tback = addonmanager.cut_traceback(tback, "invoke_addon")
59         log_msg = log_msg + "\n" + "".join(traceback.format_exception(etype, value, tback))
60     ctx.log.error(log_msg)
61 
62 
63 ReloadInterval = 1
64 
65 
66 class Script:
67     """
68         An addon that manages a single script.
69     """
70 
71     def __init__(self, path: str, reload: bool) -> None:
72         self.name = "scriptmanager:" + path
73         self.path = path
74         self.fullpath = os.path.expanduser(
75             path.strip("'\" ")
76         )
77         self.ns = None
78 
79         if not os.path.isfile(self.fullpath):
80             raise exceptions.OptionsError('No such script')
81 
82         self.reloadtask = None
83         if reload:
84             self.reloadtask = asyncio.ensure_future(self.watcher())
85         else:
86             self.loadscript()
87 
88     def done(self):
89         if self.reloadtask:
90             self.reloadtask.cancel()
91 
92     @property
93     def addons(self):
94         return [self.ns] if self.ns else []
95 
96     def loadscript(self):
97         ctx.log.info("Loading script %s" % self.path)
98         if self.ns:
99             ctx.master.addons.remove(self.ns)
100         self.ns = None
101         with addonmanager.safecall():
102             ns = load_script(self.fullpath)
103             ctx.master.addons.register(ns)
104             self.ns = ns
105         if self.ns:
106             # We're already running, so we have to explicitly register and
107             # configure the addon
108             ctx.master.addons.invoke_addon(self.ns, hooks.RunningHook())
109             try:
110                 ctx.master.addons.invoke_addon(
111                     self.ns,
112                     hooks.ConfigureHook(ctx.options.keys())
113                 )
114             except exceptions.OptionsError as e:
115                 script_error_handler(self.fullpath, e, msg=str(e))
116 
117     async def watcher(self):
118         last_mtime = 0
119         while True:
120             try:
121                 mtime = os.stat(self.fullpath).st_mtime
122             except FileNotFoundError:
123                 ctx.log.info("Removing script %s" % self.path)
124                 scripts = list(ctx.options.scripts)
125                 scripts.remove(self.path)
126                 ctx.options.update(scripts=scripts)
127                 return
128             if mtime > last_mtime:
129                 self.loadscript()
130                 last_mtime = mtime
131             await asyncio.sleep(ReloadInterval)
132 
133 
134 class ScriptLoader:
135     """
136         An addon that manages loading scripts from options.
137     """
138     def __init__(self):
139         self.is_running = False
140         self.addons = []
141 
142     def load(self, loader):
143         loader.add_option(
144             "scripts", typing.Sequence[str], [],
145             "Execute a script."
146         )
147 
148     def running(self):
149         self.is_running = True
150 
151     @command.command("script.run")
152     def script_run(self, flows: typing.Sequence[flow.Flow], path: mtypes.Path) -> None:
153         """
154             Run a script on the specified flows. The script is configured with
155             the current options and all lifecycle events for each flow are
156             simulated. Note that the load event is not invoked.
157         """
158         if not os.path.isfile(path):
159             ctx.log.error('No such script: %s' % path)
160             return
161         mod = load_script(path)
162         if mod:
163             with addonmanager.safecall():
164                 ctx.master.addons.invoke_addon(mod, hooks.RunningHook())
165                 ctx.master.addons.invoke_addon(
166                     mod,
167                     hooks.ConfigureHook(ctx.options.keys()),
168                 )
169                 for f in flows:
170                     for evt in eventsequence.iterate(f):
171                         ctx.master.addons.invoke_addon(mod, evt)
172 
173     def configure(self, updated):
174         if "scripts" in updated:
175             for s in ctx.options.scripts:
176                 if ctx.options.scripts.count(s) > 1:
177                     raise exceptions.OptionsError("Duplicate script")
178 
179             for a in self.addons[:]:
180                 if a.path not in ctx.options.scripts:
181                     ctx.log.info("Un-loading script: %s" % a.path)
182                     ctx.master.addons.remove(a)
183                     self.addons.remove(a)
184 
185             # The machinations below are to ensure that:
186             #   - Scripts remain in the same order
187             #   - Scripts are not initialized un-necessarily. If only a
188             #   script's order in the script list has changed, it is just
189             #   moved.
190 
191             current = {}
192             for a in self.addons:
193                 current[a.path] = a
194 
195             ordered = []
196             newscripts = []
197             for s in ctx.options.scripts:
198                 if s in current:
199                     ordered.append(current[s])
200                 else:
201                     sc = Script(s, True)
202                     ordered.append(sc)
203                     newscripts.append(sc)
204 
205             self.addons = ordered
206 
207             for s in newscripts:
208                 ctx.master.addons.register(s)
209                 if self.is_running:
210                     # If we're already running, we configure and tell the addon
211                     # we're up and running.
212                     ctx.master.addons.invoke_addon(s, hooks.RunningHook())
213
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mitmproxy/addons/script.py b/mitmproxy/addons/script.py
--- a/mitmproxy/addons/script.py
+++ b/mitmproxy/addons/script.py
@@ -75,6 +75,7 @@
             path.strip("'\" ")
         )
         self.ns = None
+        self.is_running = False
 
         if not os.path.isfile(self.fullpath):
             raise exceptions.OptionsError('No such script')
@@ -85,6 +86,9 @@
         else:
             self.loadscript()
 
+    def running(self):
+        self.is_running = True
+
     def done(self):
         if self.reloadtask:
             self.reloadtask.cancel()
@@ -105,7 +109,8 @@
         if self.ns:
             # We're already running, so we have to explicitly register and
             # configure the addon
-            ctx.master.addons.invoke_addon(self.ns, hooks.RunningHook())
+            if self.is_running:
+                ctx.master.addons.invoke_addon(self.ns, hooks.RunningHook())
             try:
                 ctx.master.addons.invoke_addon(
                     self.ns,

{"golden_diff": "diff --git a/mitmproxy/addons/script.py b/mitmproxy/addons/script.py\n--- a/mitmproxy/addons/script.py\n+++ b/mitmproxy/addons/script.py\n@@ -75,6 +75,7 @@\n             path.strip(\"'\\\" \")\n         )\n         self.ns = None\n+        self.is_running = False\n \n         if not os.path.isfile(self.fullpath):\n             raise exceptions.OptionsError('No such script')\n@@ -85,6 +86,9 @@\n         else:\n             self.loadscript()\n \n+    def running(self):\n+        self.is_running = True\n+\n     def done(self):\n         if self.reloadtask:\n             self.reloadtask.cancel()\n@@ -105,7 +109,8 @@\n         if self.ns:\n             # We're already running, so we have to explicitly register and\n             # configure the addon\n-            ctx.master.addons.invoke_addon(self.ns, hooks.RunningHook())\n+            if self.is_running:\n+                ctx.master.addons.invoke_addon(self.ns, hooks.RunningHook())\n             try:\n                 ctx.master.addons.invoke_addon(\n                     self.ns,\n", "issue": "Addons: `running` is invoked twice.\n##### Steps to reproduce the problem:\r\n\r\n1. Write an addon that implements `running()`\r\n2. Running is invoked twice on startup.\r\n\nAddons: `running` is invoked twice.\n##### Steps to reproduce the problem:\r\n\r\n1. Write an addon that implements `running()`\r\n2. Running is invoked twice on startup.\r\n\n", "before_files": [{"content": "import asyncio\nimport os\nimport importlib.util\nimport importlib.machinery\nimport sys\nimport types\nimport typing\nimport traceback\n\nfrom mitmproxy import addonmanager, hooks\nfrom mitmproxy import exceptions\nfrom mitmproxy import flow\nfrom mitmproxy import command\nfrom mitmproxy import eventsequence\nfrom mitmproxy import ctx\nimport mitmproxy.types as mtypes\n\n\ndef load_script(path: str) -> typing.Optional[types.ModuleType]:\n    fullname = \"__mitmproxy_script__.{}\".format(\n        os.path.splitext(os.path.basename(path))[0]\n    )\n    # the fullname is not unique among scripts, so if there already is an existing script with said\n    # fullname, remove it.\n    sys.modules.pop(fullname, None)\n    oldpath = sys.path\n    sys.path.insert(0, os.path.dirname(path))\n    m = None\n    try:\n        loader = importlib.machinery.SourceFileLoader(fullname, path)\n        spec = importlib.util.spec_from_loader(fullname, loader=loader)\n        assert spec\n        m = importlib.util.module_from_spec(spec)\n        loader.exec_module(m)\n        if not getattr(m, \"name\", None):\n            m.name = path  # type: ignore\n    except Exception as e:\n        script_error_handler(path, e, msg=str(e))\n    finally:\n        sys.path[:] = oldpath\n        return m\n\n\ndef script_error_handler(path, exc, msg=\"\", tb=False):\n    \"\"\"\n        Handles all the user's script errors with\n        an optional traceback\n    \"\"\"\n    exception = type(exc).__name__\n    if msg:\n        exception = msg\n    lineno = \"\"\n    if hasattr(exc, \"lineno\"):\n        lineno = str(exc.lineno)\n    log_msg = f\"in script {path}:{lineno} {exception}\"\n    if tb:\n        etype, value, tback = sys.exc_info()\n        tback = addonmanager.cut_traceback(tback, \"invoke_addon\")\n        log_msg = log_msg + \"\\n\" + \"\".join(traceback.format_exception(etype, value, tback))\n    ctx.log.error(log_msg)\n\n\nReloadInterval = 1\n\n\nclass Script:\n    \"\"\"\n        An addon that manages a single script.\n    \"\"\"\n\n    def __init__(self, path: str, reload: bool) -> None:\n        self.name = \"scriptmanager:\" + path\n        self.path = path\n        self.fullpath = os.path.expanduser(\n            path.strip(\"'\\\" \")\n        )\n        self.ns = None\n\n        if not os.path.isfile(self.fullpath):\n            raise exceptions.OptionsError('No such script')\n\n        self.reloadtask = None\n        if reload:\n            self.reloadtask = asyncio.ensure_future(self.watcher())\n        else:\n            self.loadscript()\n\n    def done(self):\n        if self.reloadtask:\n            self.reloadtask.cancel()\n\n    @property\n    def addons(self):\n        return [self.ns] if self.ns else []\n\n    def loadscript(self):\n        ctx.log.info(\"Loading script %s\" % self.path)\n        if self.ns:\n            ctx.master.addons.remove(self.ns)\n        self.ns = None\n        with addonmanager.safecall():\n            ns = load_script(self.fullpath)\n            ctx.master.addons.register(ns)\n            self.ns = ns\n        if self.ns:\n            # We're already running, so we have to explicitly register and\n            # configure the addon\n            ctx.master.addons.invoke_addon(self.ns, hooks.RunningHook())\n            try:\n                ctx.master.addons.invoke_addon(\n                    self.ns,\n                    hooks.ConfigureHook(ctx.options.keys())\n                )\n            except exceptions.OptionsError as e:\n                script_error_handler(self.fullpath, e, msg=str(e))\n\n    async def watcher(self):\n        last_mtime = 0\n        while True:\n            try:\n                mtime = os.stat(self.fullpath).st_mtime\n            except FileNotFoundError:\n                ctx.log.info(\"Removing script %s\" % self.path)\n                scripts = list(ctx.options.scripts)\n                scripts.remove(self.path)\n                ctx.options.update(scripts=scripts)\n                return\n            if mtime > last_mtime:\n                self.loadscript()\n                last_mtime = mtime\n            await asyncio.sleep(ReloadInterval)\n\n\nclass ScriptLoader:\n    \"\"\"\n        An addon that manages loading scripts from options.\n    \"\"\"\n    def __init__(self):\n        self.is_running = False\n        self.addons = []\n\n    def load(self, loader):\n        loader.add_option(\n            \"scripts\", typing.Sequence[str], [],\n            \"Execute a script.\"\n        )\n\n    def running(self):\n        self.is_running = True\n\n    @command.command(\"script.run\")\n    def script_run(self, flows: typing.Sequence[flow.Flow], path: mtypes.Path) -> None:\n        \"\"\"\n            Run a script on the specified flows. The script is configured with\n            the current options and all lifecycle events for each flow are\n            simulated. Note that the load event is not invoked.\n        \"\"\"\n        if not os.path.isfile(path):\n            ctx.log.error('No such script: %s' % path)\n            return\n        mod = load_script(path)\n        if mod:\n            with addonmanager.safecall():\n                ctx.master.addons.invoke_addon(mod, hooks.RunningHook())\n                ctx.master.addons.invoke_addon(\n                    mod,\n                    hooks.ConfigureHook(ctx.options.keys()),\n                )\n                for f in flows:\n                    for evt in eventsequence.iterate(f):\n                        ctx.master.addons.invoke_addon(mod, evt)\n\n    def configure(self, updated):\n        if \"scripts\" in updated:\n            for s in ctx.options.scripts:\n                if ctx.options.scripts.count(s) > 1:\n                    raise exceptions.OptionsError(\"Duplicate script\")\n\n            for a in self.addons[:]:\n                if a.path not in ctx.options.scripts:\n                    ctx.log.info(\"Un-loading script: %s\" % a.path)\n                    ctx.master.addons.remove(a)\n                    self.addons.remove(a)\n\n            # The machinations below are to ensure that:\n            #   - Scripts remain in the same order\n            #   - Scripts are not initialized un-necessarily. If only a\n            #   script's order in the script list has changed, it is just\n            #   moved.\n\n            current = {}\n            for a in self.addons:\n                current[a.path] = a\n\n            ordered = []\n            newscripts = []\n            for s in ctx.options.scripts:\n                if s in current:\n                    ordered.append(current[s])\n                else:\n                    sc = Script(s, True)\n                    ordered.append(sc)\n                    newscripts.append(sc)\n\n            self.addons = ordered\n\n            for s in newscripts:\n                ctx.master.addons.register(s)\n                if self.is_running:\n                    # If we're already running, we configure and tell the addon\n                    # we're up and running.\n                    ctx.master.addons.invoke_addon(s, hooks.RunningHook())\n", "path": "mitmproxy/addons/script.py"}], "after_files": [{"content": "import asyncio\nimport os\nimport importlib.util\nimport importlib.machinery\nimport sys\nimport types\nimport typing\nimport traceback\n\nfrom mitmproxy import addonmanager, hooks\nfrom mitmproxy import exceptions\nfrom mitmproxy import flow\nfrom mitmproxy import command\nfrom mitmproxy import eventsequence\nfrom mitmproxy import ctx\nimport mitmproxy.types as mtypes\n\n\ndef load_script(path: str) -> typing.Optional[types.ModuleType]:\n    fullname = \"__mitmproxy_script__.{}\".format(\n        os.path.splitext(os.path.basename(path))[0]\n    )\n    # the fullname is not unique among scripts, so if there already is an existing script with said\n    # fullname, remove it.\n    sys.modules.pop(fullname, None)\n    oldpath = sys.path\n    sys.path.insert(0, os.path.dirname(path))\n    m = None\n    try:\n        loader = importlib.machinery.SourceFileLoader(fullname, path)\n        spec = importlib.util.spec_from_loader(fullname, loader=loader)\n        assert spec\n        m = importlib.util.module_from_spec(spec)\n        loader.exec_module(m)\n        if not getattr(m, \"name\", None):\n            m.name = path  # type: ignore\n    except Exception as e:\n        script_error_handler(path, e, msg=str(e))\n    finally:\n        sys.path[:] = oldpath\n        return m\n\n\ndef script_error_handler(path, exc, msg=\"\", tb=False):\n    \"\"\"\n        Handles all the user's script errors with\n        an optional traceback\n    \"\"\"\n    exception = type(exc).__name__\n    if msg:\n        exception = msg\n    lineno = \"\"\n    if hasattr(exc, \"lineno\"):\n        lineno = str(exc.lineno)\n    log_msg = f\"in script {path}:{lineno} {exception}\"\n    if tb:\n        etype, value, tback = sys.exc_info()\n        tback = addonmanager.cut_traceback(tback, \"invoke_addon\")\n        log_msg = log_msg + \"\\n\" + \"\".join(traceback.format_exception(etype, value, tback))\n    ctx.log.error(log_msg)\n\n\nReloadInterval = 1\n\n\nclass Script:\n    \"\"\"\n        An addon that manages a single script.\n    \"\"\"\n\n    def __init__(self, path: str, reload: bool) -> None:\n        self.name = \"scriptmanager:\" + path\n        self.path = path\n        self.fullpath = os.path.expanduser(\n            path.strip(\"'\\\" \")\n        )\n        self.ns = None\n        self.is_running = False\n\n        if not os.path.isfile(self.fullpath):\n            raise exceptions.OptionsError('No such script')\n\n        self.reloadtask = None\n        if reload:\n            self.reloadtask = asyncio.ensure_future(self.watcher())\n        else:\n            self.loadscript()\n\n    def running(self):\n        self.is_running = True\n\n    def done(self):\n        if self.reloadtask:\n            self.reloadtask.cancel()\n\n    @property\n    def addons(self):\n        return [self.ns] if self.ns else []\n\n    def loadscript(self):\n        ctx.log.info(\"Loading script %s\" % self.path)\n        if self.ns:\n            ctx.master.addons.remove(self.ns)\n        self.ns = None\n        with addonmanager.safecall():\n            ns = load_script(self.fullpath)\n            ctx.master.addons.register(ns)\n            self.ns = ns\n        if self.ns:\n            # We're already running, so we have to explicitly register and\n            # configure the addon\n            if self.is_running:\n                ctx.master.addons.invoke_addon(self.ns, hooks.RunningHook())\n            try:\n                ctx.master.addons.invoke_addon(\n                    self.ns,\n                    hooks.ConfigureHook(ctx.options.keys())\n                )\n            except exceptions.OptionsError as e:\n                script_error_handler(self.fullpath, e, msg=str(e))\n\n    async def watcher(self):\n        last_mtime = 0\n        while True:\n            try:\n                mtime = os.stat(self.fullpath).st_mtime\n            except FileNotFoundError:\n                ctx.log.info(\"Removing script %s\" % self.path)\n                scripts = list(ctx.options.scripts)\n                scripts.remove(self.path)\n                ctx.options.update(scripts=scripts)\n                return\n            if mtime > last_mtime:\n                self.loadscript()\n                last_mtime = mtime\n            await asyncio.sleep(ReloadInterval)\n\n\nclass ScriptLoader:\n    \"\"\"\n        An addon that manages loading scripts from options.\n    \"\"\"\n    def __init__(self):\n        self.is_running = False\n        self.addons = []\n\n    def load(self, loader):\n        loader.add_option(\n            \"scripts\", typing.Sequence[str], [],\n            \"Execute a script.\"\n        )\n\n    def running(self):\n        self.is_running = True\n\n    @command.command(\"script.run\")\n    def script_run(self, flows: typing.Sequence[flow.Flow], path: mtypes.Path) -> None:\n        \"\"\"\n            Run a script on the specified flows. The script is configured with\n            the current options and all lifecycle events for each flow are\n            simulated. Note that the load event is not invoked.\n        \"\"\"\n        if not os.path.isfile(path):\n            ctx.log.error('No such script: %s' % path)\n            return\n        mod = load_script(path)\n        if mod:\n            with addonmanager.safecall():\n                ctx.master.addons.invoke_addon(mod, hooks.RunningHook())\n                ctx.master.addons.invoke_addon(\n                    mod,\n                    hooks.ConfigureHook(ctx.options.keys()),\n                )\n                for f in flows:\n                    for evt in eventsequence.iterate(f):\n                        ctx.master.addons.invoke_addon(mod, evt)\n\n    def configure(self, updated):\n        if \"scripts\" in updated:\n            for s in ctx.options.scripts:\n                if ctx.options.scripts.count(s) > 1:\n                    raise exceptions.OptionsError(\"Duplicate script\")\n\n            for a in self.addons[:]:\n                if a.path not in ctx.options.scripts:\n                    ctx.log.info(\"Un-loading script: %s\" % a.path)\n                    ctx.master.addons.remove(a)\n                    self.addons.remove(a)\n\n            # The machinations below are to ensure that:\n            #   - Scripts remain in the same order\n            #   - Scripts are not initialized un-necessarily. If only a\n            #   script's order in the script list has changed, it is just\n            #   moved.\n\n            current = {}\n            for a in self.addons:\n                current[a.path] = a\n\n            ordered = []\n            newscripts = []\n            for s in ctx.options.scripts:\n                if s in current:\n                    ordered.append(current[s])\n                else:\n                    sc = Script(s, True)\n                    ordered.append(sc)\n                    newscripts.append(sc)\n\n            self.addons = ordered\n\n            for s in newscripts:\n                ctx.master.addons.register(s)\n                if self.is_running:\n                    # If we're already running, we configure and tell the addon\n                    # we're up and running.\n                    ctx.master.addons.invoke_addon(s, hooks.RunningHook())\n", "path": "mitmproxy/addons/script.py"}]}